server udc fix
This commit is contained in:
parent
158b9ef3ab
commit
a8d843fd2d
@ -65,43 +65,40 @@ impl UsbGadget {
|
|||||||
Err(anyhow::anyhow!("⚠️ UDC {ctrl} did not re‑appear within {limit_ms} ms"))
|
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<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 ––––*/
|
||||||
|
|
||||||
/// Hard‑reset the gadget → identical to a physical cable re‑plug
|
/// Hard‑reset the gadget → identical to a physical cable re‑plug
|
||||||
pub fn cycle(&self) -> Result<()> {
|
pub fn cycle(&self) -> Result<()> {
|
||||||
/* 0 – ensure we *know* the controller even after a previous crash */
|
/* 0 – ensure 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 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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* 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 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}");
|
info!("🔌 re‑attaching gadget to {ctrl}");
|
||||||
Self::write_attr(self.udc_file, &ctrl)?;
|
Self::write_attr(self.udc_file, &ctrl)?;
|
||||||
|
|
||||||
/* 4 – assert pull‑up */
|
|
||||||
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 pull‑ups
|
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 + 300 ms reset + bind
|
/// helper: unbind + 300 ms 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"))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user