From e22712d9207ebcfdcf2e88b3dc8484278189fbbe Mon Sep 17 00:00:00 2001 From: Antoine Reversat Date: Thu, 7 May 2026 12:20:07 -0400 Subject: [PATCH 1/3] Replace hid_descs with unknown_descs --- drivers/usb/xhcid/src/driver_interface.rs | 24 ++-- drivers/usb/xhcid/src/xhci/scheme.rs | 137 ++++++++++++---------- 2 files changed, 85 insertions(+), 76 deletions(-) diff --git a/drivers/usb/xhcid/src/driver_interface.rs b/drivers/usb/xhcid/src/driver_interface.rs index 727f8d7e99..ba45d01ad5 100644 --- a/drivers/usb/xhcid/src/driver_interface.rs +++ b/drivers/usb/xhcid/src/driver_interface.rs @@ -59,9 +59,10 @@ pub struct ConfDesc { pub attributes: u8, pub max_power: u8, pub interface_descs: SmallVec<[IfDesc; 1]>, + pub unknown_descs: SmallVec<[UnknownDesc; 1]>, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] pub struct EndpDesc { pub kind: u8, pub address: u8, @@ -215,7 +216,7 @@ impl EndpDesc { .unwrap_or(false) } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct IfDesc { pub kind: u8, pub number: u8, @@ -225,31 +226,26 @@ pub struct IfDesc { pub protocol: u8, pub interface_str: Option, pub endpoints: SmallVec<[EndpDesc; 4]>, - pub hid_descs: SmallVec<[HidDesc; 1]>, + pub unknown_descs: SmallVec<[UnknownDesc; 1]>, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] pub struct SuperSpeedCmp { pub kind: u8, pub max_burst: u8, pub attributes: u8, pub bytes_per_interval: u16, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] pub struct SuperSpeedPlusIsochCmp { pub kind: u8, pub bytes_per_interval: u32, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct HidDesc { +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct UnknownDesc { + pub len: u8, pub kind: u8, - pub hid_spec_release: u16, - pub country: u8, - pub desc_count: u8, - pub desc_ty: u8, - pub desc_len: u16, - pub optional_desc_ty: u8, - pub optional_desc_len: u16, + pub all_bytes: Vec, } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct PortReq { diff --git a/drivers/usb/xhcid/src/xhci/scheme.rs b/drivers/usb/xhcid/src/xhci/scheme.rs index ca27b3fec7..fe80d2062d 100644 --- a/drivers/usb/xhcid/src/xhci/scheme.rs +++ b/drivers/usb/xhcid/src/xhci/scheme.rs @@ -477,20 +477,6 @@ impl From for EndpDesc { } } -impl From for HidDesc { - fn from(d: usb::HidDescriptor) -> Self { - Self { - kind: d.kind, - hid_spec_release: d.hid_spec_release, - country: d.country_code, - desc_count: d.num_descriptors, - desc_ty: d.report_desc_ty, - desc_len: d.report_desc_len, - optional_desc_ty: d.optional_desc_ty, - optional_desc_len: d.optional_desc_len, - } - } -} impl From for SuperSpeedCmp { fn from(d: usb::SuperSpeedCompanionDescriptor) -> Self { @@ -519,9 +505,9 @@ pub enum AnyDescriptor { Config(usb::ConfigDescriptor), Interface(usb::InterfaceDescriptor), Endpoint(usb::EndpointDescriptor), - Hid(usb::HidDescriptor), SuperSpeedCompanion(usb::SuperSpeedCompanionDescriptor), SuperSpeedPlusCompanion(usb::SuperSpeedPlusIsochCmpDescriptor), + Unknown(Vec), } impl AnyDescriptor { @@ -534,6 +520,7 @@ impl AnyDescriptor { let kind = bytes[1]; if bytes.len() < len.into() { + error!("Parsed length is bigger than buffer len"); return None; } @@ -543,13 +530,9 @@ impl AnyDescriptor { 2 => Self::Config(*plain::from_bytes(bytes).ok()?), 4 => Self::Interface(*plain::from_bytes(bytes).ok()?), 5 => Self::Endpoint(*plain::from_bytes(bytes).ok()?), - 33 => Self::Hid(*plain::from_bytes(bytes).ok()?), 48 => Self::SuperSpeedCompanion(*plain::from_bytes(bytes).ok()?), 49 => Self::SuperSpeedPlusCompanion(*plain::from_bytes(bytes).ok()?), - _ => { - //panic!("Descriptor unknown {}: bytes {:#0x?}", kind, bytes); - return None; - } + _ => Self::Unknown(bytes[..len as usize].iter().cloned().collect()), }, len.into(), )) @@ -563,7 +546,7 @@ impl Xhci { slot: u8, desc: usb::InterfaceDescriptor, endps: impl IntoIterator, - hid_descs: impl IntoIterator, + unknown_descs: impl IntoIterator, lang_id: u16, ) -> Result { Ok(IfDesc { @@ -582,7 +565,7 @@ impl Xhci { protocol: desc.protocol, sub_class: desc.sub_class, endpoints: endps.into_iter().collect(), - hid_descs: hid_descs.into_iter().collect(), + unknown_descs: unknown_descs.into_iter().collect(), }) } /// Pushes a command TRB to the command ring, rings the doorbell, and then awaits its Command @@ -1539,56 +1522,85 @@ impl Xhci { } let mut interface_descs = SmallVec::new(); + let mut unknown_descs = SmallVec::new(); + let mut iface_unknown_descs = SmallVec::<[UnknownDesc; 1]>::new(); let mut iter = descriptors.into_iter().peekable(); + + let mut cur_iface = None; + let mut cur_endpoint = None; + let mut endpoints = SmallVec::<[EndpDesc; 4]>::new(); while let Some(item) = iter.next() { - if let AnyDescriptor::Interface(idesc) = item { - let mut endpoints = SmallVec::<[EndpDesc; 4]>::new(); - let mut hid_descs = SmallVec::<[HidDesc; 1]>::new(); - - while endpoints.len() < idesc.endpoints as usize { - let next = match iter.next() { - Some(AnyDescriptor::Endpoint(n)) => n, - Some(AnyDescriptor::Hid(h)) if idesc.class == 3 => { - hid_descs.push(h.into()); - continue; - } - Some(unexpected) => { - log::warn!("expected endpoint, got {:X?}", unexpected); - break; - } - None => break, - }; - let mut endp = EndpDesc::from(next); - - loop { - match iter.peek() { - Some(AnyDescriptor::SuperSpeedCompanion(n)) => { - endp.ssc = Some(SuperSpeedCmp::from(n.clone())); - iter.next().unwrap(); + debug!("Processing descriptor {item:?}"); + + + match item { + AnyDescriptor::Interface(idesc) => { + if let Some(cur_idesc) = cur_iface { + if let Some(endp) = cur_endpoint{ + if !endpoints.contains(&endp) { + endpoints.push(endp); } - Some(AnyDescriptor::SuperSpeedPlusCompanion(n)) => { - endp.sspc = Some(SuperSpeedPlusIsochCmp::from(n.clone())); - iter.next().unwrap(); - } - _ => break, } + interface_descs.push( + self.new_if_desc(port_id, slot, cur_idesc, endpoints, iface_unknown_descs, lang_id) + .await?, + ); } - - endpoints.push(endp); + cur_iface = Some(idesc); + endpoints = SmallVec::new(); + iface_unknown_descs = SmallVec::new(); + }, + AnyDescriptor::Endpoint(endpdesc) => { + if let Some(desc) = cur_endpoint { + endpoints.push(desc); + } + cur_endpoint = Some(EndpDesc::from(endpdesc)); + }, + AnyDescriptor::SuperSpeedCompanion(ssc) => { + if let Some(endp) = cur_endpoint.as_mut() { + endp.ssc = Some(SuperSpeedCmp::from(ssc.clone())); + } else { + error!("Found a superspeedcompanion descriptor before encountering any endpoint descriptor."); + } + }, + AnyDescriptor::SuperSpeedPlusCompanion(sscp) => { + if let Some(endp) = cur_endpoint.as_mut() { + endp.sspc = Some(SuperSpeedPlusIsochCmp::from(sscp.clone())); + } else { + error!("Found a superspeedpluscompanion descriptor before encountering any endpoint descriptor."); + } + }, + AnyDescriptor::Unknown(bytes) => { + // If we're processing an interface, add the desc to it, otherwise add it to the configuration + if cur_iface.is_none() { + unknown_descs.push(UnknownDesc {len: bytes[0], kind: bytes[1], all_bytes: bytes}); + } else { + iface_unknown_descs.push(UnknownDesc {len: bytes[0], kind: bytes[1], all_bytes: bytes}); + } + }, + _ => { + error!("Found unexpected descriptor {item:?} while parsing config descriptors, skipping"); + continue; } - - interface_descs.push( - self.new_if_desc(port_id, slot, idesc, endpoints, hid_descs, lang_id) - .await?, - ); - } else { - log::warn!("expected interface, got {:?}", item); - // TODO - //break; } } + // Push the last endpoint + if let Some(endp) = cur_endpoint{ + if !endpoints.contains(&endp) { + endpoints.push(endp); + } + } + + // Push the last interface + if let Some(idesc) = cur_iface { + let new_if_desc = self.new_if_desc(port_id, slot, idesc, endpoints, iface_unknown_descs, lang_id).await?; + if !interface_descs.contains(&new_if_desc) { + interface_descs.push(new_if_desc); + } + } + config_descs.push(ConfDesc { kind: desc.kind, configuration: if desc.configuration_str > 0 { @@ -1603,6 +1615,7 @@ impl Xhci { attributes: desc.attributes, max_power: desc.max_power, interface_descs, + unknown_descs }); } From f6bb61ba189ae233645e4aa3901aa303d134a694 Mon Sep 17 00:00:00 2001 From: Antoine Reversat Date: Thu, 7 May 2026 14:05:13 -0400 Subject: [PATCH 2/3] Move report descriptor parsing into usbhid --- drivers/input/usbhidd/Cargo.toml | 2 + drivers/input/usbhidd/src/descs.rs | 88 ++++++++++++++++++++++++++++++ drivers/input/usbhidd/src/main.rs | 12 ++-- 3 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 drivers/input/usbhidd/src/descs.rs diff --git a/drivers/input/usbhidd/Cargo.toml b/drivers/input/usbhidd/Cargo.toml index 2c0cda1915..05bef7d12d 100644 --- a/drivers/input/usbhidd/Cargo.toml +++ b/drivers/input/usbhidd/Cargo.toml @@ -13,8 +13,10 @@ anyhow.workspace = true bitflags.workspace = true log.workspace = true orbclient.workspace = true +plain.workspace = true redox_syscall.workspace = true rehid = { git = "https://gitlab.redox-os.org/redox-os/rehid.git" } +smallvec.workspace = true xhcid = { path = "../../usb/xhcid" } common = { path = "../../common" } diff --git a/drivers/input/usbhidd/src/descs.rs b/drivers/input/usbhidd/src/descs.rs new file mode 100644 index 0000000000..dddd00b39d --- /dev/null +++ b/drivers/input/usbhidd/src/descs.rs @@ -0,0 +1,88 @@ +use anyhow::{Result, anyhow}; +use plain::Plain; +use smallvec::SmallVec; +use std::convert::TryInto; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Default)] +pub enum HidClassType { + #[default] + HID = 0x21, + Report, + Physical +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug, Default)] +pub struct HidClassDescriptor { + pub desc_type: HidClassType, + pub desc_len: u16, +} + +unsafe impl Plain for HidClassDescriptor {} + +#[derive(Clone, Debug, Default)] +pub struct HidDescriptor { + pub length: u8, + pub kind: u8, + pub hid_spec_release: u16, + pub country_code: u8, + pub num_descriptors: u8, + pub descriptors: SmallVec<[HidClassDescriptor; 1]>, +} + +impl HidDescriptor { + // Size of the fixed part of HidDescriptor + const HID_DESC_FIXED_SIZE: u8 = 6; + const HID_CLASS_DESC_SIZE: u8 = 3; + + pub fn from_bytes(bytes: &[u8]) -> Result { + let length = bytes[0]; + let kind = bytes[1]; + + // A valid descriptor has at least one class descriptor + if (length < Self::HID_DESC_FIXED_SIZE + Self::HID_CLASS_DESC_SIZE) { + return Err(anyhow!("Invalid length")); + } + + if (kind != HidClassType::HID as u8) { + return Err(anyhow!("This is not a hid descriptor")); + } + + let num_descriptors = bytes[5]; + + if (length != Self::HID_DESC_FIXED_SIZE + num_descriptors * Self::HID_CLASS_DESC_SIZE) { + return Err(anyhow!("Len doesn't match the given number of descriptors ({num_descriptors})")); + } + + let mut descriptors = SmallVec::<[HidClassDescriptor; 1]>::with_capacity(num_descriptors as usize); + + for i in 0..num_descriptors { + match HidClassDescriptor::from_bytes(&bytes[(Self::HID_DESC_FIXED_SIZE + i*Self::HID_CLASS_DESC_SIZE) as usize..(Self::HID_DESC_FIXED_SIZE + (i+1)*Self::HID_CLASS_DESC_SIZE) as usize]) { + Ok(desc) => descriptors.push(*desc), + Err(e) => return Err(anyhow!("{e:?}")), + } + } + + Ok(Self { + length, + kind, + hid_spec_release: u16::from_ne_bytes(bytes[2..4].try_into()?), + country_code: bytes[4], + num_descriptors, + descriptors + }) + } + + + pub fn get_report_desc(&self) -> Result { + for desc in self.descriptors.iter() { + match desc.desc_type { + HidClassType::Report => return Ok(*desc), + _ => () + } + } + return Err(anyhow!("No Report descriptor found")); + + } +} \ No newline at end of file diff --git a/drivers/input/usbhidd/src/main.rs b/drivers/input/usbhidd/src/main.rs index 15c5b77820..b3cb97e98e 100644 --- a/drivers/input/usbhidd/src/main.rs +++ b/drivers/input/usbhidd/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use std::{env, thread, time}; use inputd::ProducerHandle; @@ -13,7 +13,10 @@ use xhcid_interface::{ XhciClientHandle, }; +use crate::descs::HidDescriptor; + mod reqs; +mod descs; fn send_key_event(display: &mut ProducerHandle, usage_page: u16, usage: u16, pressed: bool) { let scancode = match usage_page { @@ -217,9 +220,9 @@ fn main() -> Result<()> { None } }); - let hid_desc = if_desc.hid_descs.iter().find_map(|hid_desc| { + let hid_desc = if_desc.unknown_descs.iter().find_map(|unknown_desc| { //TODO: should we do any filtering? - Some(hid_desc) + HidDescriptor::from_bytes(&unknown_desc.all_bytes).ok() })?; Some((if_desc.clone(), endp_desc_opt, hid_desc)) } else { @@ -246,8 +249,7 @@ fn main() -> Result<()> { // This sets all reports to a duration of 4ms reqs::set_idle(&handle, 1, 0, interface_num as u16).context("Failed to set idle")?; - let report_desc_len = hid_desc.desc_len; - assert_eq!(hid_desc.desc_ty, REPORT_DESC_TY); + let report_desc_len = hid_desc.get_report_desc()?.desc_len; let mut report_desc_bytes = vec![0u8; report_desc_len as usize]; handle From 3f6c0c706fed36dcabefa77293c032ec3b39b6f7 Mon Sep 17 00:00:00 2001 From: Antoine Reversat Date: Thu, 7 May 2026 15:22:33 -0400 Subject: [PATCH 3/3] Run cargo fmt --- drivers/input/usbhidd/src/descs.rs | 32 ++++++++------ drivers/input/usbhidd/src/main.rs | 2 +- drivers/usb/xhcid/src/xhci/scheme.rs | 64 +++++++++++++++++++--------- 3 files changed, 62 insertions(+), 36 deletions(-) diff --git a/drivers/input/usbhidd/src/descs.rs b/drivers/input/usbhidd/src/descs.rs index dddd00b39d..23742fcd14 100644 --- a/drivers/input/usbhidd/src/descs.rs +++ b/drivers/input/usbhidd/src/descs.rs @@ -1,4 +1,4 @@ -use anyhow::{Result, anyhow}; +use anyhow::{anyhow, Result}; use plain::Plain; use smallvec::SmallVec; use std::convert::TryInto; @@ -9,7 +9,7 @@ pub enum HidClassType { #[default] HID = 0x21, Report, - Physical + Physical, } #[repr(C, packed)] @@ -48,41 +48,45 @@ impl HidDescriptor { if (kind != HidClassType::HID as u8) { return Err(anyhow!("This is not a hid descriptor")); } - + let num_descriptors = bytes[5]; if (length != Self::HID_DESC_FIXED_SIZE + num_descriptors * Self::HID_CLASS_DESC_SIZE) { - return Err(anyhow!("Len doesn't match the given number of descriptors ({num_descriptors})")); + return Err(anyhow!( + "Len doesn't match the given number of descriptors ({num_descriptors})" + )); } - - let mut descriptors = SmallVec::<[HidClassDescriptor; 1]>::with_capacity(num_descriptors as usize); - + + let mut descriptors = + SmallVec::<[HidClassDescriptor; 1]>::with_capacity(num_descriptors as usize); + for i in 0..num_descriptors { - match HidClassDescriptor::from_bytes(&bytes[(Self::HID_DESC_FIXED_SIZE + i*Self::HID_CLASS_DESC_SIZE) as usize..(Self::HID_DESC_FIXED_SIZE + (i+1)*Self::HID_CLASS_DESC_SIZE) as usize]) { + match HidClassDescriptor::from_bytes( + &bytes[(Self::HID_DESC_FIXED_SIZE + i * Self::HID_CLASS_DESC_SIZE) as usize + ..(Self::HID_DESC_FIXED_SIZE + (i + 1) * Self::HID_CLASS_DESC_SIZE) as usize], + ) { Ok(desc) => descriptors.push(*desc), Err(e) => return Err(anyhow!("{e:?}")), } } - + Ok(Self { length, kind, hid_spec_release: u16::from_ne_bytes(bytes[2..4].try_into()?), country_code: bytes[4], num_descriptors, - descriptors + descriptors, }) } - pub fn get_report_desc(&self) -> Result { for desc in self.descriptors.iter() { match desc.desc_type { HidClassType::Report => return Ok(*desc), - _ => () + _ => (), } } return Err(anyhow!("No Report descriptor found")); - } -} \ No newline at end of file +} diff --git a/drivers/input/usbhidd/src/main.rs b/drivers/input/usbhidd/src/main.rs index b3cb97e98e..d49f1c1111 100644 --- a/drivers/input/usbhidd/src/main.rs +++ b/drivers/input/usbhidd/src/main.rs @@ -15,8 +15,8 @@ use xhcid_interface::{ use crate::descs::HidDescriptor; -mod reqs; mod descs; +mod reqs; fn send_key_event(display: &mut ProducerHandle, usage_page: u16, usage: u16, pressed: bool) { let scancode = match usage_page { diff --git a/drivers/usb/xhcid/src/xhci/scheme.rs b/drivers/usb/xhcid/src/xhci/scheme.rs index fe80d2062d..30ee55a5b2 100644 --- a/drivers/usb/xhcid/src/xhci/scheme.rs +++ b/drivers/usb/xhcid/src/xhci/scheme.rs @@ -477,7 +477,6 @@ impl From for EndpDesc { } } - impl From for SuperSpeedCmp { fn from(d: usb::SuperSpeedCompanionDescriptor) -> Self { Self { @@ -1525,60 +1524,74 @@ impl Xhci { let mut unknown_descs = SmallVec::new(); let mut iface_unknown_descs = SmallVec::<[UnknownDesc; 1]>::new(); let mut iter = descriptors.into_iter().peekable(); - + let mut cur_iface = None; let mut cur_endpoint = None; let mut endpoints = SmallVec::<[EndpDesc; 4]>::new(); while let Some(item) = iter.next() { debug!("Processing descriptor {item:?}"); - - + match item { AnyDescriptor::Interface(idesc) => { if let Some(cur_idesc) = cur_iface { - if let Some(endp) = cur_endpoint{ + if let Some(endp) = cur_endpoint { if !endpoints.contains(&endp) { endpoints.push(endp); } } interface_descs.push( - self.new_if_desc(port_id, slot, cur_idesc, endpoints, iface_unknown_descs, lang_id) - .await?, + self.new_if_desc( + port_id, + slot, + cur_idesc, + endpoints, + iface_unknown_descs, + lang_id, + ) + .await?, ); } cur_iface = Some(idesc); endpoints = SmallVec::new(); iface_unknown_descs = SmallVec::new(); - }, + } AnyDescriptor::Endpoint(endpdesc) => { if let Some(desc) = cur_endpoint { endpoints.push(desc); } cur_endpoint = Some(EndpDesc::from(endpdesc)); - }, + } AnyDescriptor::SuperSpeedCompanion(ssc) => { if let Some(endp) = cur_endpoint.as_mut() { endp.ssc = Some(SuperSpeedCmp::from(ssc.clone())); } else { error!("Found a superspeedcompanion descriptor before encountering any endpoint descriptor."); } - }, + } AnyDescriptor::SuperSpeedPlusCompanion(sscp) => { if let Some(endp) = cur_endpoint.as_mut() { endp.sspc = Some(SuperSpeedPlusIsochCmp::from(sscp.clone())); } else { error!("Found a superspeedpluscompanion descriptor before encountering any endpoint descriptor."); } - }, + } AnyDescriptor::Unknown(bytes) => { // If we're processing an interface, add the desc to it, otherwise add it to the configuration if cur_iface.is_none() { - unknown_descs.push(UnknownDesc {len: bytes[0], kind: bytes[1], all_bytes: bytes}); + unknown_descs.push(UnknownDesc { + len: bytes[0], + kind: bytes[1], + all_bytes: bytes, + }); } else { - iface_unknown_descs.push(UnknownDesc {len: bytes[0], kind: bytes[1], all_bytes: bytes}); + iface_unknown_descs.push(UnknownDesc { + len: bytes[0], + kind: bytes[1], + all_bytes: bytes, + }); } - }, + } _ => { error!("Found unexpected descriptor {item:?} while parsing config descriptors, skipping"); continue; @@ -1587,18 +1600,27 @@ impl Xhci { } // Push the last endpoint - if let Some(endp) = cur_endpoint{ + if let Some(endp) = cur_endpoint { if !endpoints.contains(&endp) { endpoints.push(endp); } } - + // Push the last interface if let Some(idesc) = cur_iface { - let new_if_desc = self.new_if_desc(port_id, slot, idesc, endpoints, iface_unknown_descs, lang_id).await?; - if !interface_descs.contains(&new_if_desc) { - interface_descs.push(new_if_desc); - } + let new_if_desc = self + .new_if_desc( + port_id, + slot, + idesc, + endpoints, + iface_unknown_descs, + lang_id, + ) + .await?; + if !interface_descs.contains(&new_if_desc) { + interface_descs.push(new_if_desc); + } } config_descs.push(ConfDesc { @@ -1615,7 +1637,7 @@ impl Xhci { attributes: desc.attributes, max_power: desc.max_power, interface_descs, - unknown_descs + unknown_descs, }); }