usbip-rs/lib/src/hid.rs
Davíð Steinn Geirsson 4c368c02b5 feat: concurrent ISO pipelining via nusb update and &self handlers
Update nusb to c1380673 which allows multiple IsoEndpoint instances per
address, enabling concurrent URB submission from separate threads.

Change UsbInterfaceHandler trait methods from &mut self to &self and
replace Arc<Mutex<Box<dyn Handler>>> with Arc<dyn Handler>. This
removes the serialization bottleneck where the handler mutex was held
for the entire USB transfer duration, causing ISO audio to play at
~67% speed.

Handlers needing interior mutability (HID, CDC) now use Mutex on
individual fields. Passthrough handlers already used Arc<Mutex<>>
internally and need no changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 15:10:28 +00:00

199 lines
6.8 KiB
Rust

//! Implement HID device
use super::*;
// reference:
// HID 1.11: https://www.usb.org/sites/default/files/documents/hid1_11.pdf
// HID Usage Tables 1.12: https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
enum UsbHidKeyboardHandlerState {
Idle,
KeyDown,
}
/// A handler of a HID keyboard
#[derive(Debug)]
pub struct UsbHidKeyboardHandler {
pub report_descriptor: Vec<u8>,
pub pending_key_events: Mutex<VecDeque<UsbHidKeyboardReport>>,
state: Mutex<UsbHidKeyboardHandlerState>,
}
/// A report of a HID keyboard
///
/// For definition of key codes, see [HID Usage Tables](https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf)
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct UsbHidKeyboardReport {
/// Key modifier
pub modifier: u8,
/// Key code
pub keys: [u8; 6],
}
impl UsbHidKeyboardReport {
pub fn from_ascii(ascii: u8) -> Result<UsbHidKeyboardReport> {
let (modifier, key) = match ascii {
b'a'..=b'z' => (0, ascii - b'a' + 4),
b'A'..=b'Z' => (0x02, ascii - b'A' + 4), // Left Shift modifier
b'1'..=b'9' => (0, ascii - b'1' + 30),
b'0' => (0, 39),
b'\r' | b'\n' => (0, 40),
b' ' => (0, 44), // Space
_ => {
return Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
format!("Unrecognized ascii {}", ascii),
))
}
};
Ok(UsbHidKeyboardReport {
modifier,
keys: [key, 0, 0, 0, 0, 0],
})
}
}
impl UsbHidKeyboardHandler {
pub fn new_keyboard() -> Self {
Self {
pending_key_events: Mutex::new(VecDeque::new()),
state: Mutex::new(UsbHidKeyboardHandlerState::Idle),
report_descriptor: vec![
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
// Modifier
0x05, 0x07, // Key Codes
0x19, 0xE0, // Usage Min
0x29, 0xE7, // Usage Max
0x15, 0x00, // Logic Min
0x25, 0x01, // Logic Max
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input
// Reserved
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x01, // Input
// key codes
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logic Min
0x25, 0x65, // Logic Max
0x05, 0x07, // Usage Page
0x19, 0x00, // Usage Min
0x29, 0x65, // Usage Max
0x81, 0x00, // Input (Data, Array)
0xC0, // End collection
],
}
}
}
impl UsbInterfaceHandler for UsbHidKeyboardHandler {
fn handle_urb(
&self,
_interface: &UsbInterface,
request: UrbRequest,
) -> Result<UrbResponse> {
let ep = request.ep;
let setup = request.setup;
if ep.is_ep0() {
// control transfers
match (setup.request_type, setup.request) {
(0b10000001, 0x06) => {
// GET_DESCRIPTOR
// high byte: type
match FromPrimitive::from_u16(setup.value >> 8) {
Some(HidDescriptorType::Report) => {
return Ok(UrbResponse { data: self.report_descriptor.clone(), ..Default::default() });
}
_ => {
return Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
format!("Unsupported HID descriptor type: {setup:x?}"),
))
}
}
}
(0b00100001, 0x0A) => {
// SET_IDLE
return Ok(UrbResponse::default());
}
_ => {
return Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
format!("Unsupported HID request: {setup:x?}"),
))
}
}
} else {
// interrupt transfer
if let Direction::In = ep.direction() {
// interrupt in
let mut state = self.state.lock().unwrap();
match *state {
UsbHidKeyboardHandlerState::Idle => {
if let Some(report) = self.pending_key_events.lock().unwrap().pop_front() {
let mut resp = vec![report.modifier, 0];
resp.extend_from_slice(&report.keys);
info!("HID key down");
*state = UsbHidKeyboardHandlerState::KeyDown;
return Ok(UrbResponse { data: resp, ..Default::default() });
}
}
UsbHidKeyboardHandlerState::KeyDown => {
let resp = vec![0; 6];
info!("HID key up");
*state = UsbHidKeyboardHandlerState::Idle;
return Ok(UrbResponse { data: resp, ..Default::default() });
}
}
}
}
Ok(UrbResponse::default())
}
fn get_class_specific_descriptor(&self) -> Vec<u8> {
vec![
0x09, // bLength
HidDescriptorType::Hid as u8, // bDescriptorType: HID
0x11,
0x01, // bcdHID 1.11
0x00, // bCountryCode
0x01, // bNumDescriptors
HidDescriptorType::Report as u8, // bDescriptorType[0] HID
self.report_descriptor.len() as u8,
(self.report_descriptor.len() >> 8) as u8, // wDescriptorLength[0]
]
}
fn as_any(&self) -> &dyn Any {
self
}
}
/// A list of defined HID descriptor type
#[derive(Copy, Clone, Debug, FromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum HidDescriptorType {
Hid = 0x21,
Report = 0x22,
Physical = 0x23,
}
#[cfg(test)]
mod tests {
use crate::util::tests::*;
use super::*;
#[test]
fn desc_verify() {
setup_test_logger();
let handler = UsbHidKeyboardHandler::new_keyboard();
verify_descriptor(&handler.get_class_specific_descriptor());
}
}