// SPDX-License-Identifier: MIT
//
#![no_std]
//! This crate provides parsing and building of HID Report Descriptors, including the [hid] module
//! to inspect or build a report descriptor in more detail. Check out the `hut` crate for known HID
//! Usages to make sense of the various HID fields.
//!
//! # Parsing HID Report Descriptors
//!
//! Entry point is usually [`ReportDescriptor::try_from(bytes)`](ReportDescriptor::try_from):
//!
//! ```
//! # use hidreport::*;
//! # let bytes: &[u8] = &[0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x05, 0x01, 0x09, 0x02, 0xa1, 0x02, 0x85, 0x1a, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x05, 0x95, 0x05, 0x75, 0x01, 0x15, 0x00, 0x25, 0x01, 0x81, 0x02, 0x75, 0x03, 0x95, 0x01, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x95, 0x02, 0x75, 0x10, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x81, 0x06, 0xa1, 0x02, 0x85, 0x12, 0x09, 0x48, 0x95, 0x01, 0x75, 0x02, 0x15, 0x00, 0x25, 0x01, 0x35, 0x01, 0x45, 0x0c, 0xb1, 0x02, 0x85, 0x1a, 0x09, 0x38, 0x35, 0x00, 0x45, 0x00, 0x95, 0x01, 0x75, 0x10, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x81, 0x06, 0xc0, 0xa1, 0x02, 0x85, 0x12, 0x09, 0x48, 0x75, 0x02, 0x15, 0x00, 0x25, 0x01, 0x35, 0x01, 0x45, 0x0c, 0xb1, 0x02, 0x35, 0x00, 0x45, 0x00, 0x75, 0x04, 0xb1, 0x01, 0x85, 0x1a, 0x05, 0x0c, 0x95, 0x01, 0x75, 0x10, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x0a, 0x38, 0x02, 0x81, 0x06, 0xc0, 0xc0, 0xc0, 0xc0, 0x05, 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x05, 0x01, 0x09, 0x02, 0xa1, 0x02, 0x85, 0x1f, 0x05, 0x0c, 0x0a, 0x38, 0x02, 0x95, 0x01, 0x75, 0x10, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x81, 0x06, 0x85, 0x17, 0x06, 0x00, 0xff, 0x0a, 0x06, 0xff, 0x0a, 0x0f, 0xff, 0x15, 0x00, 0x25, 0x01, 0x35, 0x01, 0x45, 0x0c, 0x95, 0x02, 0x75, 0x02, 0xb1, 0x02, 0x0a, 0x04, 0xff, 0x35, 0x00, 0x45, 0x00, 0x95, 0x01, 0x75, 0x01, 0xb1, 0x02, 0x75, 0x03, 0xb1, 0x01, 0xc0, 0xc0];
//! # fn read_from_device() -> Vec<u8> {
//! # vec![0x1a, 0x00, 0xff, 0xff, 0xfe, 0xff, 00, 00, 00, 0x00]
//! # }
//! #
//! let rdesc: ReportDescriptor = ReportDescriptor::try_from(bytes).unwrap();
//! for r in rdesc.input_reports() {
//! println!("Input Report with report ID: {:?}", r.report_id());
//! }
//!
//! let input_report_bytes = read_from_device();
//! let report = rdesc.find_input_report(&input_report_bytes).unwrap();
//! println!("This is an input report for report ID: {:?}", report.report_id());
//! let field = report.fields().first().unwrap();
//! match field {
//! Field::Variable(var) => {
//! let val: u32 = var.extract(&input_report_bytes).unwrap().into();
//! println!("Field {:?} is of value {}", field, val);
//! }
//! Field::Array(arr) => {
//! let vals: Vec<u32> = arr.extract(&input_report_bytes).unwrap().iter().map(u32::from).collect();
//! println!("Field {:?} has values {:?}", field, vals);
//! }
//! Field::Constant(_) => {
//! println!("Field {:?} is <padding data>", field);
//! }
//! }
//! ```
//!
//! In this document and unless stated otherwise, a reference to "Section a.b.c" refers to the
//! [HID Device Class Definition for HID 1.11](https://www.usb.org/document-library/device-class-definition-hid-111).
//!
//! # Building HID Report Descriptors programmatically
//!
//! This module can be used to build a HID Report Descriptor from scratch via the [hid] module:
//!
//! ```
//! # use crate::hidreport::hid::*;
//! # use crate::hidreport::types::*;
//! # fn build() {
//! use hut::{self, AsUsagePage, AsUsage};
//! let builder = ReportDescriptorBuilder::new();
//! let rdesc: Vec<u8> = builder
//! .append(hut::UsagePage::GenericDesktop.into())
//! .append(hut::GenericDesktop::Mouse.usage().into())
//! .open_collection(CollectionItem::Application)
//! .open_collection(CollectionItem::Physical)
//! .push()
//! .append(LogicalMinimum::from(0).into())
//! .append(LogicalMaximum::from(128).into())
//! .pop()
//! .append(ReportCount::from(2).into())
//! .append(ReportSize::from(8).into())
//! .append(hut::GenericDesktop::X.usage().into())
//! .append(hut::GenericDesktop::Y.usage().into())
//! .input(ItemBuilder::new()
//! .variable()
//! .absolute()
//! .input())
//! .close_collection()
//! .close_collection()
//! .build();
//! # }
//! ```
//! See the [ReportDescriptorBuilder] documentation for more details.
extern crate alloc;
#[cfg(feature = "std")]
extern crate std; // We can do this regardless of `no_std`
use core::ops::Range;
#[cfg(feature = "std")]
use std::hash::{Hash, Hasher};
use thiserror::Error;
use alloc::{string::String, vec, vec::Vec};
#[cfg(feature = "hut")]
use hut::{AsUsage, AsUsagePage};
pub mod hid;
pub mod types;
pub use hid::CollectionItem as CollectionType;
use hid::*;
pub use types::*;
macro_rules! ensure {
($cond:expr, $msg:literal) => {
if (!$cond) {
return Err(ParserError::InvalidData {
offset: 0,
message: $msg.into(),
});
}
};
($cond:expr, $err:expr) => {
if (!$cond) {
return Err($err);
}
};
}
pub(crate) use ensure;
/// Implements `From<Foo> for Bar` to call `From<&Foo> for Bar`
macro_rules! impl_from_without_ref {
($tipo:ty, $to_expr:ident, $to:ty) => {
impl From<$tipo> for $to {
fn from(f: $tipo) -> $to {
$to_expr::from(&f)
}
}
};
}
/// Extract the bit range from the given byte array, converting the
/// result into a [u32].
///
/// The number of bits in the range must be less or equal to 32.
fn extract_bits(bytes: &[u8], bits: &Range<usize>) -> u32 {
let nbits = bits.len();
assert_ne!(nbits, 0);
assert!(nbits <= 32);
let start_index = bits.start / 8;
let end_index = (bits.end - 1) / 8;
let bytes = &bytes[start_index..=end_index];
let value: u64 = Range {
start: 0u64,
end: bytes.len() as u64,
}
//.inspect(|idx| println!("Accessing index {idx}: {:x?}", bytes[*idx as usize]))
.fold(0u64, |acc: u64, idx| {
acc | ((bytes[idx as usize] as u64) << (8 * idx))
});
let base_shift = bits.start % 8;
let mask_shift = 32 - nbits;
let mask = (!0) >> mask_shift;
let value = (value >> base_shift) as u32;
value & mask
}
/// Calculates the two's complement for a value with
/// a given number of of bits.
trait TwosComplement<To> {
/// Returns the two's complement for a value
/// with a given number of bits.
fn twos_comp(self, nbits: usize) -> To;
}
impl TwosComplement<i8> for u8 {
fn twos_comp(self, nbits: usize) -> i8 {
assert!(nbits > 0);
if nbits >= 8 || self & (1 << (nbits - 1)) == 0 {
self as i8
} else {
let s = self as i16;
let min = 1 << nbits;
(-min + s) as i8
}
}
}
impl TwosComplement<i16> for u16 {
fn twos_comp(self, nbits: usize) -> i16 {
assert!(nbits > 0);
if nbits >= 16 || self & (1 << (nbits - 1)) == 0 {
self as i16
} else {
let s = self as i32;
let min = 1 << nbits;
(-min + s) as i16
}
}
}
impl TwosComplement<i32> for u32 {
fn twos_comp(self, nbits: usize) -> i32 {
assert!(nbits > 0);
if nbits >= 32 || self & (1 << (nbits - 1)) == 0 {
self as i32
} else {
let s = self as i64;
let min = 1 << nbits;
(-min + s) as i32
}
}
}
/// A [ReportDescriptor] is the static set of [Items](hid::Item)
/// that define how data from the device should be interpreted.
///
/// A device may have up to three different types of [Reports](Report)
/// (Input, Output, and Feature), all of which are defined in the
/// single report descriptor.
///
/// ```
/// # use hidreport::*;
/// # let bytes: &[u8] = &[0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x05, 0x01, 0x09, 0x02, 0xa1, 0x02, 0x85, 0x1a, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x05, 0x95, 0x05, 0x75, 0x01, 0x15, 0x00, 0x25, 0x01, 0x81, 0x02, 0x75, 0x03, 0x95, 0x01, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x95, 0x02, 0x75, 0x10, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x81, 0x06, 0xa1, 0x02, 0x85, 0x12, 0x09, 0x48, 0x95, 0x01, 0x75, 0x02, 0x15, 0x00, 0x25, 0x01, 0x35, 0x01, 0x45, 0x0c, 0xb1, 0x02, 0x85, 0x1a, 0x09, 0x38, 0x35, 0x00, 0x45, 0x00, 0x95, 0x01, 0x75, 0x10, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x81, 0x06, 0xc0, 0xa1, 0x02, 0x85, 0x12, 0x09, 0x48, 0x75, 0x02, 0x15, 0x00, 0x25, 0x01, 0x35, 0x01, 0x45, 0x0c, 0xb1, 0x02, 0x35, 0x00, 0x45, 0x00, 0x75, 0x04, 0xb1, 0x01, 0x85, 0x1a, 0x05, 0x0c, 0x95, 0x01, 0x75, 0x10, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x0a, 0x38, 0x02, 0x81, 0x06, 0xc0, 0xc0, 0xc0, 0xc0, 0x05, 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x05, 0x01, 0x09, 0x02, 0xa1, 0x02, 0x85, 0x1f, 0x05, 0x0c, 0x0a, 0x38, 0x02, 0x95, 0x01, 0x75, 0x10, 0x16, 0x01, 0x80, 0x26, 0xff, 0x7f, 0x81, 0x06, 0x85, 0x17, 0x06, 0x00, 0xff, 0x0a, 0x06, 0xff, 0x0a, 0x0f, 0xff, 0x15, 0x00, 0x25, 0x01, 0x35, 0x01, 0x45, 0x0c, 0x95, 0x02, 0x75, 0x02, 0xb1, 0x02, 0x0a, 0x04, 0xff, 0x35, 0x00, 0x45, 0x00, 0x95, 0x01, 0x75, 0x01, 0xb1, 0x02, 0x75, 0x03, 0xb1, 0x01, 0xc0, 0xc0];
/// # fn read_from_device() -> Vec<u8> {
/// # vec![0x1a, 0x00, 0xff, 0xff, 0xfe, 0xff, 00, 00, 00, 0x00]
/// # }
/// #
/// let rdesc: ReportDescriptor = ReportDescriptor::try_from(bytes).unwrap();
/// for r in rdesc.input_reports() {
/// println!("Input Report with report ID: {:?}", r.report_id());
/// }
/// let input_report_bytes = read_from_device();
/// let report = rdesc.find_input_report(&input_report_bytes).unwrap();
/// println!("This is an input report for report ID: {:?}", report.report_id());
/// let field = report.fields().first().unwrap();
/// match field {
/// Field::Variable(var) => println!("Field {:?} is of value {}", field, u32::from(var.extract(&input_report_bytes).unwrap())),
/// Field::Array(arr) => println!("Field {:?} has values {:?}", field, arr.extract(&input_report_bytes).unwrap().iter().map(u32::from)),
/// Field::Constant(_) => println!("Field {:?} is <padding data>", field),
/// }
/// ```
///
#[derive(Debug, Default)]
pub struct ReportDescriptor {
input_reports: Vec<RDescReport>,
output_reports: Vec<RDescReport>,
feature_reports: Vec<RDescReport>,
}
impl<'a> ReportDescriptor {
/// Returns the set of input reports or the empty
/// slice if none exist.
/// ```
/// # use hidreport::*;
/// # fn func(rdesc: &ReportDescriptor) {
/// let reports = rdesc.input_reports();
/// for report in reports {
/// println!("Report ID: {:?}", report.report_id());
/// }
/// # }
/// ```
pub fn input_reports(&self) -> &[impl Report] {
&self.input_reports
}
/// Returns the set of output reports or the empty
/// slice if none exist.
/// ```
/// # use hidreport::*;
/// # fn func(rdesc: &ReportDescriptor) {
/// let reports = rdesc.output_reports();
/// for report in reports {
/// println!("Report ID: {:?}", report.report_id());
/// }
/// # }
/// ```
pub fn output_reports(&self) -> &[impl Report] {
&self.output_reports
}
/// Returns the set of feature reports or the empty
/// slice if none exist.
/// ```
/// # use hidreport::*;
/// # fn func(rdesc: &ReportDescriptor) {
/// let reports = rdesc.feature_reports();
/// for report in reports {
/// println!("Report ID: {:?}", report.report_id());
/// }
/// # }
/// ```
pub fn feature_reports(&self) -> &[impl Report] {
&self.feature_reports
}
fn find_report(&'a self, list: &'a [RDescReport], prefix: u8) -> Option<&'a impl Report> {
let first = list.first()?;
let rid = Some(ReportId(prefix));
// Do we have report IDs? If not, the first report is what we want.
match first.report_id() {
None => Some(first),
Some(_) => list.iter().find(|r| r.report_id() == &rid),
}
}
/// Find the input report that matches this byte sequence.
///
/// ```
/// # use hidreport::*;
/// # fn func(bytes: &[u8], rdesc: &ReportDescriptor) {
/// // bytes was read from the device (or some other source)
/// let report = rdesc.find_input_report(bytes).unwrap();
/// for field in report.fields() {
/// // ...
/// }
/// # }
/// ```
///
/// ReportDescriptors with multiple reports require a report
/// to have a single byte prefix specifying the [ReportId].
pub fn find_input_report(&self, bytes: &[u8]) -> Option<&impl Report> {
self.find_report(&self.input_reports, bytes[0])
}
/// Find the output report that matches this byte sequence.
///
/// ```
/// # use hidreport::*;
/// # fn func(bytes: &[u8], rdesc: &ReportDescriptor) {
/// // bytes was read from the device (or some other source)
/// let report = rdesc.find_output_report(bytes).unwrap();
/// for field in report.fields() {
/// // ...
/// }
/// # }
/// ```
///
/// ReportDescriptors with multiple reports require a report
/// to have a single byte prefix specifying the [ReportId].
pub fn find_output_report(&self, bytes: &[u8]) -> Option<&impl Report> {
self.find_report(&self.output_reports, bytes[0])
}
/// Find the feature report that matches this byte sequence.
///
/// ```
/// # use hidreport::*;
/// # fn func(bytes: &[u8], rdesc: &ReportDescriptor) {
/// // bytes was read from the device (or some other source)
/// let report = rdesc.find_feature_report(bytes).unwrap();
/// for field in report.fields() {
/// // ...
/// }
/// # }
/// ```
///
/// ReportDescriptors with multiple reports require a report
/// to have a single byte prefix specifying the [ReportId].
pub fn find_feature_report(&self, bytes: &[u8]) -> Option<&impl Report> {
self.find_report(&self.feature_reports, bytes[0])
}
}
impl TryFrom<&[u8]> for ReportDescriptor {
type Error = ParserError;
/// Try to parse the given byte array as a report descriptor.
fn try_from(bytes: &[u8]) -> Result<ReportDescriptor> {
parse_report_descriptor(bytes)
}
}
impl TryFrom<&Vec<u8>> for ReportDescriptor {
type Error = ParserError;
/// Try to parse the given byte array as a report descriptor.
fn try_from(bytes: &Vec<u8>) -> Result<ReportDescriptor> {
parse_report_descriptor(bytes)
}
}
#[derive(Copy, Clone, Debug)]
enum Direction {
Input,
Output,
Feature,
}
/// A HID Input, Output or Feature Report.
///
/// Where a report contains the [Report::report_id] the first
/// byte of the report is always that Report ID, followed
/// by the data in the sequence announced in the HID [ReportDescriptor].
///
/// Use [`size_in_bits()`][Report::size_in_bits] or
/// [`size_in_bytes()`](Report::size_in_bytes) to
/// get the length of this report.
///
/// Note that each of Input, Output and Feature Reports
/// have their own enumeration of Report IDs, i.e. an Input Report
/// with a Report ID of e.g. 1 may have a different size and/or [Field]s
/// to an Output Report with a Report ID of 1.
///
/// The Report ID has no meaning other than to distinguish
/// different reports. See Section 6.2.2.7 for details.
pub trait Report {
/// Returns the HID Report ID for this report, if any.
fn report_id(&self) -> &Option<ReportId>;
/// Returns the parsed HID Fields ID for this report. A caller should
/// iterate through these fields to find the ones it is interested
/// in and use the [Field::bits] to extract the data from future
/// reports.
fn fields(&self) -> &[Field];
/// The size in bits for this report.
fn size_in_bits(&self) -> usize;
/// The size in bytes for this object.
///
/// Where [`size_in_bits()`](Report::size_in_bits) is
/// not a multiple of 8, the [`size_in_bytes()`](Report::size_in_bytes) rounds up
/// fit all bits.
fn size_in_bytes(&self) -> usize {
self.size_in_bits().div_ceil(8)
}
}
/// A HID Input, Output or Feature Report.
///
/// Where a report contains the [Report::report_id] the first
/// byte of the report is always that Report ID, followed
/// by the data in the sequence announced in the HID [ReportDescriptor].
///
/// Note that each of Input, Output and Feature Reports
/// have their own enumeration of Report IDs, i.e. an Input Report
/// with a Report ID of e.g. 1 may have a different size and/or [Field]s
/// to an Output Report with a Report ID of 1.
///
/// The Report ID has no meaning other than to distinguish
/// different reports. See Section 6.2.2.7 for details.
#[derive(Debug)]
struct RDescReport {
/// The report ID, if any
id: Option<ReportId>,
/// The size of this report in bits
size: usize,
/// The fields present in this report
fields: Vec<Field>,
}
impl Report for RDescReport {
fn report_id(&self) -> &Option<ReportId> {
&self.id
}
fn fields(&self) -> &[Field] {
&self.fields
}
/// The size of this HID report on the wire, in bits
fn size_in_bits(&self) -> usize {
self.size
}
}
/// The usage of a [Field] defines the interpretation of a
/// data value. See the `hut` crate for a list of known Usages.
///
/// A Usage comprises of a 16 bit [UsagePage] and a 16 bit [UsageId].
///
/// ```
/// # use hidreport::*;
/// let up = UsagePage::from(0x01); // Generic Desktop
/// let uid = UsageId::from(0x02); // Mouse
/// let usage = Usage::from_page_and_id(up, uid);
/// ```
/// For known named usages see the `hut` crate.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Usage {
pub usage_page: UsagePage,
pub usage_id: UsageId,
}
impl Usage {
/// Create a [Usage] from a [UsagePage] and a [UsageId].
pub fn from_page_and_id(usage_page: UsagePage, usage_id: UsageId) -> Usage {
Usage {
usage_page,
usage_id,
}
}
}
impl From<u32> for Usage {
fn from(u: u32) -> Usage {
Usage {
usage_page: UsagePage::from((u >> 16) as u16),
usage_id: UsageId::from((u & 0xffff) as u16),
}
}
}
impl From<&Usage> for u32 {
fn from(u: &Usage) -> u32 {
((u16::from(u.usage_page) as u32) << 16) | u16::from(u.usage_id) as u32
}
}
impl_from_without_ref!(Usage, u32, u32);
impl From<&Usage> for UsageMinimum {
fn from(u: &Usage) -> UsageMinimum {
UsageMinimum(u32::from(u))
}
}
impl_from_without_ref!(Usage, UsageMinimum, UsageMinimum);
impl From<&Usage> for UsageMaximum {
fn from(u: &Usage) -> UsageMaximum {
UsageMaximum(u32::from(u))
}
}
impl_from_without_ref!(Usage, UsageMaximum, UsageMaximum);
#[cfg(feature = "hut")]
impl From<hut::Usage> for Usage {
fn from(usage: hut::Usage) -> Usage {
let usage_page = UsagePage::from(usage.usage_page_value());
let usage_id = UsageId::from(usage.usage_id_value());
Usage::from_page_and_id(usage_page, usage_id)
}
}
/// A unique (within this report descriptor) identifier for a [Field].
///
/// The [FieldId] does not exist in the actual ReportDescriptor, it is
/// a unique ID assigned by this crate to later identify a given field
/// for data extraction and/or further parsing.
#[derive(Clone, Copy, Debug, PartialEq, Hash, PartialOrd)]
pub struct FieldId(u32);
impl From<&FieldId> for u32 {
fn from(f: &FieldId) -> u32 {
f.0
}
}
impl_from_without_ref!(FieldId, u32, u32);
/// A wrapper around the value of a [Field] inside
/// a HID Report's byte array. This value may
/// be signed, depending on the [Field].
///
/// ```
/// # use hidreport::VariableField;
/// # fn read_from_device() -> Vec<u8> {
/// # vec![0x1a, 0x00, 0xff, 0xff, 0xfe, 0xff, 00, 00, 00, 0x00]
/// # }
/// #
/// # fn func(field: &VariableField, bytes: &[u8]) {
/// let bytes: Vec<u8> = read_from_device();
/// let val = field.extract(bytes.as_slice()).unwrap();
/// if val.is_signed() {
/// let unsigned: u32 = val.into();
/// } else {
/// let signed: i32 = val.into();
/// }
/// # }
/// ```
///
/// The value is always of size [u32] or [i32], regardless of the
/// number of bits in the HID Report. Cast to [u16], [u8], etc. as needed.
#[derive(Clone, Copy, Debug)]
pub struct FieldValue {
is_signed: bool,
value: u32,
}
impl FieldValue {
/// Returns `true` if the contained value is signed, `false` otherwise.
pub fn is_signed(&self) -> bool {
self.is_signed
}
}
impl From<&FieldValue> for u32 {
fn from(v: &FieldValue) -> u32 {
v.value
}
}
impl_from_without_ref!(FieldValue, u32, u32);
impl From<&FieldValue> for u16 {
fn from(v: &FieldValue) -> u16 {
v.value as u16
}
}
impl_from_without_ref!(FieldValue, u16, u16);
impl From<&FieldValue> for u8 {
fn from(v: &FieldValue) -> u8 {
v.value as u8
}
}
impl_from_without_ref!(FieldValue, u8, u8);
impl From<&FieldValue> for i32 {
fn from(v: &FieldValue) -> i32 {
v.value as i32
}
}
impl_from_without_ref!(FieldValue, i32, i32);
impl From<&FieldValue> for i16 {
fn from(v: &FieldValue) -> i16 {
v.value as i16
}
}
impl_from_without_ref!(FieldValue, i16, i16);
impl From<&FieldValue> for i8 {
fn from(v: &FieldValue) -> i8 {
v.value as i8
}
}
impl_from_without_ref!(FieldValue, i8, i8);
/// A single field inside a [Report].
///
/// Fields may be [Variable](Field::Variable) and represent a
/// single element of data or they may be
/// a [Array](Field::Array) that represent
/// multiple elements.
///
/// Fields of type [Constant](Field::Constant) should be ignored by
/// the caller.
///
/// Given a set of bytes that is a HID Report use [VariableField::extract()] or
/// [ArrayField::extract()] to extract the value for this field, for example:
/// ```
/// # use hidreport::VariableField;
/// # fn func(field: &VariableField, bytes: &[u8]) {
/// let val = field.extract(bytes).unwrap();
/// if val.is_signed() {
/// let unsigned: u32 = val.into();
/// } else {
/// let signed: i32 = val.into();
/// }
/// # }
/// ```
#[derive(Clone, Debug)]
pub enum Field {
/// A single element of data
Variable(VariableField),
/// A set of multiple fields
Array(ArrayField),
/// Padding
Constant(ConstantField),
}
impl Field {
/// Return the unique (within this report descriptor) ID for this field.
///
/// The [FieldId] does not exist in the actual ReportDescriptor, it is
/// a unique ID assigned by this crate to later identify a given field
/// for data extraction and/or further parsing.
pub fn id(&self) -> FieldId {
match self {
Field::Variable(f) => f.id,
Field::Array(f) => f.id,
Field::Constant(f) => f.id,
}
}
/// Returns the bit range that make up this field.
pub fn bits(&self) -> &Range<usize> {
match self {
Field::Variable(f) => &f.bits,
Field::Array(f) => &f.bits,
Field::Constant(f) => &f.bits,
}
}
/// Returns the Report ID this field belongs to, if any.
fn report_id(&self) -> &Option<ReportId> {
match self {
Field::Variable(f) => &f.report_id,
Field::Array(f) => &f.report_id,
Field::Constant(f) => &f.report_id,
}
}
fn update_bit_offset(&mut self, offset: usize) {
let r = self.bits();
let r = (offset + r.start)..(offset + r.end);
match self {
Field::Variable(f) => f.bits = r,
Field::Array(f) => f.bits = r,
Field::Constant(f) => f.bits = r,
};
}
/// The length of the field in bits
fn len(&self) -> usize {
self.bits().len()
}
/// Return the set of collections this [Field] belongs to
/// or the empty slice.
pub fn collections(&self) -> &[Collection] {
match self {
Field::Variable(f) => &f.collections,
Field::Array(f) => &f.collections,
Field::Constant(..) => &[],
}
}
}
/// Field attributes, see Main Data Item in Section 6.2.5.
///
/// These properties come in pairs (bit set or unset in the HID report descriptor item),
/// for readability in the caller, a function is provided for each state.
pub trait FieldAttributes {
/// True if the data is relative compared to a previous report
///
/// Mutually exclusive with [FieldAttributes::is_absolute].
fn is_relative(&self) -> bool;
/// True if the data is absolute
///
/// Mutually exclusive with [FieldAttributes::is_relative].
fn is_absolute(&self) -> bool {
!self.is_relative()
}
/// True if the data wraps around at the logical
/// minimum/maximum (e.g. a dial that can spin at 360 degrees).
///
/// Mutually exclusive with [FieldAttributes::does_not_wrap].
fn wraps(&self) -> bool;
/// True if the data does not wrap at the logical
/// minimum/maximum.
///
/// Mutually exclusive with [FieldAttributes::wraps].
fn does_not_wrap(&self) -> bool {
!self.wraps()
}
/// True if the data was pre-processed on the device
/// and the logical range is not linear.
///
/// Mutually exclusive with [FieldAttributes::is_linear].
fn is_nonlinear(&self) -> bool;
/// True if the data was not pre-processed on the device
/// and the logical range is linear.
///
/// Mutually exclusive with [FieldAttributes::is_nonlinear].
fn is_linear(&self) -> bool {
!self.is_nonlinear()
}
/// True if the control does not have a preferred state it
/// returns to when the user stops interacting (e.g. a joystick
/// may return to a neutral position)
///
/// Mutually exclusive with [FieldAttributes::has_preferred_state].
fn has_no_preferred_state(&self) -> bool;
/// True if the control has a preferred state it
/// returns to when the user stops interacting (e.g. a joystick
/// may return to a neutral position)
///
/// Mutually exclusive with [FieldAttributes::has_no_preferred_state].
fn has_preferred_state(&self) -> bool {
!self.has_no_preferred_state()
}
/// True if the control has a null state where it does not send
/// data (e.g. a joystick in neutral state)
///
/// Mutually exclusive with [FieldAttributes::has_no_null_state].
fn has_null_state(&self) -> bool;
/// True if the control does not have a null state where it does not send
/// data.
///
/// Mutually exclusive with [FieldAttributes::has_null_state].
fn has_no_null_state(&self) -> bool {
!self.has_null_state()
}
/// True if the control emits a fixed size stream of bytes.
///
/// Mutually exclusive with [FieldAttributes::is_bitfield].
fn is_buffered_bytes(&self) -> bool;
/// True if the control is a single bit field (value).
///
/// Mutually exclusive with [FieldAttributes::is_buffered_bytes].
fn is_bitfield(&self) -> bool {
!self.is_buffered_bytes()
}
/// Indiciates whether control should be changed by the host.
/// Volatile fields can change without host interactions.
///
/// This attribute is only available on a [Field] in an OutputReport
/// or an FeatureReport. It is always [None] for fields in an InputReport.
fn is_volatile(&self) -> Option<bool>;
/// Indiciates whether control should be changed by the host.
/// Volatile fields can change without host interactions.
///
/// This attribute is only available on a [Field] in an OutputReport
/// or an FeatureReport. It is always [None] for fields in an InputReport.
fn is_nonvolatile(&self) -> Option<bool> {
self.is_volatile().map(|v| !v)
}
}
/// A [VariableField] represents a single physical control.
#[derive(Clone, Debug)]
pub struct VariableField {
id: FieldId,
report_id: Option<ReportId>,
pub bits: Range<usize>,
pub usage: Usage,
pub logical_minimum: LogicalMinimum,
pub logical_maximum: LogicalMaximum,
pub physical_minimum: Option<PhysicalMinimum>,
pub physical_maximum: Option<PhysicalMaximum>,
pub unit: Option<Unit>,
pub unit_exponent: Option<UnitExponent>,
pub collections: Vec<Collection>,
is_relative: bool,
wraps: bool,
is_nonlinear: bool,
has_no_preferred_state: bool,
has_null_state: bool,
is_buffered_bytes: bool,
is_volatile: Option<bool>,
}
impl VariableField {
/// Returns true if this field contains signed values,
/// i.e. the LogicalMinimum is less than zero.
pub fn is_signed(&self) -> bool {
self.logical_minimum < LogicalMinimum(0)
}
/// Extract this field's value as [FieldValue] from a report's bytes.
/// The value is extracted as its correct bit size, the returned [FieldValue]
/// can then be casted in to a [u32], [i32], etc. via the [From] trait.
/// ```
/// # use hidreport::VariableField;
/// # fn func(field: &VariableField, bytes: &[u8]) {
/// let val = field.extract(bytes).unwrap();
/// if val.is_signed() {
/// let unsigned: u32 = val.into();
/// } else {
/// let signed: i32 = val.into();
/// }
/// # }
/// ```
pub fn extract(&self, bytes: &[u8]) -> Result<FieldValue> {
if let Some(report_id) = self.report_id {
if ReportId(bytes[0]) != report_id {
return Err(ParserError::MismatchingReportId);
}
}
let v = extract_bits(bytes, &self.bits);
let v = if self.is_signed() {
v.twos_comp(self.bits.len()) as u32
} else {
v
};
Ok(FieldValue {
is_signed: self.is_signed(),
value: v,
})
}
}
impl FieldAttributes for VariableField {
fn is_relative(&self) -> bool {
self.is_relative
}
fn wraps(&self) -> bool {
self.wraps
}
fn is_nonlinear(&self) -> bool {
self.is_nonlinear
}
fn has_no_preferred_state(&self) -> bool {
self.has_no_preferred_state
}
fn has_null_state(&self) -> bool {
self.has_null_state
}
fn is_buffered_bytes(&self) -> bool {
self.is_buffered_bytes
}
fn is_volatile(&self) -> Option<bool> {
self.is_volatile
}
}
/// Wrapper around the commonly used [UsageMinimum] and [UsageMaximum].
#[derive(Debug)]
pub struct UsageRange {
usage_page: UsagePage,
minimum: UsageMinimum,
maximum: UsageMaximum,
}
impl UsageRange {
/// The [UsageMinimum]. Note that in reports and report descriptors
/// the Usage Minimum may or may not include the Usage Page. The
/// minimum returned here always includes the [UsagePage].
pub fn minimum(&self) -> UsageMinimum {
self.minimum
}
/// The [UsageMaximum]. Note that in reports and report descriptors
/// the Usage Maximum may or may not include the Usage Page. The
/// maximum returned here always includes the [UsagePage].
pub fn maximum(&self) -> UsageMaximum {
self.maximum
}
/// If the given usage falls within this usage range (i.e. it is of the
/// same [UsagePage] and it within the inclusive [UsageMinimum]/[UsageMaximum])
/// return the provided usage as [Option].
pub fn lookup_usage<'a>(&self, usage: &'a Usage) -> Option<&'a Usage> {
if usage.usage_page == self.usage_page
&& usage.usage_id >= self.minimum.usage_id()
&& usage.usage_id <= self.maximum.usage_id()
{
Some(usage)
} else {
None
}
}
/// Look up the given [UsageId] and return the corresponding
/// [Usage], if any. The [UsageId] is assumed to be in the same
/// [UsagePage] as this range, use [lookup_usage()][Self::lookup_usage] if you need
/// a check for the [UsagePage] as well.
pub fn lookup_id(&self, id: UsageId) -> Option<Usage> {
if id >= self.minimum.usage_id() && id <= self.maximum.usage_id() {
Some(Usage::from_page_and_id(self.usage_page, id))
} else {
None
}
}
}
/// An [ArrayField] represents a group of physical controls,
/// see section 6.2.2.5.
///
/// > An array provides an alternate means for
/// > describing the data returned from a group of
/// > buttons. Arrays are more efficient, if less flexible
/// > than variable items. Rather than returning a single
/// > bit for each button in the group, an array returns an
/// > index in each field that corresponds to the pressed
/// > button
#[derive(Clone, Debug)]
pub struct ArrayField {
id: FieldId,
report_id: Option<ReportId>,
pub bits: Range<usize>,
usages: Vec<Usage>,
is_usage_range: bool,
pub report_count: ReportCount,
pub logical_minimum: LogicalMinimum,
pub logical_maximum: LogicalMaximum,
pub physical_minimum: Option<PhysicalMinimum>,
pub physical_maximum: Option<PhysicalMaximum>,
pub unit: Option<Unit>,
pub unit_exponent: Option<UnitExponent>,
pub collections: Vec<Collection>,
is_relative: bool,
wraps: bool,
is_nonlinear: bool,
has_no_preferred_state: bool,
has_null_state: bool,
is_buffered_bytes: bool,
is_volatile: Option<bool>,
}
impl ArrayField {
/// Returns the set of usages for this field. This is the
/// inclusive range of [UsageMinimum]`..=`[UsageMaximum]
/// as defined for this field.
///
/// In most cases it's better to use [usage_range()](Self::usage_range),
/// most [ArrayField]s seen in the wild use a usage minimum and maximum
/// rather than individual usages. Use [is_usage_range()](Self::is_usage_range)
/// to be sure.
pub fn usages(&self) -> &[Usage] {
&self.usages
}
/// Returns the [UsageRange] for this field, if any.
///
/// If the report descriptor did not specify the usages via
/// [UsageMinimum] and [UsageMaximum], this function returns [None].
pub fn usage_range(&self) -> Option<UsageRange> {
if self.is_usage_range {
let min = self.usages.first().unwrap();
let max = self.usages.last().unwrap();
Some(UsageRange {
usage_page: min.usage_page,
minimum: UsageMinimum::from(min),
maximum: UsageMaximum::from(max),
})
} else {
None
}
}
/// Returns true if the usages were defined via
/// a [UsageMinimum]`..=`[UsageMaximum] in the report
/// descriptor, false if the usages were given individually.
///
/// For example, the following pseudo-report descriptor
/// defines an ArrayField with a usage range first,
/// then one with individual usages.
/// ```text
/// UsageMinimum (0)
/// UsageMaximum (255)
/// Input (Array)
/// Usage (Tip Switch)
/// Usage (Barrel Switch)
/// Usage (Secondary Barrel Switch)
/// Input (Array)
/// ```
pub fn is_usage_range(&self) -> bool {
self.is_usage_range
}
/// Returns true if this field contains signed values,.
/// i.e. the LogicalMinimum is less than zero.
pub fn is_signed(&self) -> bool {
self.logical_minimum < LogicalMinimum(0)
}
/// Extract this field's value as [FieldValue] from a report's bytes.
/// The value is extracted as its correct bit size, the returned [FieldValue]
/// can then be casted in to a [u32], [i32], etc. via the [From] trait.
///
/// ```
/// # use hidreport::ArrayField;
/// # fn func(field: &ArrayField, bytes: &[u8]) {
/// if field.is_signed() {
/// println!("Signed values: {:?}", field
/// .extract(bytes)
/// .unwrap()
/// .iter()
/// .map(i32::from)
/// .collect::<Vec<i32>>());
/// } else {
/// println!("Unsigned values: {:?}", field
/// .extract(bytes)
/// .unwrap()
/// .iter()
/// .map(u32::from)
/// .collect::<Vec<u32>>());
/// }
///
/// # }
/// ```
pub fn extract(&self, bytes: &[u8]) -> Result<Vec<FieldValue>> {
if let Some(report_id) = self.report_id {
if ReportId(bytes[0]) != report_id {
return Err(ParserError::MismatchingReportId);
}
}
let count = usize::from(self.report_count);
let values: Result<Vec<FieldValue>> =
(0..count).map(|idx| self.extract_one(bytes, idx)).collect();
values
}
/// Extract a single value from this array. See [ArrayField::extract].
///
/// The index must be less than [Self::report_count].
pub fn extract_one(&self, bytes: &[u8], idx: usize) -> Result<FieldValue> {
if idx >= usize::from(self.report_count) {
return Err(ParserError::OutOfBounds);
}
if let Some(report_id) = self.report_id {
if ReportId(bytes[0]) != report_id {
return Err(ParserError::MismatchingReportId);
}
}
let count = usize::from(self.report_count);
let bits_per_report = self.bits.len() / count;
let offset = self.bits.start + bits_per_report * idx;
let bits = offset..offset + bits_per_report;
let v = extract_bits(bytes, &bits);
let v = if self.is_signed() {
v.twos_comp(self.bits.len()) as u32
} else {
v
};
Ok(FieldValue {
is_signed: self.is_signed(),
value: v,
})
}
}
impl FieldAttributes for ArrayField {
fn is_relative(&self) -> bool {
self.is_relative
}
fn wraps(&self) -> bool {
self.wraps
}
fn is_nonlinear(&self) -> bool {
self.is_nonlinear
}
fn has_no_preferred_state(&self) -> bool {
self.has_no_preferred_state
}
fn has_null_state(&self) -> bool {
self.has_null_state
}
fn is_buffered_bytes(&self) -> bool {
self.is_buffered_bytes
}
fn is_volatile(&self) -> Option<bool> {
self.is_volatile
}
}
/// A [ConstantField] is one that represents a [hid::MainItem]
/// with Constant data, see Section 6.2.2.4.
///
/// > Constant indicates the item is a static read-only field in a
/// > report and cannot be modified (written) by the
/// > host.
///
/// Data in a [ConstantField] should be ignored by the caller, it
/// is merely used as padding, usually to align the subsequent
/// value on a byte boundary.
#[derive(Clone, Debug)]
pub struct ConstantField {
id: FieldId,
report_id: Option<ReportId>,
pub bits: Range<usize>,
usages: Vec<Usage>,
}
impl ConstantField {
pub fn usages(&self) -> &[Usage] {
&self.usages
}
}
/// A unique (within this report descriptor) identifier for a collection.
///
/// A device may have multiple collections that are otherwise identical
/// (in particular logical collections), the collection ID serves
/// to identify whether two fields are part of the same collection.
#[derive(Clone, Debug, PartialEq, Hash, PartialOrd)]
pub struct CollectionId(u32);
/// Collections group [Fields](Field) together into logical or physical
/// groups.
///
/// For example, a set of buttons and x/y axes may be grouped
/// together to represent a Mouse device.
/// Each [Field] may belong to a number of collections.
///
/// ```
/// # use hidreport::*;
/// # fn func(field: &VariableField) {
/// let collection = field.collections.first().unwrap();
/// match collection.collection_type() {
/// CollectionType::Physical => println!("This field is part of a physical collection"),
/// _ => {},
/// }
/// # }
/// ```
///
#[derive(Clone, Debug)]
pub struct Collection {
id: CollectionId,
collection_type: CollectionType,
usages: Vec<Usage>,
}
impl Collection {
/// Returns the unique ID for this collection
pub fn id(&self) -> &CollectionId {
&self.id
}
/// Return the type of this collection (e.g. physical, logical, ...)
pub fn collection_type(&self) -> CollectionType {
self.collection_type
}
/// Returns the usages assigned to this collection
pub fn usages(&self) -> &[Usage] {
&self.usages
}
}
impl PartialEq for Collection {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Collection {}
#[cfg(feature = "std")]
impl Hash for Collection {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
#[derive(Error, Debug)]
pub enum ParserError {
#[error("Invalid data at offset {offset}: {message}")]
InvalidData { offset: usize, message: String },
#[error("Parsing would lead to out-of-bounds")]
OutOfBounds,
#[error("Mismatching Report ID")]
MismatchingReportId,
}
type Result<T> = core::result::Result<T, ParserError>;
#[derive(Clone, Copy, Debug, Default)]
struct Globals {
usage_page: Option<UsagePage>,
logical_minimum: Option<LogicalMinimum>,
logical_maximum: Option<LogicalMaximum>,
physical_minimum: Option<PhysicalMinimum>,
physical_maximum: Option<PhysicalMaximum>,
unit_exponent: Option<UnitExponent>,
unit: Option<Unit>,
report_size: Option<ReportSize>,
report_id: Option<ReportId>,
report_count: Option<ReportCount>,
}
/// Special struct for the [Locals] because the usage_page
/// is optional for those, unlike our [Usage] struct which is
/// the finalized one.
#[derive(Clone, Copy, Debug)]
struct LocalUsage {
usage_page: Option<UsagePage>,
usage_id: UsageId,
}
#[derive(Clone, Debug, Default)]
struct Locals {
usage: Vec<LocalUsage>,
// FIXME: needs the same LocalUsage treatment
usage_minimum: Option<UsageMinimum>,
usage_maximum: Option<UsageMaximum>,
designator_index: Option<DesignatorIndex>,
designator_minimum: Option<DesignatorMinimum>,
designator_maximum: Option<DesignatorMaximum>,
string_index: Option<StringIndex>,
string_minimum: Option<StringMinimum>,
string_maximum: Option<StringMaximum>,
delimiter: Option<Delimiter>,
}
#[derive(Debug)]
struct Stack {
globals: Vec<Globals>,
locals: Vec<Locals>,
pub collections: Vec<Collection>,
}
impl Stack {
fn new() -> Self {
Stack {
globals: vec![Globals::default()],
locals: vec![Locals::default()],
collections: vec![],
}
}
fn push(&mut self) {
let current = self.globals.last().unwrap();
self.globals.push(*current);
// FIXME: this clones the Usage vector which is likely to mess us up, I don't
// think they transfer across push/pop
let current = self.locals.last().unwrap().clone();
self.locals.push(current);
}
fn pop(&mut self) -> Result<()> {
ensure!(
!self.globals.is_empty() && !self.locals.is_empty(),
ParserError::InvalidData {
offset: 0,
message: "Too many Pops".into(),
}
);
self.globals.pop();
self.locals.pop();
ensure!(
!self.globals.is_empty() && !self.locals.is_empty(),
ParserError::InvalidData {
offset: 0,
message: "Too many Pops, not enough Pushes".into(),
}
);
Ok(())
}
fn reset_locals(&mut self) {
self.locals.pop();
self.locals.push(Locals::default());
}
fn globals(&mut self) -> &mut Globals {
self.globals.last_mut().unwrap()
}
fn locals(&mut self) -> &mut Locals {
self.locals.last_mut().unwrap()
}
// Should be globals and globals_mut but i'd have to
// update the update_stack macro for that.
fn globals_const(&self) -> &Globals {
self.globals.last().unwrap()
}
fn locals_const(&self) -> &Locals {
self.locals.last().unwrap()
}
}
#[derive(PartialEq)]
enum UsageRangeType {
IndividualUsages,
UsageMinimumMaximum,
}
fn compile_usages(globals: &Globals, locals: &Locals) -> Result<(UsageRangeType, Vec<Usage>)> {
// Prefer UsageMinimum/Maximum over Usage because the latter may be set from an earlier call
match locals.usage_minimum {
Some(_) => {
let Some(min) = locals.usage_minimum else {
return Err(ParserError::InvalidData {
offset: 0,
message: "Missing UsageMinimum in locals".into(),
});
};
let Some(max) = locals.usage_maximum else {
return Err(ParserError::InvalidData {
offset: 0,
message: "Missing UsageMaximum in locals".into(),
});
};
let Some(usage_page) = globals.usage_page else {
return Err(ParserError::InvalidData {
offset: 0,
message: "Missing UsagePage in globals".into(),
});
};
let min: u32 = min.into();
let max: u32 = max.into();
let usages = (min..=max)
.map(|u| Usage {
usage_page: UsagePage(usage_page.into()),
usage_id: UsageId(u as u16),
})
.collect();
Ok((UsageRangeType::UsageMinimumMaximum, usages))
}
None => {
let usages = locals
.usage
.iter()
.map(|usage| match usage {
// local item's Usage had a Usage Page included
LocalUsage {
usage_page: Some(up),
usage_id,
} => Usage {
usage_page: *up,
usage_id: *usage_id,
},
// Usage Page comes from the global item
LocalUsage {
usage_page: None,
usage_id,
} => {
let usage_page = globals.usage_page.expect("Missing UsagePage in globals");
Usage {
usage_page,
usage_id: *usage_id,
}
}
})
.collect();
Ok((UsageRangeType::IndividualUsages, usages))
}
}
}
fn handle_main_item(item: &MainItem, stack: &mut Stack, base_id: u32) -> Result<Vec<Field>> {
let globals = stack.globals_const();
let locals = stack.locals_const();
let report_id = globals.report_id;
let (
is_constant,
is_variable,
is_relative,
wraps,
is_nonlinear,
has_no_preferred_state,
has_null_state,
is_buffered_bytes,
is_volatile,
) = match item {
MainItem::Input(i) => (
i.is_constant(),
i.is_variable(),
i.is_relative(),
i.wraps(),
i.is_nonlinear(),
i.has_no_preferred_state(),
i.has_null_state(),
i.is_buffered_bytes(),
None,
),
MainItem::Output(i) => (
i.is_constant(),
i.is_variable(),
i.is_relative(),
i.wraps(),
i.is_nonlinear(),
i.has_no_preferred_state(),
i.has_null_state(),
i.is_buffered_bytes(),
Some(i.is_volatile()),
),
MainItem::Feature(i) => (
i.is_constant(),
i.is_variable(),
i.is_relative(),
i.wraps(),
i.is_nonlinear(),
i.has_no_preferred_state(),
i.has_null_state(),
i.is_buffered_bytes(),
Some(i.is_volatile()),
),
_ => panic!("Invalid item for handle_main_item()"),
};
let bit_offset = 0;
// We have HID report descriptors in the wild that do not set a report size/count/whatever.
// Since the most important implementations so far have been in C-like languages, they
// will likely default to zero for unset values (unlike our None).
// Let's do this here to be as compatible as possible.
let report_size = globals.report_size.unwrap_or(ReportSize(0));
let report_count = globals.report_count.unwrap_or(ReportCount(0));
if report_count == ReportCount(0) || report_size == ReportSize(0) {
return Ok(vec![]);
}
if is_constant {
let nbits = usize::from(report_size) * usize::from(report_count);
let bits = bit_offset..(bit_offset + nbits);
let field = ConstantField {
id: FieldId(base_id + bit_offset as u32),
bits,
report_id,
usages: vec![],
};
return Ok(vec![Field::Constant(field)]);
}
let logical_minimum = globals.logical_minimum.unwrap_or(LogicalMinimum(0));
let logical_maximum = globals.logical_maximum.unwrap_or(LogicalMaximum(0));
// Some report descriptors are missing either phys min or max, assume zero
// where one of them is not None
let physical_maximum: Option<PhysicalMaximum>;
let physical_minimum: Option<PhysicalMinimum>;
if globals.physical_minimum.is_some() != globals.physical_maximum.is_some() {
physical_maximum = globals.physical_maximum.or(Some(PhysicalMaximum(0)));
physical_minimum = globals.physical_minimum.or(Some(PhysicalMinimum(0)));
} else {
physical_maximum = globals.physical_maximum;
physical_minimum = globals.physical_minimum;
}
let unit = globals.unit;
let unit_exponent = globals.unit_exponent;
let (usage_range_type, usages) = compile_usages(globals, locals)?;
ensure!(!usages.is_empty(), "Missing Usages for main item");
// This may be an empty vec
let collections = stack.collections.clone();
let fields: Vec<Field> = if is_variable {
let mut bit_offset = 0;
Range {
start: 0,
end: usize::from(report_count),
}
.map(|c| {
let nbits = usize::from(report_size);
let bits = bit_offset..(bit_offset + nbits);
bit_offset += nbits;
let usage = usages.get(c).or_else(|| usages.last()).unwrap();
let field = VariableField {
id: FieldId(base_id + bit_offset as u32),
usage: *usage,
bits,
logical_minimum,
logical_maximum,
physical_minimum,
physical_maximum,
unit,
unit_exponent,
collections: collections.clone(),
report_id,
is_relative,
wraps,
is_nonlinear,
has_no_preferred_state,
has_null_state,
is_buffered_bytes,
is_volatile,
};
Field::Variable(field)
})
.collect()
} else {
let bit_offset = 0;
let nbits = usize::from(report_size) * usize::from(report_count);
let bits = bit_offset..(bit_offset + nbits);
let field = ArrayField {
id: FieldId(base_id + bit_offset as u32),
usages,
is_usage_range: (usage_range_type == UsageRangeType::UsageMinimumMaximum),
bits,
logical_minimum,
logical_maximum,
physical_minimum,
physical_maximum,
unit,
unit_exponent,
collections,
report_id,
report_count,
is_relative,
wraps,
is_nonlinear,
has_no_preferred_state,
has_null_state,
is_buffered_bytes,
is_volatile,
};
vec![Field::Array(field)]
};
Ok(fields)
}
macro_rules! update_stack {
($stack:ident, $class:ident, $which:ident, $from:ident) => {
//println!("Updating {} with value {:?}", stringify!($which), &$from);
let state = $stack.$class();
state.$which = Some($from);
};
}
fn parse_report_descriptor(bytes: &[u8]) -> Result<ReportDescriptor> {
ensure!(!bytes.is_empty(), "Empty report descriptor");
let items = hid::ReportDescriptorItems::try_from(bytes)?;
let mut stack = Stack::new();
let mut rdesc = ReportDescriptor::default();
for rdesc_item in items.iter() {
//println!("Handling offset {}", rdesc_item.offset());
let item = rdesc_item.item();
match item.item_type() {
ItemType::Main(MainItem::Collection(i)) => {
let globals = stack.globals_const();
let locals = stack.locals_const();
// This may be an empty vec
let (_usage_range_type, usages) = match compile_usages(globals, locals) {
Ok(usages) => usages,
Err(ParserError::InvalidData { message, .. }) => {
return Err(ParserError::InvalidData {
offset: rdesc_item.offset(),
message,
})
}
Err(e) => return Err(e),
};
let c = Collection {
id: CollectionId(rdesc_item.offset() as u32),
collection_type: i,
usages,
};
stack.collections.push(c);
stack.reset_locals();
}
ItemType::Main(MainItem::EndCollection) => {
if stack.collections.pop().is_none() {
return Err(ParserError::InvalidData {
offset: rdesc_item.offset(),
message: "Too many EndCollection".into(),
});
};
stack.reset_locals();
}
ItemType::Main(item) => {
let mut fields =
match handle_main_item(&item, &mut stack, (rdesc_item.offset() * 8) as u32) {
Ok(fields) => fields,
Err(ParserError::InvalidData { message, .. }) => {
return Err(ParserError::InvalidData {
offset: rdesc_item.offset(),
message,
})
}
Err(e) => return Err(e),
};
stack.reset_locals();
// Report descriptors with a ReportCount or ReportSize of 0 (or those missing)
// will have an empty fields list. These exist in the wild.
if !fields.is_empty() {
// Now update the returned field(s) and push them into the right report
let direction = match item {
MainItem::Input(_) => Direction::Input,
MainItem::Output(_) => Direction::Output,
MainItem::Feature(_) => Direction::Feature,
_ => panic!("Invalid item for handle_main_item()"),
};
let reports: &mut Vec<RDescReport> = match direction {
Direction::Input => &mut rdesc.input_reports,
Direction::Output => &mut rdesc.output_reports,
Direction::Feature => &mut rdesc.feature_reports,
};
let report_id = fields.first().unwrap().report_id();
let report = match report_id {
None => reports.first_mut(),
Some(id) => reports
.iter_mut()
.find(|r| r.id.is_some() && &r.id.unwrap() == id),
};
let report = match report {
None => {
let initial_size = if report_id.is_some() { 8 } else { 0 };
reports.push(RDescReport {
id: *report_id,
size: initial_size,
fields: vec![],
});
reports.last_mut().unwrap()
}
Some(r) => r,
};
// We know which report the fields belong to, let's update the offsets and field id
let offset = report.size;
fields.iter_mut().for_each(|f| {
f.update_bit_offset(offset);
report.size += f.len();
});
report.fields.append(&mut fields);
}
}
ItemType::Long => {}
ItemType::Reserved => {}
ItemType::Global(GlobalItem::UsagePage(usage_page)) => {
update_stack!(stack, globals, usage_page, usage_page);
}
ItemType::Global(GlobalItem::LogicalMinimum(minimum)) => {
update_stack!(stack, globals, logical_minimum, minimum);
}
ItemType::Global(GlobalItem::LogicalMaximum(maximum)) => {
// We don't know if the maximum is signed or unsigned unless we
// look at the minimum value and check if that is signed or unsigned.
// We default to signed but if the minimum is unsigned, we might have
// to re-interpret.
let minimum = stack
.globals_const()
.logical_minimum
.unwrap_or(LogicalMinimum(0));
let mut maximum = maximum;
if minimum < LogicalMinimum(0) {
if let Some(data) = item.data() {
if !data.is_empty() {
let v = hid::HidValue::try_from(&data as &[u8]).unwrap();
maximum = LogicalMaximum(v.into());
}
}
};
update_stack!(stack, globals, logical_maximum, maximum);
}
ItemType::Global(GlobalItem::PhysicalMinimum(minimum)) => {
update_stack!(stack, globals, physical_minimum, minimum);
}
ItemType::Global(GlobalItem::PhysicalMaximum(maximum)) => {
// We don't know if the maximum is signed or unsigned unless we
// look at the minimum value and check if that is signed or unsigned.
// We default to signed but if the minimum is unsigned, we might have
// to re-interpret.
let minimum = stack
.globals_const()
.physical_minimum
.unwrap_or(PhysicalMinimum(0));
let mut maximum = maximum;
if minimum < PhysicalMinimum(0) {
if let Some(data) = item.data() {
if !data.is_empty() {
let v = hid::HidValue::try_from(&data as &[u8]).unwrap();
maximum = PhysicalMaximum(v.into())
}
}
};
update_stack!(stack, globals, physical_maximum, maximum);
}
ItemType::Global(GlobalItem::UnitExponent(exponent)) => {
update_stack!(stack, globals, unit_exponent, exponent);
}
ItemType::Global(GlobalItem::Unit(unit)) => {
update_stack!(stack, globals, unit, unit);
}
ItemType::Global(GlobalItem::ReportSize(size)) => {
update_stack!(stack, globals, report_size, size);
}
ItemType::Global(GlobalItem::ReportId(id)) => {
update_stack!(stack, globals, report_id, id);
}
ItemType::Global(GlobalItem::ReportCount(count)) => {
update_stack!(stack, globals, report_count, count);
}
ItemType::Global(GlobalItem::Push) => {
stack.push();
}
ItemType::Global(GlobalItem::Pop) => match stack.pop() {
Ok(_) => {}
Err(ParserError::InvalidData { message, .. }) => {
return Err(ParserError::InvalidData {
offset: rdesc_item.offset(),
message,
})
}
Err(e) => return Err(e),
},
ItemType::Global(GlobalItem::Reserved) => {}
ItemType::Local(LocalItem::Usage(usage_page, usage_id)) => {
let usage = LocalUsage {
usage_page: Some(usage_page),
usage_id,
};
stack.locals().usage.push(usage);
}
ItemType::Local(LocalItem::UsageId(usage_id)) => {
let usage = LocalUsage {
usage_page: None,
usage_id,
};
stack.locals().usage.push(usage);
}
ItemType::Local(LocalItem::UsageMinimum(minimum)) => {
update_stack!(stack, locals, usage_minimum, minimum);
}
ItemType::Local(LocalItem::UsageMaximum(maximum)) => {
update_stack!(stack, locals, usage_maximum, maximum);
}
ItemType::Local(LocalItem::DesignatorIndex(index)) => {
update_stack!(stack, locals, designator_index, index);
}
ItemType::Local(LocalItem::DesignatorMinimum(minimum)) => {
update_stack!(stack, locals, designator_minimum, minimum);
}
ItemType::Local(LocalItem::DesignatorMaximum(maximum)) => {
update_stack!(stack, locals, designator_maximum, maximum);
}
ItemType::Local(LocalItem::StringIndex(index)) => {
update_stack!(stack, locals, string_index, index);
}
ItemType::Local(LocalItem::StringMinimum(minimum)) => {
update_stack!(stack, locals, string_minimum, minimum);
}
ItemType::Local(LocalItem::StringMaximum(maximum)) => {
update_stack!(stack, locals, string_maximum, maximum);
}
ItemType::Local(LocalItem::Delimiter(delimiter)) => {
update_stack!(stack, locals, delimiter, delimiter);
}
ItemType::Local(LocalItem::Reserved { value: _ }) => {}
};
}
Ok(rdesc)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_twos_comp() {
assert_eq!(5u8.twos_comp(3), -3);
assert_eq!(5u8.twos_comp(4), 5);
assert_eq!(0xffu8.twos_comp(8), -1);
assert_eq!(5u16.twos_comp(3), -3);
assert_eq!(5u16.twos_comp(4), 5);
assert_eq!(0xffffu16.twos_comp(16), -1);
assert_eq!(5u32.twos_comp(3), -3);
assert_eq!(5u32.twos_comp(4), 5);
assert_eq!(0xffffffffu32.twos_comp(32), -1);
}
#[test]
fn extract() {
let bytes: [u8; 4] = [0b1100_1010, 0b1011_1001, 0b1001_0110, 0b0001_0101];
let test_field = |bits: Range<usize>, signed: bool| -> VariableField {
VariableField {
id: FieldId(0),
report_id: None,
bits,
usage: Usage::from(0),
logical_minimum: LogicalMinimum(if signed { -1 } else { 0 }),
logical_maximum: LogicalMaximum(0),
physical_minimum: None,
physical_maximum: None,
unit: None,
unit_exponent: None,
collections: vec![],
is_relative: false,
wraps: false,
is_nonlinear: false,
has_no_preferred_state: false,
has_null_state: false,
is_buffered_bytes: false,
is_volatile: None,
}
};
assert_eq!(0u8, test_field(0..1, false).extract(&bytes).unwrap().into());
assert_eq!(2u8, test_field(0..2, false).extract(&bytes).unwrap().into());
assert_eq!(
10u8,
test_field(0..4, false).extract(&bytes).unwrap().into()
);
assert_eq!(0i8, test_field(0..1, true).extract(&bytes).unwrap().into());
assert_eq!(-2i8, test_field(0..2, true).extract(&bytes).unwrap().into());
assert_eq!(-6i8, test_field(0..4, true).extract(&bytes).unwrap().into());
assert_eq!(
0b1001_1100u8,
test_field(4..12, true).extract(&bytes).unwrap().into()
);
assert_eq!(
0b1001_1100u8 as i8,
test_field(4..12, true).extract(&bytes).unwrap().into()
);
assert_eq!(
0u16,
test_field(0..1, false).extract(&bytes).unwrap().into()
);
assert_eq!(
2u16,
test_field(0..2, false).extract(&bytes).unwrap().into()
);
assert_eq!(
10u16,
test_field(0..4, false).extract(&bytes).unwrap().into()
);
assert_eq!(0i16, test_field(0..1, true).extract(&bytes).unwrap().into());
assert_eq!(
-2i16,
test_field(0..2, true).extract(&bytes).unwrap().into()
);
assert_eq!(
-6i16,
test_field(0..4, true).extract(&bytes).unwrap().into()
);
assert_eq!(
0b0110_1011_1001_1100,
test_field(4..20, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0b0110_1011_1001_1100,
test_field(4..20, true).extract(&bytes).unwrap().into()
);
assert_eq!(
0b1_0110_1011_1001_110u16 as i16,
test_field(5..21, true).extract(&bytes).unwrap().into()
);
assert_eq!(
0b0110_1011_1001_1100,
test_field(4..20, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0b0110_1011_1001_1100,
test_field(4..20, true).extract(&bytes).unwrap().into()
);
assert_eq!(
((0b1_0110_1011_1001_110u16 as i16) as i32),
test_field(5..21, true).extract(&bytes).unwrap().into()
);
assert_eq!(
((0b1_0110_1011_1001_110u16 as i16) as i32),
test_field(5..21, true).extract(&bytes).unwrap().into()
);
let bytes: [u8; 1] = [0x0f];
assert_eq!(0x3, test_field(0..2, false).extract(&bytes).unwrap().into());
assert_eq!(0xf, test_field(0..4, false).extract(&bytes).unwrap().into());
assert_eq!(0x0, test_field(4..8, false).extract(&bytes).unwrap().into());
assert_eq!(
0x0f,
test_field(0..8, false).extract(&bytes).unwrap().into()
);
let bytes: [u8; 2] = [0x0f, 0x5e];
assert_eq!(0x3, test_field(0..2, false).extract(&bytes).unwrap().into());
assert_eq!(0xf, test_field(0..4, false).extract(&bytes).unwrap().into());
assert_eq!(0x0, test_field(4..8, false).extract(&bytes).unwrap().into());
assert_eq!(
0xe0f,
test_field(0..12, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0x5e0f,
test_field(0..16, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0xe,
test_field(8..12, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0x5,
test_field(12..16, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0x5e,
test_field(8..16, false).extract(&bytes).unwrap().into()
);
let bytes: [u8; 4] = [0x0f, 0x5e, 0xab, 0x78];
assert_eq!(0x3, test_field(0..2, false).extract(&bytes).unwrap().into());
assert_eq!(0xf, test_field(0..4, false).extract(&bytes).unwrap().into());
assert_eq!(0x0, test_field(4..8, false).extract(&bytes).unwrap().into());
assert_eq!(
0xe0f,
test_field(0..12, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0x5e0f,
test_field(0..16, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0xe,
test_field(8..12, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0x5,
test_field(12..16, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0x5e,
test_field(8..16, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0xb5e0f,
test_field(0..20, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0xab5e0f,
test_field(0..24, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0xb5e0,
test_field(4..20, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0xab5e,
test_field(8..24, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0xb,
test_field(16..20, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0xab,
test_field(16..24, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0x78ab5e0f,
test_field(0..32, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0x7,
test_field(28..32, false).extract(&bytes).unwrap().into()
);
assert_eq!(
0x78,
test_field(24..32, false).extract(&bytes).unwrap().into()
);
}
#[test]
#[cfg(feature = "hut")]
fn test_array_field_with_usage_range() {
use crate::hid::*;
use hut;
let rdesc_bytes = ReportDescriptorBuilder::new()
.usage_page(hut::UsagePage::GenericDesktop)
.usage_id(hut::GenericDesktop::Mouse)
.open_collection(CollectionItem::Application)
.append(LogicalMinimum::from(0).into())
.append(LogicalMaximum::from(255).into())
.append(ReportSize::from(8).into())
.append(ReportCount::from(3).into())
// Use UsageMinimum and UsageMaximum for the array field
.append(UsageMinimum::from(0x00010001).into())
.append(UsageMaximum::from(0x000100ff).into())
.input(ItemBuilder::new().array().input())
.close_collection()
.build();
let rdesc = ReportDescriptor::try_from(rdesc_bytes.as_slice()).unwrap();
let input_reports = rdesc.input_reports();
assert_eq!(input_reports.len(), 1);
let report = &input_reports[0];
let fields: Vec<&ArrayField> = report
.fields()
.iter()
.filter_map(|f| match f {
Field::Array(a) => Some(a),
_ => None,
})
.collect();
assert_eq!(fields.len(), 1);
let array_field = fields[0];
assert!(
array_field.is_usage_range(),
"ArrayField created with UsageMinimum/UsageMaximum should have is_usage_range() == true"
);
let usage_range = array_field.usage_range();
assert!(
usage_range.is_some(),
"ArrayField created with UsageMinimum/UsageMaximum should return Some from usage_range()"
);
let range = usage_range.unwrap();
assert_eq!(range.minimum(), UsageMinimum::from(0x00010001));
assert_eq!(range.maximum(), UsageMaximum::from(0x000100ff));
let usages = array_field.usages();
assert_eq!(usages.len(), 255); // 0x01 to 0xff inclusive
assert_eq!(u32::from(&usages[0]), 0x00010001);
assert_eq!(u32::from(&usages[254]), 0x000100ff);
}
#[test]
#[cfg(feature = "hut")]
fn test_array_field_with_individual_usages() {
use crate::hid::*;
use hut;
let rdesc_bytes = ReportDescriptorBuilder::new()
.usage_page(hut::UsagePage::Digitizers)
.usage_id(hut::Digitizers::Digitizer)
.open_collection(CollectionItem::Application)
.append(LogicalMinimum::from(0).into())
.append(LogicalMaximum::from(255).into())
.append(ReportSize::from(8).into())
.append(ReportCount::from(3).into())
// Use individual Usage items instead of UsageMinimum/UsageMaximum
.usage_id(hut::Digitizers::TipSwitch) // 0x42
.usage_id(hut::Digitizers::BarrelSwitch) // 0x44
.usage_id(hut::Digitizers::SecondaryBarrelSwitch) // 0x5a
.input(ItemBuilder::new().array().input())
.close_collection()
.build();
let rdesc = ReportDescriptor::try_from(rdesc_bytes.as_slice()).unwrap();
let input_reports = rdesc.input_reports();
assert_eq!(input_reports.len(), 1);
let report = &input_reports[0];
let fields: Vec<&ArrayField> = report
.fields()
.iter()
.filter_map(|f| match f {
Field::Array(a) => Some(a),
_ => None,
})
.collect();
assert_eq!(fields.len(), 1);
let array_field = fields[0];
assert!(
!array_field.is_usage_range(),
"ArrayField created with individual Usages should have is_usage_range() == false"
);
let usage_range = array_field.usage_range();
assert!(
usage_range.is_none(),
"ArrayField created with individual Usages should return None from usage_range()"
);
let usages = array_field.usages();
assert_eq!(usages.len(), 3, "Should have exactly 3 usages, not a range");
assert_eq!(u16::from(usages[0].usage_id), 0x42); // TipSwitch
assert_eq!(u16::from(usages[1].usage_id), 0x44); // BarrelSwitch
assert_eq!(u16::from(usages[2].usage_id), 0x5a); // SecondaryBarrelSwitch
}
#[test]
#[cfg(feature = "hut")]
fn test_array_field_with_single_usage_as_range() {
use crate::hid::*;
use hut;
let rdesc_bytes = ReportDescriptorBuilder::new()
.usage_page(hut::UsagePage::Button)
.usage_id(hut::Button::Button(1))
.open_collection(CollectionItem::Application)
.append(LogicalMinimum::from(0).into())
.append(LogicalMaximum::from(1).into())
.append(ReportSize::from(1).into())
.append(ReportCount::from(1).into())
// Edge case: UsageMinimum == UsageMaximum (single usage as a range)
.append(UsageMinimum::from(0x00090001).into())
.append(UsageMaximum::from(0x00090001).into())
.input(ItemBuilder::new().array().input())
.close_collection()
.build();
let rdesc = ReportDescriptor::try_from(rdesc_bytes.as_slice()).unwrap();
let input_reports = rdesc.input_reports();
assert_eq!(input_reports.len(), 1);
let report = &input_reports[0];
let fields: Vec<&ArrayField> = report
.fields()
.iter()
.filter_map(|f| match f {
Field::Array(a) => Some(a),
_ => None,
})
.collect();
assert_eq!(fields.len(), 1);
let array_field = fields[0];
assert!(
array_field.is_usage_range(),
"ArrayField with UsageMinimum==UsageMaximum should still have is_usage_range() == true"
);
let usage_range = array_field.usage_range();
assert!(usage_range.is_some());
let range = usage_range.unwrap();
assert_eq!(range.minimum(), UsageMinimum::from(0x00090001));
assert_eq!(range.maximum(), UsageMaximum::from(0x00090001));
let usages = array_field.usages();
assert_eq!(usages.len(), 1);
}
#[test]
#[cfg(feature = "hut")]
fn test_array_field_with_single_individual_usage() {
use crate::hid::*;
use hut;
let rdesc_bytes = ReportDescriptorBuilder::new()
.usage_page(hut::UsagePage::Button)
.usage_id(hut::Button::Button(1))
.open_collection(CollectionItem::Application)
.append(LogicalMinimum::from(0).into())
.append(LogicalMaximum::from(1).into())
.append(ReportSize::from(1).into())
.append(ReportCount::from(1).into())
// Single individual usage
.usage_id(hut::Button::Button(1))
.input(ItemBuilder::new().array().input())
.close_collection()
.build();
let rdesc = ReportDescriptor::try_from(rdesc_bytes.as_slice()).unwrap();
let input_reports = rdesc.input_reports();
assert_eq!(input_reports.len(), 1);
let report = &input_reports[0];
let fields: Vec<&ArrayField> = report
.fields()
.iter()
.filter_map(|f| match f {
Field::Array(a) => Some(a),
_ => None,
})
.collect();
assert_eq!(fields.len(), 1);
let array_field = fields[0];
assert!(
!array_field.is_usage_range(),
"ArrayField with single individual Usage should have is_usage_range() == false"
);
assert!(
array_field.usage_range().is_none(),
"ArrayField with single individual Usage should return None from usage_range()"
);
let usages = array_field.usages();
assert_eq!(usages.len(), 1);
}
}