From 5cf25de966979de2f88e24975abc25386f820741 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 25 Jun 2025 21:02:29 -0500 Subject: [PATCH] server logging update --- server/src/usb_gadget.rs | 83 ++++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 12 deletions(-) diff --git a/server/src/usb_gadget.rs b/server/src/usb_gadget.rs index f8ef203..cbe32ae 100644 --- a/server/src/usb_gadget.rs +++ b/server/src/usb_gadget.rs @@ -1,6 +1,32 @@ -use std::{fs::OpenOptions, io::Write, thread, time::Duration}; +// server/src/usb_gadget.rs + +use std::{fs::{self, OpenOptions}, io::Write, thread, time::Duration}; use anyhow::{Result, Context}; -use tracing::{info, warn}; +use tracing::{debug, error, info, warn}; + +fn wait_for_detach(udc_path: &str) { + for _ in 0..20 { + // as soon as the file is **empty**, the detach succeeded + if fs::read_to_string(udc_path).map(|s| s.trim().is_empty()).unwrap_or(false) { + debug!("🔌 UDC is now *detached*"); + return; + } + thread::sleep(Duration::from_millis(50)); + } + warn!("⏳ UDC did not detach within the expected time-out"); +} + +/// Like `wait_for_detach`, but the opposite condition. +fn wait_for_attach(udc_path: &str) { + for _ in 0..20 { + if fs::read_to_string(udc_path).map(|s| !s.trim().is_empty()).unwrap_or(false) { + debug!("🔌 UDC is *attached* again"); + return; + } + thread::sleep(Duration::from_millis(50)); + } + warn!("⏳ UDC did not attach within the expected time-out"); +} #[derive(Clone)] pub struct UsbGadget { @@ -10,15 +36,24 @@ pub struct UsbGadget { impl UsbGadget { pub fn new(name: &'static str) -> Self { // /sys/kernel/config/usb_gadget//UDC - Self { udc_file: Box::leak( - format!("/sys/kernel/config/usb_gadget/{name}/UDC").into_boxed_str()) + Self { + udc_file: Box::leak( + format!("/sys/kernel/config/usb_gadget/{name}/UDC").into_boxed_str(), + ), } } - /// Force the host to re‑enumerate our HID gadget. + /// Force the host to re-enumerate our HID gadget. + /// Retries on EBUSY so that a transient “resource busy” never kills the whole process. pub fn cycle(&self) -> Result<()> { - info!("🔌 UDC‑cycle: detaching gadget"); - OpenOptions::new().write(true).open(self.udc_file)?.write_all(b"")?; + info!("🔌 UDC-cycle: detaching gadget"); + OpenOptions::new() + .write(true) + .open(self.udc_file)? + .write_all(b"")?; // empty string → detach + wait_for_detach(self.udc_file); + + // Let the controller settle thread::sleep(Duration::from_millis(200)); let udc_name = std::fs::read_dir("/sys/class/udc")? @@ -26,10 +61,34 @@ impl UsbGadget { .transpose()? .context("no UDC present")? .file_name(); - info!("🔌 UDC‑cycle: re‑attaching to {}", udc_name.to_string_lossy()); - OpenOptions::new().write(true).open(self.udc_file)? - .write_all(udc_name.to_str().unwrap().as_bytes())?; - info!("🔌 USB‑gadget cycled"); - Ok(()) + + // Retry loop for the notoriously fragile re-attach + for attempt in 0..5 { + info!("🔌 UDC-cycle: re-attaching to {} (try #{attempt})", udc_name.to_string_lossy()); + match OpenOptions::new() + .write(true) + .open(self.udc_file)? + .write_all(udc_name.to_str().unwrap().as_bytes()) + { + Ok(()) => { + wait_for_attach(self.udc_file); + info!("🟢 USB-gadget cycled successfully"); + return Ok(()); + } + Err(e) if e.raw_os_error() == Some(libc::EBUSY) => { + warn!("🚧 UDC busy on re-attach – retrying in 100 ms"); + thread::sleep(Duration::from_millis(100)); + continue; + } + Err(e) => { + error!("💥 unexpected error while re-attaching UDC: {e:?}"); + return Err(e).context("re-attaching gadget to UDC"); + } + } + } + + Err(anyhow::anyhow!( + "giving up after 5 attempts – UDC remained busy" + )) } }