use std::{fs::OpenOptions, io::Write, thread, time::Duration}; use anyhow::{Result, Context}; use tracing::{info, warn}; #[derive(Clone)] pub struct UsbGadget { udc_file: &'static str, } impl UsbGadgetManager { pub fn new(gadget_name: &'static str) -> Self { // /sys/kernel/config/usb_gadget//UDC Self { udc_file: Box::leak( format!("/sys/kernel/config/usb_gadget/{gadget_name}/UDC").into_boxed_str()) } } /// Force the host to re‑enumerate our HID gadget. pub fn cycle(&self) -> Result<()> { // 1. detach info!("UDC‑cycle: detaching gadget"); OpenOptions::new().write(true).open(self.udc_file)? .write_all(b"")?; // 2. wait ≥ 100 ms so host sees a disconnect std::thread::sleep(Duration::from_millis(200)); // 3. re‑attach to **first** UDC (dwc2) let udc = std::fs::read_dir("/sys/class/udc")? .next().context("no UDC present")? .file_name(); let name = udc.to_string_lossy(); info!("UDC‑cycle: re‑attaching to {name}"); OpenOptions::new().write(true).open(self.udc_file)? .write_all(name.as_bytes())?; Ok(()) } }