diff --git a/server/src/usb_gadget.rs b/server/src/usb_gadget.rs index 228b55c..b45f23a 100644 --- a/server/src/usb_gadget.rs +++ b/server/src/usb_gadget.rs @@ -65,43 +65,40 @@ impl UsbGadget { Err(anyhow::anyhow!("⚠️ UDC {ctrl} did not re‑appear within {limit_ms} ms")) } + /// Scan platform devices when /sys/class/udc is empty + fn probe_platform_udc() -> Result> { + for entry in fs::read_dir("/sys/bus/platform/devices")? { + let p = entry?.file_name().into_string().unwrap(); + if p.ends_with(".usb") { return Ok(Some(p)); } + } + Ok(None) + } + /*–––– public API ––––*/ /// Hard‑reset the gadget → identical to a physical cable re‑plug pub fn cycle(&self) -> Result<()> { /* 0 – ensure we *know* the controller even after a previous crash */ - let ctrl = match Self::find_controller() { - Ok(c) => c, - Err(_) => { - // try to recover by reading the last name from the gadget’s UDC file - let last = fs::read_to_string(self.udc_file)?.trim().to_owned(); - if last.is_empty() { - return Err(anyhow::anyhow!("no UDC present and UDC file is empty")); - } - warn!("⚠️ UDC missing, attempting to re‑bind {last}"); - Self::rebind_driver(&last)?; - last - } - }; + let mut ctrl = Self::find_controller() + .or_else(|_| Self::probe_platform_udc()? + .ok_or_else(|| anyhow::anyhow!("no UDC present")))?; /* 1 – detach gadget */ info!("🔌 detaching gadget from {ctrl}"); Self::write_attr(self.udc_file, "")?; Self::wait_state(&ctrl, "not attached", 3_000)?; - /* 2 – unbind / bind platform driver (dwc2/dwc3) */ + /* 2 – reset driver */ Self::rebind_driver(&ctrl)?; - Self::wait_udc_present(&ctrl, 3_000) - .context("controller did not re‑appear after bind")?; - /* 3 – re‑attach gadget */ + /* 3 – wait UDC node to re‑appear */ + Self::wait_udc_present(&ctrl, 3_000)?; + + /* 4 – re‑attach + pull‑up */ info!("🔌 re‑attaching gadget to {ctrl}"); Self::write_attr(self.udc_file, &ctrl)?; - - /* 4 – assert pull‑up */ let sc = format!("/sys/class/udc/{ctrl}/soft_connect"); - // toggle 0 → 1 to force the controller to assert pull‑ups - Self::write_attr(&sc, "0")?; // guarantee clean edge + Self::write_attr(&sc, "0")?; thread::sleep(Duration::from_millis(50)); Self::write_attr(&sc, "1")?; @@ -128,20 +125,28 @@ impl UsbGadget { /// helper: unbind + 300 ms reset + bind fn rebind_driver(ctrl: &str) -> Result<()> { - let cand = ["dwc2", "dwc3"]; // cover RasPi, RK, AM62, … + let cand = ["dwc2", "dwc3"]; for drv in cand { let root = format!("/sys/bus/platform/drivers/{drv}"); - if !Path::new(&root).exists() { - continue; - } + if !Path::new(&root).exists() { continue } + info!("🔧 unbinding UDC driver ({drv})"); - Self::write_attr(format!("{root}/unbind"), ctrl)?; - thread::sleep(Duration::from_millis(500)); + let _ = Self::write_attr(format!("{root}/unbind"), ctrl); // ignore EINVAL + thread::sleep(Duration::from_millis(300)); info!("🔧 binding UDC driver ({drv})"); - Self::write_attr(format!("{root}/bind"), ctrl)?; - thread::sleep(Duration::from_millis(500)); - return Ok(()); + for attempt in 1..=10 { + match Self::write_attr(format!("{root}/bind"), ctrl) { + Ok(_) => return Ok(()), + Err(e) if attempt < 10 => { + trace!("bind busy (#{attempt}) – retrying…"); + thread::sleep(Duration::from_millis(100)); + continue; + } + Err(e) => return Err(e) + .context(format!("bind failed after {attempt} tries")), + } + } } Err(anyhow::anyhow!("no dwc2/dwc3 driver nodes found")) }