From 53ddfbb88eb50c93651c74c3db89faff4f2ed4b3 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 8 Feb 2020 11:05:29 +0100 Subject: [PATCH] Fix the HID report descriptor parsing. --- usbhidd/Cargo.toml | 1 - usbhidd/src/main.rs | 221 +-------------------------- usbhidd/src/report_desc.rs | 275 ++++++++++++++++++++++++++++++++++ xhcid/src/driver_interface.rs | 1 - xhcid/src/xhci/mod.rs | 4 +- 5 files changed, 281 insertions(+), 221 deletions(-) create mode 100644 usbhidd/src/report_desc.rs diff --git a/usbhidd/Cargo.toml b/usbhidd/Cargo.toml index f535b7e35e..b76c7ad501 100644 --- a/usbhidd/Cargo.toml +++ b/usbhidd/Cargo.toml @@ -8,7 +8,6 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -base64 = "0.11" bitflags = "1.2" ux = "0.1" xhcid = { path = "../xhcid" } diff --git a/usbhidd/src/main.rs b/usbhidd/src/main.rs index 76c80af142..3723b45877 100644 --- a/usbhidd/src/main.rs +++ b/usbhidd/src/main.rs @@ -1,221 +1,11 @@ use std::convert::TryInto; use std::env; -use bitflags::bitflags; -use ux::{u2, u4}; use xhcid_interface::{DevDesc, XhciClientHandle}; -/*#[repr(u8)] -enum Protocol { +mod report_desc; -}*/ - -bitflags! { - pub struct MainItemFlags: u32 { - const CONSTANT = 1 << 0; - const VARIABLE = 1 << 1; - const RELATIVE = 1 << 2; - const WRAP = 1 << 3; - const NONLINEAR = 1 << 4; - const NO_PREFERRED_STATE = 1 << 5; - const NULL_STATE = 1 << 6; - const VOLATILE = 1 << 7; - const BUFFERED_BYTES = 1 << 8; - } -} -#[repr(u8)] -pub enum MainCollectionFlags { - Physical = 0, - Application, - Logical, - Report, - NamedArray, - UsageSwitch, - UsageModifier, -} - -const REPORT_DESC_TY: u8 = 34; - -#[derive(Debug)] -enum MainItem { - Input(u32), - Output(u32), - Feature(u32), - Collection(u8), - EndOfCollection, -} -#[derive(Debug)] -enum GlobalItem { - UsagePage(u32), - LogicalMinimum(u32), - LogicalMaximum(u32), - PhysicalMinimum(u32), - PhysicalMaximum(u32), - UnitExponent(u32), - Unit(u32), - ReportSize(u32), - RepordId(u32), - ReportCount(u32), - Push(u32), - Pop(u32), -} -#[derive(Debug)] -enum LocalItem { - Usage(u32), - UsageMinimum(u32), - UsageMaximum(u32), - DesignatorIndex(u32), - DesignatorMinimum(u32), - DesignatorMaximum(u32), - StringIndex(u32), - StringMinimum(u32), - StringMaximum(u32), - Delimeter(u32), -} -#[derive(Debug)] -enum ReportItem { - Main(MainItem), - Global(GlobalItem), - Local(LocalItem), -} -impl From for ReportItem { - fn from(main: MainItem) -> Self { - Self::Main(main) - } -} -impl From for ReportItem { - fn from(main: GlobalItem) -> Self { - Self::Global(main) - } -} -impl From for ReportItem { - fn from(main: LocalItem) -> Self { - Self::Local(main) - } -} - -impl ReportItem { - fn size(size: u2) -> u8 { - match u8::from(size) { - 0 => 0, - 1 => 1, - 2 => 2, - 3 => 4, - _ => unreachable!(), - } - } - fn parse_short(size: u2, ty: u2, tag: u4, bytes: &[u8]) -> Option<(Self, usize)> { - Some(match (u8::from(tag), u8::from(ty)) { - (tag, 0b00) => { - let real_size = Self::size(size) as usize; - let mut value_bytes = [0u8; 4]; - if real_size > 0 { - value_bytes[..real_size].copy_from_slice(&bytes[..real_size]) - }; - let value = u32::from_le_bytes(value_bytes); - - match tag { - 0b1000 => (MainItem::Input(value).into(), 1 + real_size), - 0b1001 => (MainItem::Output(value).into(), 1 + real_size), - 0b1011 => (MainItem::Feature(value).into(), 1 + real_size), - 0b1010 => (MainItem::Collection(bytes[0]).into(), 2), - 0b1100 => (MainItem::EndOfCollection.into(), 1 + real_size), - _ => return None, - } - } - (tag, 0b01) => { - let real_size = Self::size(size) as usize; - let mut value_bytes = [0u8; 4]; - if real_size > 0 { - value_bytes[..real_size].copy_from_slice(&bytes[..real_size]) - }; - let value = u32::from_le_bytes(value_bytes); - match tag { - 0b0000 => (GlobalItem::UsagePage(value).into(), 1 + real_size), - 0b0001 => (GlobalItem::LogicalMinimum(value).into(), 1 + real_size), - 0b0010 => (GlobalItem::LogicalMaximum(value).into(), 1 + real_size), - 0b0011 => (GlobalItem::PhysicalMinimum(value).into(), 1 + real_size), - 0b0100 => (GlobalItem::PhysicalMaximum(value).into(), 1 + real_size), - 0b0101 => (GlobalItem::UnitExponent(value).into(), 1 + real_size), - 0b0110 => (GlobalItem::Unit(value).into(), 1 + real_size), - 0b0111 => (GlobalItem::ReportSize(value).into(), 1 + real_size), - 0b1000 => (GlobalItem::RepordId(value).into(), 1 + real_size), - 0b1001 => (GlobalItem::ReportCount(value).into(), 1 + real_size), - 0b1010 => (GlobalItem::Push(value).into(), 1 + real_size), - 0b1011 => (GlobalItem::Pop(value).into(), 1 + real_size), - _ => return None, - } - } - (tag, 0b10) => { - let real_size = Self::size(size) as usize; - let mut value_bytes = [0u8; 4]; - if real_size > 0 { - value_bytes[..real_size].copy_from_slice(&bytes[..real_size]) - }; - let value = u32::from_le_bytes(value_bytes); - match tag { - 0b0000 => (LocalItem::Usage(value).into(), 1 + real_size), - 0b0001 => (LocalItem::UsageMinimum(value).into(), 1 + real_size), - 0b0010 => (LocalItem::UsageMaximum(value).into(), 1 + real_size), - 0b0011 => (LocalItem::DesignatorIndex(value).into(), 1 + real_size), - 0b0100 => (LocalItem::DesignatorMinimum(value).into(), 1 + real_size), - 0b0101 => (LocalItem::DesignatorMaximum(value).into(), 1 + real_size), - 0b0111 => (LocalItem::StringIndex(value).into(), 1 + real_size), - 0b1000 => (LocalItem::StringMinimum(value).into(), 1 + real_size), - 0b1001 => (LocalItem::StringMaximum(value).into(), 1 + real_size), - 0b1010 => (LocalItem::Delimeter(value).into(), 1 + real_size), - _ => return None, - } - } - (_, 0b11) => panic!("Calling parse_short but with long item"), - _ => unreachable!(), - }) - } - fn parse_long(size: u8, long_tag: u8, bytes: &[u8]) -> (Self, usize) { - todo!() - } -} - -struct ReportIter<'a> { - desc: &'a [u8], - offset: usize, -} -impl<'a> ReportIter<'a> { - fn new(desc: &'a [u8]) -> Self { - Self { desc, offset: 0 } - } -} -impl<'a> Iterator for ReportIter<'a> { - type Item = ReportItem; - - fn next(&mut self) -> Option { - let first = self.desc[self.offset]; - let size = first & 0b11; - let ty = first & 0b1100 >> 2; - let tag = first & 0b11110000 >> 4; - - if size == 0b10 && ty == 3 && tag == 0b1111 { - // Long Item - let size = self.desc[self.offset + 1]; - let long_tag = self.desc[self.offset + 2]; - let data = &self.desc[self.offset + 2..self.offset + 2 + size as usize]; - - let (item, len) = ReportItem::parse_long(size, long_tag, data); - self.offset += len; - Some(item) - } else { - // Short Item - - // Although there is a 2-bit size field, the size doesn't have to be the actual size of the data. - let data = &self.desc[self.offset + 1..]; - - let (item, len) = - ReportItem::parse_short(u2::new(size), u2::new(ty), u4::new(tag), data)?; - self.offset += len; - Some(item) - } - } -} +use report_desc::{ReportIter, ReportFlatIter, REPORT_DESC_TY}; fn main() { let mut args = env::args().skip(1); @@ -254,11 +44,8 @@ fn main() { report_desc_len, ) .expect("Failed to retrieve report descriptor"); - use std::io::Write as _; - std::io::stdout() - .write_all(base64::encode(&report_desc_bytes).as_bytes()) - .unwrap(); - let iterator = ReportIter::new(&report_desc_bytes); + + let iterator = ReportIter::new(ReportFlatIter::new(&report_desc_bytes)); for item in iterator { println!("HID ITEM {:?}", item); diff --git a/usbhidd/src/report_desc.rs b/usbhidd/src/report_desc.rs new file mode 100644 index 0000000000..e77da8afb5 --- /dev/null +++ b/usbhidd/src/report_desc.rs @@ -0,0 +1,275 @@ +use bitflags::bitflags; +use ux::{u2, u4}; + +/*#[repr(u8)] +enum Protocol { + +}*/ + +bitflags! { + pub struct MainItemFlags: u32 { + const CONSTANT = 1 << 0; + const VARIABLE = 1 << 1; + const RELATIVE = 1 << 2; + const WRAP = 1 << 3; + const NONLINEAR = 1 << 4; + const NO_PREFERRED_STATE = 1 << 5; + const NULL_STATE = 1 << 6; + const VOLATILE = 1 << 7; + const BUFFERED_BYTES = 1 << 8; + } +} +#[repr(u8)] +pub enum MainCollectionFlags { + Physical = 0, + Application, + Logical, + Report, + NamedArray, + UsageSwitch, + UsageModifier, +} + +pub const REPORT_DESC_TY: u8 = 34; + +#[derive(Debug)] +pub enum MainItem { + Input(u32), + Output(u32), + Feature(u32), + Collection(u8), + EndOfCollection, +} +#[derive(Debug)] +pub enum GlobalItem { + UsagePage(u32), + LogicalMinimum(u32), + LogicalMaximum(u32), + PhysicalMinimum(u32), + PhysicalMaximum(u32), + UnitExponent(u32), + Unit(u32), + ReportSize(u32), + RepordId(u32), + ReportCount(u32), + Push(u32), + Pop(u32), +} +#[derive(Debug)] +pub enum LocalItem { + Usage(u32), + UsageMinimum(u32), + UsageMaximum(u32), + DesignatorIndex(u32), + DesignatorMinimum(u32), + DesignatorMaximum(u32), + StringIndex(u32), + StringMinimum(u32), + StringMaximum(u32), + Delimeter(u32), +} +#[derive(Debug)] +pub enum ReportItem { + Main(MainItem), + Global(GlobalItem), + Local(LocalItem), +} +impl From for ReportItem { + fn from(main: MainItem) -> Self { + Self::Main(main) + } +} +impl From for ReportItem { + fn from(main: GlobalItem) -> Self { + Self::Global(main) + } +} +impl From for ReportItem { + fn from(main: LocalItem) -> Self { + Self::Local(main) + } +} + +impl ReportItem { + pub fn size(size: u2) -> u8 { + match u8::from(size) { + 0 => 0, + 1 => 1, + 2 => 2, + 3 => 4, + _ => unreachable!(), + } + } + pub fn parse_short(size: u2, ty: u2, tag: u4, bytes: &[u8]) -> Option<(Self, usize)> { + Some(match (u8::from(tag), u8::from(ty)) { + (tag, 0b00) => { + let real_size = Self::size(size) as usize; + let mut value_bytes = [0u8; 4]; + if real_size > 0 { + value_bytes[..real_size].copy_from_slice(&bytes[..real_size]) + }; + let value = u32::from_le_bytes(value_bytes); + + match tag { + 0b1000 => (MainItem::Input(value).into(), 1 + real_size), + 0b1001 => (MainItem::Output(value).into(), 1 + real_size), + 0b1011 => (MainItem::Feature(value).into(), 1 + real_size), + 0b1010 => (MainItem::Collection(bytes[0]).into(), 2), + 0b1100 => (MainItem::EndOfCollection.into(), 1 + real_size), + _ => return None, + } + } + (tag, 0b01) => { + let real_size = Self::size(size) as usize; + let mut value_bytes = [0u8; 4]; + if real_size > 0 { + value_bytes[..real_size].copy_from_slice(&bytes[..real_size]) + }; + let value = u32::from_le_bytes(value_bytes); + match tag { + 0b0000 => (GlobalItem::UsagePage(value).into(), 1 + real_size), + 0b0001 => (GlobalItem::LogicalMinimum(value).into(), 1 + real_size), + 0b0010 => (GlobalItem::LogicalMaximum(value).into(), 1 + real_size), + 0b0011 => (GlobalItem::PhysicalMinimum(value).into(), 1 + real_size), + 0b0100 => (GlobalItem::PhysicalMaximum(value).into(), 1 + real_size), + 0b0101 => (GlobalItem::UnitExponent(value).into(), 1 + real_size), + 0b0110 => (GlobalItem::Unit(value).into(), 1 + real_size), + 0b0111 => (GlobalItem::ReportSize(value).into(), 1 + real_size), + 0b1000 => (GlobalItem::RepordId(value).into(), 1 + real_size), + 0b1001 => (GlobalItem::ReportCount(value).into(), 1 + real_size), + 0b1010 => (GlobalItem::Push(value).into(), 1 + real_size), + 0b1011 => (GlobalItem::Pop(value).into(), 1 + real_size), + _ => return None, + } + } + (tag, 0b10) => { + let real_size = Self::size(size) as usize; + let mut value_bytes = [0u8; 4]; + if real_size > 0 { + value_bytes[..real_size].copy_from_slice(&bytes[..real_size]) + }; + let value = u32::from_le_bytes(value_bytes); + match tag { + 0b0000 => (LocalItem::Usage(value).into(), 1 + real_size), + 0b0001 => (LocalItem::UsageMinimum(value).into(), 1 + real_size), + 0b0010 => (LocalItem::UsageMaximum(value).into(), 1 + real_size), + 0b0011 => (LocalItem::DesignatorIndex(value).into(), 1 + real_size), + 0b0100 => (LocalItem::DesignatorMinimum(value).into(), 1 + real_size), + 0b0101 => (LocalItem::DesignatorMaximum(value).into(), 1 + real_size), + 0b0111 => (LocalItem::StringIndex(value).into(), 1 + real_size), + 0b1000 => (LocalItem::StringMinimum(value).into(), 1 + real_size), + 0b1001 => (LocalItem::StringMaximum(value).into(), 1 + real_size), + 0b1010 => (LocalItem::Delimeter(value).into(), 1 + real_size), + _ => return None, + } + } + (_, 0b11) => panic!("Calling parse_short but with long item"), + _ => unreachable!(), + }) + } + pub fn parse_long(size: u8, long_tag: u8, bytes: &[u8]) -> (Self, usize) { + todo!() + } +} + +pub struct ReportFlatIter<'a> { + desc: &'a [u8], + offset: usize, +} +impl<'a> ReportFlatIter<'a> { + pub fn new(desc: &'a [u8]) -> Self { + Self { desc, offset: 0 } + } +} +impl<'a> Iterator for ReportFlatIter<'a> { + type Item = ReportItem; + + fn next(&mut self) -> Option { + if self.offset >= self.desc.len() { return None } + + let first = self.desc[self.offset]; + let size = first & 0b11; + let ty = (first & 0b1100) >> 2; + let tag = (first & 0b11110000) >> 4; + + if size == 0b10 && ty == 3 && tag == 0b1111 { + // Long Item + let size = self.desc[self.offset + 1]; + let long_tag = self.desc[self.offset + 2]; + let data = &self.desc[self.offset + 2..self.offset + 2 + size as usize]; + + let (item, len) = ReportItem::parse_long(size, long_tag, data); + self.offset += len; + Some(item) + } else { + // Short Item + + // Although there is a 2-bit size field, the size doesn't have to be the actual size of the data. + let data = &self.desc[self.offset + 1..]; + + let (item, len) = + ReportItem::parse_short(u2::new(size), u2::new(ty), u4::new(tag), data)?; + self.offset += len; + Some(item) + } + } +} + +pub struct ReportIter<'a> { + flat: ReportFlatIter<'a>, + error: bool, + // this is just for reusing the vec + open_collections: Vec<(u8, Vec)>, +} +#[derive(Debug)] +pub enum ReportIterItem { + // collection and end of collection tags will never be found here + Item(ReportItem), + Collection(u8, Vec), +} +impl<'a> ReportIter<'a> { + pub fn new(flat: ReportFlatIter<'a>) -> Self { + Self { + flat, + error: false, + open_collections: Vec::new(), + } + } +} +impl<'a> Iterator for ReportIter<'a> { + type Item = ReportIterItem; + + fn next(&mut self) -> Option { + if self.error { return None } + + self.open_collections.clear(); + + loop { + let item = self.flat.next()?; + + match item { + ReportItem::Main(MainItem::Collection(m)) => { + self.open_collections.push((m, Vec::new())); + } + ReportItem::Main(MainItem::EndOfCollection) => { + let (value, finished_collection) = match self.open_collections.pop() { + Some(finished) => finished, + None => { + self.error = true; + return None; + } + }; + if let Some((_, ref mut last)) = self.open_collections.last_mut() { + last.push(ReportIterItem::Collection(value, finished_collection)); + } else { + return Some(ReportIterItem::Collection(value, finished_collection)); + } + } + other if self.open_collections.is_empty() => return Some(ReportIterItem::Item(other)), + other => { + self.open_collections.last_mut().unwrap().1.push(ReportIterItem::Item(other)) + } + } + } + } +} diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index b20cce5e74..dc88a7a6bc 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -261,7 +261,6 @@ impl XhciClientHandle { pub fn get_standard_descs(&self) -> result::Result { let path = format!("{}:port{}/descriptors", self.scheme, self.port); let json = std::fs::read(path)?; - std::io::stdout().lock().write_all(&json)?; Ok(serde_json::from_slice(&json)?) } pub fn configure_endpoints( diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index 3b0fea760d..cf6923afd4 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -398,10 +398,10 @@ impl Xhci { .collect::>(), }; - match self.spawn_drivers(i, &mut port_state) { + /*match self.spawn_drivers(i, &mut port_state) { Ok(()) => (), Err(err) => println!("Failed to spawn driver for port {}: `{}`", i, err), - } + }*/ self.port_states.insert(i, port_state); }