HID reset modified

This commit is contained in:
Brad Stein 2025-06-26 20:38:55 -05:00
parent 74cf5a46ee
commit e8243e5c1c
2 changed files with 55 additions and 31 deletions

View File

@ -98,6 +98,15 @@ fn owners_of(path: &str) -> String {
if pids.is_empty() { "-".into() } else { pids.join(",") } 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 ─────────────────────*/ /*─────────────────── tonic service ─────────────────────*/
struct Handler { struct Handler {
kb: Arc<Mutex<tokio::fs::File>>, kb: Arc<Mutex<tokio::fs::File>>,
@ -112,6 +121,8 @@ impl Handler {
gadget.cycle()?; gadget.cycle()?;
let ctrl = UsbGadget::find_controller()?; 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)?; let state = UsbGadget::wait_state_any(&ctrl, 5_000)?;
match state.as_str() { match state.as_str() {
"configured" => info!("✅ host enumerated (configured)"), "configured" => info!("✅ host enumerated (configured)"),
@ -155,30 +166,34 @@ impl Relay for Handler {
let (tx, rx) = let (tx, rx) =
tokio::sync::mpsc::channel::<Result<KeyboardReport, Status>>(32); tokio::sync::mpsc::channel::<Result<KeyboardReport, Status>>(32);
let kb = self.kb.clone(); let kb = self.kb.clone();
let gadget = self.gadget.clone();
let ctrl = UsbGadget::find_controller().unwrap_or_default();
tokio::spawn(async move { tokio::spawn(async move {
let mut s = req.into_inner(); let mut s = req.into_inner();
while let Some(pkt) = s.next().await.transpose()? { while let Some(pkt) = s.next().await.transpose()? {
// kb.lock().await.write_all(&pkt.data).await?; /* try to write once */
const SPINS: usize = 20; let mut guard = kb.lock().await;
for _ in 0..SPINS { if let Err(e) = guard.write_all(&pkt.data).await {
match kb.lock().await.write(&pkt.data).await { /* host vanished ? */
Ok(n) if n == pkt.data.len() => break, // success if matches!(e.raw_os_error(),
Ok(_) => continue, // short write Some(libc::ESHUTDOWN)|Some(libc::ENODEV)|Some(libc::EPIPE)) {
Err(ref e) if matches!(e.raw_os_error(), warn!("host disappeared recycling gadget");
Some(libc::EPIPE)|Some(libc::ENODEV)) => break, // host gone gadget.cycle().map_err(|e| Status::internal(e.to_string()))?;
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { wait_configured(&ctrl, 10_000).await
std::hint::spin_loop(); .map_err(|e| Status::internal(e.to_string()))?;
continue; /* reopen endpoint & swap into mutex */
} *guard = open_with_retry("/dev/hidg0").await
Err(e) => { tracing::error!("hid write: {e}"); break; } .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(); /* besteffort echo */
} }
Ok::<(), Status>(()) Ok::<(), Status>(())
}); });
Ok(Response::new(ReceiverStream::new(rx))) Ok(Response::new(ReceiverStream::new(rx)))
} }
@ -189,30 +204,34 @@ impl Relay for Handler {
let (tx, rx) = let (tx, rx) =
tokio::sync::mpsc::channel::<Result<MouseReport, Status>>(4096); tokio::sync::mpsc::channel::<Result<MouseReport, Status>>(4096);
let ms = self.ms.clone(); let ms = self.ms.clone();
let gadget = self.gadget.clone();
let ctrl = UsbGadget::find_controller().unwrap_or_default();
tokio::spawn(async move { tokio::spawn(async move {
let mut s = req.into_inner(); let mut s = req.into_inner();
let mut boot_mode = true;
while let Some(pkt) = s.next().await.transpose()? { while let Some(pkt) = s.next().await.transpose()? {
loop { /* try to write once */
match ms.lock().await.write(&pkt.data).await { let mut guard = ms.lock().await;
Ok(n) if n == pkt.data.len() => break, // success if let Err(e) = guard.write_all(&pkt.data).await {
Ok(_) => continue, // short write /* host vanished ? */
Err(ref e) if matches!(e.raw_os_error(), if matches!(e.raw_os_error(),
Some(libc::EPIPE)|Some(libc::ENODEV)) => break, // host gone Some(libc::ESHUTDOWN)|Some(libc::ENODEV)|Some(libc::EPIPE)) {
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => { warn!("host disappeared recycling gadget");
std::hint::spin_loop(); gadget.cycle().map_err(|e| Status::internal(e.to_string()))?;
continue; wait_configured(&ctrl, 10_000).await
} .map_err(|e| Status::internal(e.to_string()))?;
Err(e) => { tracing::error!("hid write: {e}"); break; } /* 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 {` }
drop(guard); /* release lock before await */
let _ = tx.send(Ok(pkt)).await; tx.send(Ok(pkt)).await.ok(); /* besteffort echo */
} }
Ok::<(), Status>(()) Ok::<(), Status>(())
}); });
Ok(Response::new(ReceiverStream::new(rx))) Ok(Response::new(ReceiverStream::new(rx)))
} }

View File

@ -17,6 +17,11 @@ impl UsbGadget {
} }
} }
pub fn state(ctrl: &str) -> anyhow::Result<String> {
let p = format!("/sys/class/udc/{ctrl}/state");
Ok(std::fs::read_to_string(p)?.trim().to_owned())
}
/* helpers */ /* helpers */
/// Find the first controller in /sys/class/udc (e.g. `1000480000.usb`) /// Find the first controller in /sys/class/udc (e.g. `1000480000.usb`)