diff --git a/server/src/main.rs b/server/src/main.rs index d96d248..446fbb4 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -98,6 +98,15 @@ fn owners_of(path: &str) -> String { if pids.is_empty() { "-".into() } else { pids.join(",") } } +async fn wait_configured(ctrl: &str, limit_ms: u64) -> anyhow::Result<()> { + for _ in 0..=limit_ms/50 { + let s = UsbGadget::state(ctrl)?; + if s.trim() == "configured" { return Ok(()) } + tokio::time::sleep(Duration::from_millis(50)).await; + } + Err(anyhow::anyhow!("host never configured")) +} + /*─────────────────── tonic service ─────────────────────*/ struct Handler { kb: Arc>, @@ -112,6 +121,8 @@ impl Handler { gadget.cycle()?; let ctrl = UsbGadget::find_controller()?; + wait_configured(&ctrl, 10_000).await + .context("waiting for host to configure")?; let state = UsbGadget::wait_state_any(&ctrl, 5_000)?; match state.as_str() { "configured" => info!("✅ host enumerated (configured)"), @@ -155,30 +166,34 @@ impl Relay for Handler { let (tx, rx) = tokio::sync::mpsc::channel::>(32); let kb = self.kb.clone(); + let gadget = self.gadget.clone(); + let ctrl = UsbGadget::find_controller().unwrap_or_default(); tokio::spawn(async move { let mut s = req.into_inner(); while let Some(pkt) = s.next().await.transpose()? { - // kb.lock().await.write_all(&pkt.data).await?; - const SPINS: usize = 20; - for _ in 0..SPINS { - match kb.lock().await.write(&pkt.data).await { - Ok(n) if n == pkt.data.len() => break, // success - Ok(_) => continue, // short write - Err(ref e) if matches!(e.raw_os_error(), - Some(libc::EPIPE)|Some(libc::ENODEV)) => break, // host gone - Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { - std::hint::spin_loop(); - continue; - } - Err(e) => { tracing::error!("hid write: {e}"); break; } + /* try to write once */ + let mut guard = kb.lock().await; + if let Err(e) = guard.write_all(&pkt.data).await { + /* host vanished ? */ + if matches!(e.raw_os_error(), + Some(libc::ESHUTDOWN)|Some(libc::ENODEV)|Some(libc::EPIPE)) { + warn!("host disappeared – recycling gadget"); + gadget.cycle().map_err(|e| Status::internal(e.to_string()))?; + wait_configured(&ctrl, 10_000).await + .map_err(|e| Status::internal(e.to_string()))?; + /* reopen endpoint & swap into mutex */ + *guard = open_with_retry("/dev/hidg0").await + .map_err(|e| Status::internal(e.to_string()))?; + } else { + return Err(Status::internal(e.to_string())); } } - tx.send(Ok(pkt)).await;//.ok(); // best-effort echo + drop(guard); /* release lock before await */ + tx.send(Ok(pkt)).await.ok(); /* best‑effort echo */ } Ok::<(), Status>(()) }); - Ok(Response::new(ReceiverStream::new(rx))) } @@ -189,30 +204,34 @@ impl Relay for Handler { let (tx, rx) = tokio::sync::mpsc::channel::>(4096); let ms = self.ms.clone(); + let gadget = self.gadget.clone(); + let ctrl = UsbGadget::find_controller().unwrap_or_default(); tokio::spawn(async move { let mut s = req.into_inner(); - let mut boot_mode = true; while let Some(pkt) = s.next().await.transpose()? { - loop { - match ms.lock().await.write(&pkt.data).await { - Ok(n) if n == pkt.data.len() => break, // success - Ok(_) => continue, // short write - Err(ref e) if matches!(e.raw_os_error(), - Some(libc::EPIPE)|Some(libc::ENODEV)) => break, // host gone - Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { - std::hint::spin_loop(); - continue; - } - Err(e) => { tracing::error!("hid write: {e}"); break; } + /* try to write once */ + let mut guard = ms.lock().await; + if let Err(e) = guard.write_all(&pkt.data).await { + /* host vanished ? */ + if matches!(e.raw_os_error(), + Some(libc::ESHUTDOWN)|Some(libc::ENODEV)|Some(libc::EPIPE)) { + warn!("host disappeared – recycling gadget"); + gadget.cycle().map_err(|e| Status::internal(e.to_string()))?; + wait_configured(&ctrl, 10_000).await + .map_err(|e| Status::internal(e.to_string()))?; + /* reopen endpoint & swap into mutex */ + *guard = open_with_retry("/dev/hidg1").await + .map_err(|e| Status::internal(e.to_string()))?; + } else { + return Err(Status::internal(e.to_string())); } - } // <-- closes `loop {` - - let _ = tx.send(Ok(pkt)).await; + } + drop(guard); /* release lock before await */ + tx.send(Ok(pkt)).await.ok(); /* best‑effort echo */ } Ok::<(), Status>(()) }); - Ok(Response::new(ReceiverStream::new(rx))) } diff --git a/server/src/usb_gadget.rs b/server/src/usb_gadget.rs index ca8e5be..8d48b69 100644 --- a/server/src/usb_gadget.rs +++ b/server/src/usb_gadget.rs @@ -17,6 +17,11 @@ impl UsbGadget { } } + pub fn state(ctrl: &str) -> anyhow::Result { + let p = format!("/sys/class/udc/{ctrl}/state"); + Ok(std::fs::read_to_string(p)?.trim().to_owned()) + } + /*–––– helpers ––––*/ /// Find the first controller in /sys/class/udc (e.g. `1000480000.usb`)