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(",") }
}
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<Mutex<tokio::fs::File>>,
@ -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::<Result<KeyboardReport, Status>>(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(); /* besteffort echo */
}
Ok::<(), Status>(())
});
Ok(Response::new(ReceiverStream::new(rx)))
}
@ -189,30 +204,34 @@ impl Relay for Handler {
let (tx, rx) =
tokio::sync::mpsc::channel::<Result<MouseReport, Status>>(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;
/* 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()));
}
Err(e) => { tracing::error!("hid write: {e}"); break; }
}
} // <-- closes `loop {`
let _ = tx.send(Ok(pkt)).await;
drop(guard); /* release lock before await */
tx.send(Ok(pkt)).await.ok(); /* besteffort echo */
}
Ok::<(), Status>(())
});
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 */
/// Find the first controller in /sys/class/udc (e.g. `1000480000.usb`)