server udc fix

This commit is contained in:
Brad Stein 2025-06-26 00:13:32 -05:00
parent 158b9ef3ab
commit a8d843fd2d

View File

@ -65,43 +65,40 @@ impl UsbGadget {
Err(anyhow::anyhow!("⚠️ UDC {ctrl} did not reappear within {limit_ms}ms")) Err(anyhow::anyhow!("⚠️ UDC {ctrl} did not reappear within {limit_ms}ms"))
} }
/// Scan platform devices when /sys/class/udc is empty
fn probe_platform_udc() -> Result<Option<String>> {
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 */ /* public API */
/// Hardreset the gadget → identical to a physical cable replug /// Hardreset the gadget → identical to a physical cable replug
pub fn cycle(&self) -> Result<()> { pub fn cycle(&self) -> Result<()> {
/* 0ensure we *know* the controller even after a previous crash */ /* 0ensure we *know* the controller even after a previous crash */
let ctrl = match Self::find_controller() { let mut ctrl = Self::find_controller()
Ok(c) => c, .or_else(|_| Self::probe_platform_udc()?
Err(_) => { .ok_or_else(|| anyhow::anyhow!("no UDC present")))?;
// try to recover by reading the last name from the gadgets 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 rebind {last}");
Self::rebind_driver(&last)?;
last
}
};
/* 1 detach gadget */ /* 1 detach gadget */
info!("🔌 detaching gadget from {ctrl}"); info!("🔌 detaching gadget from {ctrl}");
Self::write_attr(self.udc_file, "")?; Self::write_attr(self.udc_file, "")?;
Self::wait_state(&ctrl, "not attached", 3_000)?; Self::wait_state(&ctrl, "not attached", 3_000)?;
/* 2 unbind / bind platform driver (dwc2/dwc3) */ /* 2 reset driver */
Self::rebind_driver(&ctrl)?; Self::rebind_driver(&ctrl)?;
Self::wait_udc_present(&ctrl, 3_000)
.context("controller did not reappear after bind")?;
/* 3 reattach gadget */ /* 3 wait UDC node to reappear */
Self::wait_udc_present(&ctrl, 3_000)?;
/* 4 reattach + pullup */
info!("🔌 reattaching gadget to {ctrl}"); info!("🔌 reattaching gadget to {ctrl}");
Self::write_attr(self.udc_file, &ctrl)?; Self::write_attr(self.udc_file, &ctrl)?;
/* 4 assert pullup */
let sc = format!("/sys/class/udc/{ctrl}/soft_connect"); let sc = format!("/sys/class/udc/{ctrl}/soft_connect");
// toggle 0 → 1 to force the controller to assert pullups Self::write_attr(&sc, "0")?;
Self::write_attr(&sc, "0")?; // guarantee clean edge
thread::sleep(Duration::from_millis(50)); thread::sleep(Duration::from_millis(50));
Self::write_attr(&sc, "1")?; Self::write_attr(&sc, "1")?;
@ -128,20 +125,28 @@ impl UsbGadget {
/// helper: unbind + 300ms reset + bind /// helper: unbind + 300ms reset + bind
fn rebind_driver(ctrl: &str) -> Result<()> { fn rebind_driver(ctrl: &str) -> Result<()> {
let cand = ["dwc2", "dwc3"]; // cover RasPi, RK, AM62, … let cand = ["dwc2", "dwc3"];
for drv in cand { for drv in cand {
let root = format!("/sys/bus/platform/drivers/{drv}"); let root = format!("/sys/bus/platform/drivers/{drv}");
if !Path::new(&root).exists() { if !Path::new(&root).exists() { continue }
continue;
}
info!("🔧 unbinding UDC driver ({drv})"); info!("🔧 unbinding UDC driver ({drv})");
Self::write_attr(format!("{root}/unbind"), ctrl)?; let _ = Self::write_attr(format!("{root}/unbind"), ctrl); // ignore EINVAL
thread::sleep(Duration::from_millis(500)); thread::sleep(Duration::from_millis(300));
info!("🔧 binding UDC driver ({drv})"); info!("🔧 binding UDC driver ({drv})");
Self::write_attr(format!("{root}/bind"), ctrl)?; for attempt in 1..=10 {
thread::sleep(Duration::from_millis(500)); match Self::write_attr(format!("{root}/bind"), ctrl) {
return Ok(()); 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")) Err(anyhow::anyhow!("no dwc2/dwc3 driver nodes found"))
} }