From 9dc2343b603efdd3f08a8a34b47fd14b382d22c0 Mon Sep 17 00:00:00 2001 From: ssmithx Date: Mon, 29 Jun 2026 16:21:27 +0000 Subject: [PATCH] fix(openwrt): enable radio0 and run wifi up before scanning On a freshly-flashed OpenWrt router, radio0 is disabled by default so iw dev returns empty. Detect the PHY via /sys/class/ieee80211/, enable radio0, run `wifi up`, then poll up to 8s for netifd to create the virtual interface before handing it to iwinfo scan. Co-Authored-By: Claude Sonnet 4.6 --- core/archipelago/src/api/rpc/middleware.rs | 1 + core/openwrt/src/wifi_scan.rs | 37 ++++++++++++++-------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/core/archipelago/src/api/rpc/middleware.rs b/core/archipelago/src/api/rpc/middleware.rs index 714068e3..fc15c75f 100644 --- a/core/archipelago/src/api/rpc/middleware.rs +++ b/core/archipelago/src/api/rpc/middleware.rs @@ -73,6 +73,7 @@ pub(super) fn sanitize_error_message(msg: &str) -> String { "apk update failed", "No wireless interface", "No wireless radio", + "WiFi radio enabled but", "Missing required field", ]; for prefix in &user_facing_prefixes { diff --git a/core/openwrt/src/wifi_scan.rs b/core/openwrt/src/wifi_scan.rs index 8cb569c0..882b9715 100644 --- a/core/openwrt/src/wifi_scan.rs +++ b/core/openwrt/src/wifi_scan.rs @@ -19,22 +19,33 @@ pub fn scan_networks(router: &Router) -> Result> { } fn find_wireless_iface(router: &Router) -> Result { - // `iw dev` works on any mac80211 system and handles new-style interface names - // (phy0-ap0, phy0-sta0, etc.) used by OpenWrt 25.x mt76 drivers. + // Fast path: interface already up (radio was previously enabled) let (out, _) = router.run("iw dev 2>/dev/null | awk '/Interface/{print $2}' | head -1")?; - let iface = out.trim().to_string(); - if !iface.is_empty() { - return Ok(iface); + if !out.trim().is_empty() { + return Ok(out.trim().to_string()); } - // Fallback: any entry in /sys/class/net with a wireless subdir - let (out2, _) = router.run( - "for i in /sys/class/net/*/wireless; do [ -d \"$i\" ] && basename $(dirname $i) && break; done 2>/dev/null", - )?; - let iface2 = out2.trim().to_string(); - if !iface2.is_empty() { - return Ok(iface2); + + // No interface yet — common on a freshly-flashed OpenWrt where radio0 is + // disabled by default. Verify the radio PHY exists at all. + let (phy_out, _) = router.run("ls /sys/class/ieee80211/ 2>/dev/null | head -1")?; + if phy_out.trim().is_empty() { + anyhow::bail!("No wireless radio found on this router"); } - anyhow::bail!("No wireless interface found on this router") + + // Enable radio0 and bring wifi up so netifd creates the virtual interface. + tracing::info!("[{}] Radio present but no interface — enabling radio0 and running wifi up", router.host); + router.run_ok("uci set wireless.radio0.disabled=0 && uci commit wireless && wifi up 2>&1")?; + + // Wait up to 8s for netifd to create the interface (it's asynchronous) + for _ in 0..8 { + std::thread::sleep(std::time::Duration::from_secs(1)); + let (out2, _) = router.run("iw dev 2>/dev/null | awk '/Interface/{print $2}' | head -1")?; + if !out2.trim().is_empty() { + return Ok(out2.trim().to_string()); + } + } + + anyhow::bail!("WiFi radio enabled but no interface appeared — check OpenWrt wireless config") } fn parse_iwinfo_scan(output: &str) -> Result> {