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>
199 lines
6.8 KiB
Rust
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());
|
|
}
|
|
}
|