From f6bb61ba189ae233645e4aa3901aa303d134a694 Mon Sep 17 00:00:00 2001 From: Antoine Reversat Date: Thu, 7 May 2026 14:05:13 -0400 Subject: [PATCH] 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