Split usb_report_descriptor into usb_hid_{item,usage}

This is cleaner IMO.

* usb_hid_item deals with parsing the report descriptor.
* usb_hid_usage has tables that define each usage.
This commit is contained in:
David Hoppenbrouwers
2022-09-09 02:42:00 +02:00
parent 7f6eb97569
commit 2175e1ed74
11 changed files with 121 additions and 122 deletions

View File

@ -1,5 +1,6 @@
[workspace]
members = [
"usb_report_descriptor",
"usb_hid_item",
"usb_hid_usage",
"usb_request",
]

View File

@ -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.

View File

@ -1,5 +1,5 @@
[package]
name = "usb_report_descriptor"
name = "usb_hid_item"
version = "0.1.0"
edition = "2021"
license = "MIT"

View File

@ -1,4 +1,4 @@
# Report descriptor item parser
# HID report descriptor item parser
## Example

View File

@ -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::Item> {
(!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));

8
usb_hid_usage/Cargo.toml Normal file
View File

@ -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]

View File

@ -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<u16> for Usage {
type Error = super::UnknownUsage;
fn try_from(n: u16) -> Result<Self, Self::Error> {
Ok(NonZeroU16::new(n).map_or(Self::NoButton, Self::Button))
}
}

63
usb_hid_usage/src/lib.rs Normal file
View File

@ -0,0 +1,63 @@
//!
//! ## References
//!
//! * <https://www.usb.org/sites/default/files/hut1_3_0.pdf>
#![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<u16> for Usage {
type Error = super::UnknownUsage;
fn try_from(n: u16) -> Result<Self, Self::Error> {
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<u16> for UsagePage {
type Error = UnknownPage;
fn try_from(n: u16) -> Result<Self, Self::Error> {
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;

View File

@ -1,3 +0,0 @@
usage! {
[0x09]
}

View File

@ -1,75 +0,0 @@
//! ## References
//!
//! * <https://www.usb.org/sites/default/files/hut1_3_0.pdf>
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
}