2025-06-24 23:48:06 -05:00
|
|
|
|
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,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-25 07:46:50 -05:00
|
|
|
|
impl UsbGadget {
|
|
|
|
|
|
pub fn new(name: &'static str) -> Self {
|
2025-06-24 23:48:06 -05:00
|
|
|
|
// /sys/kernel/config/usb_gadget/<name>/UDC
|
|
|
|
|
|
Self { udc_file: Box::leak(
|
2025-06-25 07:46:50 -05:00
|
|
|
|
format!("/sys/kernel/config/usb_gadget/{name}/UDC").into_boxed_str())
|
2025-06-24 23:48:06 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Force the host to re‑enumerate our HID gadget.
|
|
|
|
|
|
pub fn cycle(&self) -> Result<()> {
|
2025-06-25 20:36:42 -05:00
|
|
|
|
info!("🔌 UDC‑cycle: detaching gadget");
|
2025-06-25 07:46:50 -05:00
|
|
|
|
OpenOptions::new().write(true).open(self.udc_file)?.write_all(b"")?;
|
|
|
|
|
|
thread::sleep(Duration::from_millis(200));
|
2025-06-24 23:48:06 -05:00
|
|
|
|
|
2025-06-25 07:46:50 -05:00
|
|
|
|
let udc_name = std::fs::read_dir("/sys/class/udc")?
|
|
|
|
|
|
.next()
|
|
|
|
|
|
.transpose()?
|
|
|
|
|
|
.context("no UDC present")?
|
2025-06-24 23:48:06 -05:00
|
|
|
|
.file_name();
|
2025-06-25 20:36:42 -05:00
|
|
|
|
info!("🔌 UDC‑cycle: re‑attaching to {}", udc_name.to_string_lossy());
|
2025-06-24 23:48:06 -05:00
|
|
|
|
OpenOptions::new().write(true).open(self.udc_file)?
|
2025-06-25 07:46:50 -05:00
|
|
|
|
.write_all(udc_name.to_str().unwrap().as_bytes())?;
|
2025-06-25 20:36:42 -05:00
|
|
|
|
info!("🔌 USB‑gadget cycled");
|
2025-06-24 23:48:06 -05:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|