From 571bd9cf3e3b32e86f83dd0d45f7ca741a71336a Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 11 Apr 2024 09:36:20 -0600 Subject: [PATCH] usbhidd: move code to rehid --- Cargo.lock | 11 +- usbhidd/Cargo.toml | 2 +- usbhidd/src/main.rs | 57 +----- usbhidd/src/report_desc.rs | 393 ------------------------------------ usbhidd/src/reqs.rs | 9 +- usbhidd/src/usage_tables.rs | 45 ----- 6 files changed, 18 insertions(+), 499 deletions(-) delete mode 100644 usbhidd/src/report_desc.rs delete mode 100644 usbhidd/src/usage_tables.rs diff --git a/Cargo.lock b/Cargo.lock index 7286e2c967..d683e99a04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1137,6 +1137,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" +[[package]] +name = "rehid" +version = "0.1.0" +source = "git+https://gitlab.redox-os.org/redox-os/rehid.git#9bf6c9ef7cc4f95932fcaabfa3141a24bfd39bbf" +dependencies = [ + "bitflags 2.5.0", + "ux", +] + [[package]] name = "rtl8139d" version = "0.1.0" @@ -1520,7 +1529,7 @@ dependencies = [ "orbclient", "redox-log", "redox_syscall 0.5.1", - "ux", + "rehid", "xhcid", ] diff --git a/usbhidd/Cargo.toml b/usbhidd/Cargo.toml index b1cd4f0e9f..54a80e1e2f 100644 --- a/usbhidd/Cargo.toml +++ b/usbhidd/Cargo.toml @@ -13,5 +13,5 @@ log = "0.4" orbclient = "0.3.47" redox-log = "0.1" redox_syscall = "0.5" -ux = "0.1" +rehid = { git = "https://gitlab.redox-os.org/redox-os/rehid.git" } xhcid = { path = "../xhcid" } diff --git a/usbhidd/src/main.rs b/usbhidd/src/main.rs index 2165b6e604..4235c78457 100644 --- a/usbhidd/src/main.rs +++ b/usbhidd/src/main.rs @@ -7,64 +7,18 @@ use std::os::unix::io::AsRawFd; use bitflags::bitflags; use orbclient::KeyEvent as OrbKeyEvent; use redox_log::{OutputBuilder, RedoxLogger}; +use rehid::{binary_view::BinaryView, report_desc::{self, ReportTy}, usage_tables}; use xhcid_interface::{ConfigureEndpointsReq, DevDesc, PortReqRecipient, XhciClientHandle}; mod keymap; -mod report_desc; mod reqs; -mod usage_tables; use report_desc::{LocalItemsState, MainCollectionFlags, MainItem, MainItemFlags, GlobalItemsState, ReportFlatIter, ReportItem, ReportIter, ReportIterItem, REPORT_DESC_TY}; -use reqs::ReportTy; fn div_round_up(num: u32, denom: u32) -> u32 { if num % denom == 0 { num / denom } else { num / denom + 1 } } -struct BinaryView<'a> { - data: &'a [u8], - offset: usize, - len: usize, -} -impl<'a> BinaryView<'a> { - pub fn new(data: &'a [u8], offset: usize, len: usize) -> Self { - Self { - data, - offset, - len, - } - } - pub fn get(&self, bit_index: usize) -> Option { - let bit_index = self.offset + bit_index; - - if bit_index >= self.offset + self.len { return None } - - let byte_index = bit_index / 8; - let bits_from_first = bit_index % 8; - let byte = self.data.get(byte_index)?; - Some(byte & (1 << bits_from_first) != 0) - } - pub fn read_u8(&self, bit_index: usize) -> Option { - let bit_index = self.offset + bit_index; - - if bit_index >= self.offset + self.len { return None } - - let first = bit_index / 8; - let bits_from_first = bit_index % 8; - let first_byte = self.data.get(first)?; - let lo = first_byte >> bits_from_first; - - let hi = if bits_from_first > 0 { - let hi = self.data.get(first + 1)? & ((1 << bits_from_first) - 1); - let bits_to_next = 8 - bits_from_first; - hi << bits_to_next - } else { 0 }; - - - Some(lo | hi) - } -} - fn setup_logging() -> Option<&'static RedoxLogger> { let mut logger = RedoxLogger::new() .with_output( @@ -403,11 +357,12 @@ fn main() { let report_count = global_state.report_count.unwrap(); log::trace!( - "size {} count {} at {} length {}", + "size {} count {} offset {} length {} usage page {:?}", report_size, report_count, bit_offset, - bit_length + bit_length, + global_state.usage_page ); // TODO: For now, the dynamic value usages cannot overlap with selector usages... @@ -516,7 +471,7 @@ fn main() { } } } else { - log::trace!("Unknown report variable item: size {} count {} at {}", report_size, report_count, bit_offset); + log::trace!("Unknown report variable item: size {} count {} offset {} usage page {:?}", report_size, report_count, bit_offset, global_state.usage_page); } } else { // The item is an array. @@ -532,7 +487,7 @@ fn main() { log::trace!("Report index array {}: {:#x}", report_index, usage); } } else { - log::trace!("Unknown report array item: size {} count {} at {}", report_size, report_count, bit_offset); + log::trace!("Unknown report array item: size {} count {} offset {} usage page {:?}", report_size, report_count, bit_offset, global_state.usage_page); } } } diff --git a/usbhidd/src/report_desc.rs b/usbhidd/src/report_desc.rs deleted file mode 100644 index 2835ba225c..0000000000 --- a/usbhidd/src/report_desc.rs +++ /dev/null @@ -1,393 +0,0 @@ -use bitflags::bitflags; -use ux::{u2, u4}; - -use crate::reqs::ReportTy; - -/*#[repr(u8)] -enum Protocol { - -}*/ - -bitflags! { - #[derive(Clone, Copy, Debug)] - 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, -} -impl MainItem { - pub fn report_ty(&self) -> Option { - match self { - Self::Input(_) => Some(ReportTy::Input), - Self::Output(_) => Some(ReportTy::Output), - Self::Feature(_) => Some(ReportTy::Feature), - _ => None, - } - } -} -#[derive(Debug)] -pub enum GlobalItem { - UsagePage(u32), - LogicalMinimum(u32), - LogicalMaximum(u32), - PhysicalMinimum(u32), - PhysicalMaximum(u32), - UnitExponent(u32), - Unit(u32), - ReportSize(u32), - ReportId(u32), - ReportCount(u32), - Push, - Pop, -} - -#[derive(Clone, Copy, Debug, Default)] -pub struct GlobalItemsState { - pub usage_page: Option, - pub logical_min: Option, - pub logical_max: Option, - pub physical_min: Option, - pub physical_max: Option, - pub unit_exponent: Option, - pub unit: Option, - pub report_size: Option, - pub report_id: Option, - pub report_count: Option, -} - -#[derive(Debug)] -pub struct Invalid; - -pub fn update_global_state(current_state: &mut GlobalItemsState, stack: &mut Vec, report_item: &ReportItem) -> Result<(), Invalid> { - match report_item { - ReportItem::Global(global) => match global { - &GlobalItem::UsagePage(u) => current_state.usage_page = Some(u), - &GlobalItem::LogicalMinimum(m) => current_state.logical_min = Some(m), - &GlobalItem::LogicalMaximum(m) => current_state.logical_max = Some(m), - &GlobalItem::PhysicalMinimum(m) => current_state.physical_min = Some(m), - &GlobalItem::PhysicalMaximum(m) => current_state.physical_max = Some(m), - &GlobalItem::UnitExponent(e) => current_state.unit_exponent = Some(e), - &GlobalItem::Unit(u) => current_state.unit = Some(u), - &GlobalItem::ReportSize(s) => current_state.report_size = Some(s), - &GlobalItem::ReportId(i) => current_state.report_id = Some(i), - &GlobalItem::ReportCount(c) => current_state.report_count = Some(c), - &GlobalItem::Push => stack.push(*current_state), - &GlobalItem::Pop => *current_state = stack.pop().ok_or(Invalid)?, - } - ReportItem::Local(local) => (), // TODO - ReportItem::Main(_) => (), - } - Ok(()) -} - -#[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(Clone, Copy, Debug, Default)] -pub struct LocalItemsState { - pub usage: Option, - pub usage_min: Option, - pub usage_max: Option, - pub desig_idx: Option, - pub desig_min: Option, - pub desig_max: Option, - pub str_idx: Option, - pub str_min: Option, - pub str_max: Option, -} -pub fn update_local_state(current_state: &mut LocalItemsState, report_item: &ReportItem) { - match report_item { - ReportItem::Local(local) => match local { - &LocalItem::Usage(u) => current_state.usage = Some(u), - &LocalItem::UsageMinimum(m) => current_state.usage_min = Some(m), - &LocalItem::UsageMaximum(m) => current_state.usage_max = Some(m), - &LocalItem::DesignatorIndex(i) => current_state.desig_idx = Some(i), - &LocalItem::DesignatorMinimum(m) => current_state.desig_min = Some(m), - &LocalItem::DesignatorMaximum(m) => current_state.desig_max = Some(m), - &LocalItem::StringIndex(i) => current_state.str_idx = Some(i), - &LocalItem::StringMinimum(m) => current_state.str_min = Some(m), - &LocalItem::StringMaximum(m) => current_state.str_max = Some(m), - &LocalItem::Delimeter(_) => todo!(), - }, - _ => (), - } -} - -#[derive(Debug)] -pub enum ReportItem { - Main(MainItem), - Global(GlobalItem), - Local(LocalItem), -} -impl ReportItem { - pub fn as_main_item(&self) -> Option<&MainItem> { - match self { - Self::Main(m) => Some(m), - _ => None, - } - } -} -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::ReportId(value).into(), 1 + real_size), - 0b1001 => (GlobalItem::ReportCount(value).into(), 1 + real_size), - 0b1010 => (GlobalItem::Push.into(), 1), - 0b1011 => (GlobalItem::Pop.into(), 1), - _ => 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 - // TODO: When GATs are available, this could be done simply using iterators. Every collection - // yields a child iterator, which returns the mutable reference to the flat iter to its parent - // when dropped. - 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 ReportIterItem { - pub fn as_item(&self) -> Option<&ReportItem> { - match self { - Self::Item(i) => Some(i), - _ => None, - } - } - pub fn as_collection(&self) -> Option<(u8, &[ReportIterItem])> { - match self { - &Self::Collection(n, ref c) => Some((n, c)), - _ => None, - } - } -} -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/usbhidd/src/reqs.rs b/usbhidd/src/reqs.rs index 680497279b..b7887b2490 100644 --- a/usbhidd/src/reqs.rs +++ b/usbhidd/src/reqs.rs @@ -1,5 +1,6 @@ use std::slice; +use rehid::report_desc::ReportTy; use xhcid_interface::{ DeviceReqData, PortReqRecipient, PortReqTy, XhciClientHandle, XhciClientHandleError, }; @@ -11,14 +12,6 @@ const SET_IDLE_REQ: u8 = 0xA; const GET_PROTOCOL_REQ: u8 = 0x3; const SET_PROTOCOL_REQ: u8 = 0xB; -#[repr(u8)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum ReportTy { - Input = 1, - Output, - Feature, -} - fn concat(hi: u8, lo: u8) -> u16 { (u16::from(hi) << 8) | u16::from(lo) } diff --git a/usbhidd/src/usage_tables.rs b/usbhidd/src/usage_tables.rs deleted file mode 100644 index cf7b8d3424..0000000000 --- a/usbhidd/src/usage_tables.rs +++ /dev/null @@ -1,45 +0,0 @@ -// See https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf - -#[repr(u8)] -pub enum UsagePage { - GenericDesktop = 1, - SimulationsControl, - VrControls, - SportControls, - GameControls, - GenericDeviceControls, - KeyboardOrKeypad, - Led, - Button, - Ordinal, - TelephonyDevice, - Consumer, - Digitizer, - Unicode = 0x10, - AlphanumericDisplay = 0x14, - MedicalInstrument = 0x40, -} - -#[repr(u8)] -pub enum GenericDesktopUsage { - Pointer = 0x01, - Mouse, - Joystick = 0x04, - GamePad, - Keyboard, - Keypad, - MultiAxisController, - - // 0x0A-0x2F are reserved - - CountedBuffer = 0x3A, - SysControl = 0x80, -} - -#[repr(u8)] -pub enum KeyboardOrKeypadUsage { - KbdErrorRollover = 0x1, - KbdPostFail, - KbdErrorUndefined, - // the rest are used as regular keycodes -}