diff --git a/Cargo.toml b/Cargo.toml index 3001333..be2225c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ - "usb_report_descriptor", + "usb_hid_item", + "usb_hid_usage", "usb_request", ] diff --git a/README.md b/README.md index afe39d5..502e73c 100644 --- a/README.md +++ b/README.md @@ -2,5 +2,6 @@ ## Crates -* [`usb_report_descriptor`](usb_report_descriptor) item parser for HID Report descriptors. +* [`usb_hid_item`](usb_hid_item) HID item parser for report descriptors. +* [`usb_hid_usage`](usb_hid_usage) HID usage pages. * [`usb_request`](usb_request) request formatter & descriptor parser. diff --git a/usb_report_descriptor/Cargo.toml b/usb_hid_item/Cargo.toml similarity index 66% rename from usb_report_descriptor/Cargo.toml rename to usb_hid_item/Cargo.toml index 60b8b8a..e4d9a06 100644 --- a/usb_report_descriptor/Cargo.toml +++ b/usb_hid_item/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "usb_report_descriptor" +name = "usb_hid_item" version = "0.1.0" edition = "2021" license = "MIT" diff --git a/usb_report_descriptor/README.md b/usb_hid_item/README.md similarity index 94% rename from usb_report_descriptor/README.md rename to usb_hid_item/README.md index 87812c7..bf5c8b6 100644 --- a/usb_report_descriptor/README.md +++ b/usb_hid_item/README.md @@ -1,4 +1,4 @@ -# Report descriptor item parser +# HID report descriptor item parser ## Example diff --git a/usb_report_descriptor/src/lib.rs b/usb_hid_item/src/lib.rs similarity index 82% rename from usb_report_descriptor/src/lib.rs rename to usb_hid_item/src/lib.rs index 1727301..ab3ac36 100644 --- a/usb_report_descriptor/src/lib.rs +++ b/usb_hid_item/src/lib.rs @@ -1,10 +1,6 @@ #![doc = include_str!("../README.md")] #![cfg_attr(not(test), no_std)] -pub mod usage; - -pub use usage::{Usage, UsagePage}; - use core::fmt; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -16,7 +12,7 @@ pub enum Item<'a> { Feature(MainFlags), EndCollection, - UsagePage(UsagePage), + UsagePage(u16), LogicalMin(i32), LogicalMax(i32), PhysicalMin(i32), @@ -29,9 +25,10 @@ pub enum Item<'a> { Push, Pop, - Usage(Usage), - UsageMin(u32), - UsageMax(u32), + Usage16(u16), + Usage32(u16, u16), + UsageMin(u16), + UsageMax(u16), DesignatorIndex(u32), DesignatorMin(u32), DesignatorMax(u32), @@ -77,7 +74,7 @@ impl<'a> Item<'a> { const STRING_MAX: u8 = 0x98; const DELIMITER: u8 = 0xa8; - fn parse(data: &'a [u8], usage_page: u16) -> Result<(Self, &'a [u8]), ParseError> { + fn parse(data: &'a [u8]) -> Result<(Self, &'a [u8]), ParseError> { use ParseError::*; let prefix = *data.get(0).ok_or(Truncated)?; let (size, tag); @@ -133,7 +130,7 @@ impl<'a> Item<'a> { Self::FEATURE => Self::Feature(MainFlags(d32u()?)), Self::END_COLLECTION => d_empty(Self::EndCollection)?, - Self::USAGE_PAGE => Self::UsagePage(UsagePage::from_raw(d16u()?)), + Self::USAGE_PAGE => Self::UsagePage(d16u()?), Self::LOGI_MIN => Self::LogicalMin(d32i()?), Self::LOGI_MAX => Self::LogicalMax(d32i()?), Self::PHYS_MIN => Self::PhysicalMin(d32i()?), @@ -146,20 +143,18 @@ impl<'a> Item<'a> { Self::PUSH => Self::Push, Self::POP => Self::Pop, - Self::USAGE => Self::Usage(match d { - &[] => Usage::from_raw(usage_page, 0), - &[a] => Usage::from_raw(usage_page, u16::from_le_bytes([a, 0])), - &[a, b] => Usage::from_raw(usage_page, u16::from_le_bytes([a, b])), - &[a, b, c] => { - Usage::from_raw(u16::from_le_bytes([c, 0]), u16::from_le_bytes([a, b])) - } + Self::USAGE => match d { + &[] => Self::Usage16(u16::from_le_bytes([0, 0])), + &[a] => Self::Usage16(u16::from_le_bytes([a, 0])), + &[a, b] => Self::Usage16(u16::from_le_bytes([a, b])), + &[a, b, c] => Self::Usage32(u16::from_le_bytes([c, 0]), u16::from_le_bytes([a, b])), &[a, b, c, d] => { - Usage::from_raw(u16::from_le_bytes([c, d]), u16::from_le_bytes([a, b])) + Self::Usage32(u16::from_le_bytes([c, d]), u16::from_le_bytes([a, b])) } _ => return Err(UnexpectedData), - }), - Self::USAGE_MIN => Self::UsageMin(d32u()?), - Self::USAGE_MAX => Self::UsageMax(d32u()?), + }, + Self::USAGE_MIN => Self::UsageMin(d16u()?), + Self::USAGE_MAX => Self::UsageMax(d16u()?), Self::DESIGNATOR_INDEX => Self::DesignatorIndex(d32u()?), Self::DESIGNATOR_MIN => Self::DesignatorMin(d32u()?), Self::DESIGNATOR_MAX => Self::DesignatorMax(d32u()?), @@ -250,7 +245,6 @@ pub enum ParseError { pub struct Parser<'a> { data: &'a [u8], - usage_page: u16, } impl<'a> Iterator for Parser<'a> { @@ -259,21 +253,14 @@ impl<'a> Iterator for Parser<'a> { fn next(&mut self) -> Option { (!self.data.is_empty()).then(|| { let e; - (e, self.data) = Item::parse(self.data, self.usage_page)?; - match &e { - Item::UsagePage(p) => self.usage_page = p.as_raw(), - _ => {} - } + (e, self.data) = Item::parse(self.data)?; Ok(e) }) } } pub fn parse(data: &[u8]) -> Parser<'_> { - Parser { - data, - usage_page: 0, - } + Parser { data } } #[cfg(test)] @@ -298,12 +285,12 @@ mod tests { fn qemu_usb_tablet() { let mut it = parse(QEMU_USB_TABLET); let it = &mut it; - tk(it, Item::UsagePage(UsagePage::GenericDesktop)); - tk(it, Item::Usage(Usage::GenericDesktop(usage::generic_desktop::Usage::Mouse))); + tk(it, Item::UsagePage(0x1)); + tk(it, Item::Usage16(0x2)); tk(it, Item::Collection(Collection::Application)); - tk(it, Item::Usage(Usage::GenericDesktop(usage::generic_desktop::Usage::Pointer))); + tk(it, Item::Usage16(0x1)); tk(it, Item::Collection(Collection::Physical)); - tk(it, Item::UsagePage(UsagePage::Button)); + tk(it, Item::UsagePage(0x9)); tk(it, Item::UsageMin(1)); tk(it, Item::UsageMax(3)); tk(it, Item::LogicalMin(0)); @@ -314,9 +301,9 @@ mod tests { tk(it, Item::ReportCount(1)); tk(it, Item::ReportSize(5)); tk(it, Item::Input(MainFlags(0b1))); // constant - tk(it, Item::UsagePage(UsagePage::GenericDesktop)); - tk(it, Item::Usage(Usage::GenericDesktop(usage::generic_desktop::Usage::X))); - tk(it, Item::Usage(Usage::GenericDesktop(usage::generic_desktop::Usage::Y))); + tk(it, Item::UsagePage(1)); + tk(it, Item::Usage16(0x30)); + tk(it, Item::Usage16(0x31)); tk(it, Item::LogicalMin(0)); tk(it, Item::LogicalMax(0x7fff)); tk(it, Item::PhysicalMin(0)); @@ -324,8 +311,8 @@ mod tests { tk(it, Item::ReportSize(16)); tk(it, Item::ReportCount(2)); tk(it, Item::Input(MainFlags(0b010))); // absolute, variable, data - tk(it, Item::UsagePage(UsagePage::GenericDesktop)); - tk(it, Item::Usage(Usage::GenericDesktop(usage::generic_desktop::Usage::Wheel))); + tk(it, Item::UsagePage(1)); + tk(it, Item::Usage16(0x38)); tk(it, Item::LogicalMin(-0x7f)); tk(it, Item::LogicalMax(0x7f)); tk(it, Item::PhysicalMin(0)); diff --git a/usb_hid_usage/Cargo.toml b/usb_hid_usage/Cargo.toml new file mode 100644 index 0000000..f05d7dd --- /dev/null +++ b/usb_hid_usage/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "usb_hid_usage" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/usb_hid_usage/src/button.rs b/usb_hid_usage/src/button.rs new file mode 100644 index 0000000..058dccb --- /dev/null +++ b/usb_hid_usage/src/button.rs @@ -0,0 +1,17 @@ +use core::num::NonZeroU16; + +pub const PAGE: u16 = 0x09; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Usage { + NoButton, + Button(NonZeroU16), +} + +impl TryFrom for Usage { + type Error = super::UnknownUsage; + + fn try_from(n: u16) -> Result { + Ok(NonZeroU16::new(n).map_or(Self::NoButton, Self::Button)) + } +} diff --git a/usb_report_descriptor/src/usage/generic_desktop.rs b/usb_hid_usage/src/generic_desktop.rs similarity index 100% rename from usb_report_descriptor/src/usage/generic_desktop.rs rename to usb_hid_usage/src/generic_desktop.rs diff --git a/usb_hid_usage/src/lib.rs b/usb_hid_usage/src/lib.rs new file mode 100644 index 0000000..691f3a3 --- /dev/null +++ b/usb_hid_usage/src/lib.rs @@ -0,0 +1,63 @@ +//! +//! ## References +//! +//! * + +#![no_std] + +macro_rules! usage { + { [$page:literal] $($i:literal $v:ident)* } => { + pub const PAGE: u16 = $page; + + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[non_exhaustive] + pub enum Usage { + $($v,)* + } + + impl TryFrom for Usage { + type Error = super::UnknownUsage; + + fn try_from(n: u16) -> Result { + Ok(match n { + $($i => Self::$v,)* + _ => return Err(super::UnknownUsage), + }) + } + } + }; +} + +macro_rules! page { + { $($v:ident $m:ident)* } => { + $(pub mod $m;)* + + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[non_exhaustive] + pub enum UsagePage { + $($v,)* + } + + impl TryFrom for UsagePage { + type Error = UnknownPage; + + fn try_from(n: u16) -> Result { + Ok(match n { + $($m::PAGE => Self::$v,)* + _ => return Err(UnknownPage), + }) + } + } + }; +} + +page! { + GenericDesktop generic_desktop + Button button +} + +#[derive(Debug)] +pub struct UnknownUsage; + +#[derive(Debug)] +pub struct UnknownPage; diff --git a/usb_report_descriptor/src/usage/button.rs b/usb_report_descriptor/src/usage/button.rs deleted file mode 100644 index 08a2301..0000000 --- a/usb_report_descriptor/src/usage/button.rs +++ /dev/null @@ -1,3 +0,0 @@ -usage! { - [0x09] -} diff --git a/usb_report_descriptor/src/usage/mod.rs b/usb_report_descriptor/src/usage/mod.rs deleted file mode 100644 index a74aa9f..0000000 --- a/usb_report_descriptor/src/usage/mod.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! ## References -//! -//! * - -macro_rules! usage { - { [$page:literal] $($i:literal $v:ident)* } => { - pub const PAGE: u16 = $page; - - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] - #[non_exhaustive] - pub enum Usage { - $($v,)* - Unknown(u16), - } - - pub(crate) fn from_raw(raw: u16) -> Usage { - use Usage::*; - match raw { - $($i => $v,)* - r => Unknown(r), - } - } - }; -} - -pub mod generic_desktop; -pub mod button; - -macro_rules! page { - { $($v:ident $m:ident)* } => { - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] - #[non_exhaustive] - pub enum UsagePage { - $($v,)* - Unknown(u16), - } - - impl UsagePage { - pub(crate) fn from_raw(raw: u16) -> Self { - match raw { - $($m::PAGE => Self::$v,)* - r => Self::Unknown(r), - } - } - - pub(crate) fn as_raw(&self) -> u16 { - match self { - $(Self::$v => $m::PAGE,)* - Self::Unknown(n) => *n, - } - } - } - - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] - #[non_exhaustive] - pub enum Usage { - $($v($m::Usage),)* - Unknown(u16, u16), - } - - impl Usage { - pub(crate) fn from_raw(page: u16, id: u16) -> Self { - match page { - $($m::PAGE => Self::$v($m::from_raw(id)),)* - _ => Self::Unknown(page, id), - } - } - } - }; -} - -page! { - GenericDesktop generic_desktop - Button button -}