4 releases (2 breaking)
0.3.0 | Nov 19, 2024 |
---|---|
0.2.1 | Jun 10, 2024 |
0.2.0 | May 23, 2024 |
0.1.0 | May 15, 2024 |
#493 in Hardware support
413 downloads per month
Used in 3 crates
1MB
17K
SLoC
hut - HID Usage Tables
This crate provides access to the HID Usage Tables (HUT).
This module is created through code generation from the HID Usage Tables.
This crate merely provides enums and functions to convert between values, it
does not concern itself with how to obtain the values to be used. Look at e.g.
the hidreport
crate for parsing HID Report descriptors.
use hut::*;
let usage = Usage::from(GenericDesktop::Mouse);
let usage_page_value: u16 = 0x01; // Generic Desktop
let usage_id_value: u16 = 0x02; // Mouse
let usage_value: u32 = (usage_page_value as u32) << 16 | usage_id_value as u32;
let u: Usage = Usage::try_from(usage_value).unwrap();
See the documentation for more details.
License
hut
is MIT-licensed, see the COPYING file for details.
lib.rs
:
A wrapper around the HID Usage Tables (HUT).
In this document and unless stated otherwise, a reference to
- "HID Section a.b.c" refers to the HID Device Class Definition for HID 1.11
- "HUT Section a.b.c" refers to the HID Usage Tables (HUT) version 1.5
This module is created through code generation from the HID Usage Tables.
Terminology
See HID Section 5.5: a HID Usage is a 32 bit value comprising of a 16-bit Usage Page (MSB) and a 16-bit Usage ID (LSB) so that:
let usage: u32 = (usage_page << 16) | usage_id;
Note that the HID encoding requires little endian byte order on the wire.
In this module:
- "Usage Page" refers to the 16-bit value. Where the Usage Page is converted
to or from a 32-bit value the Usage Page is in the upper 16 bits of that value and
the lower 16 bits are ignored or set to zero.
let usage_page: u16 = (usage >> 16) as u16 & 0xffff;
- "Usage ID" refers to the 16-bit value. Where the Usage ID is converted to
or from a 32-bit value the Usage is in the lower 16 bits of that value and
the upper 16 bits are ignored or set to zero.
let usage_id: u16 = (usage & 0xffff) as u16;
- "Usage" refers to the 32-bit value comprising a Usage Page and a Usage.
Converting between types
All defined Usages and UsagePages implement [AsUsagePage] and (if applicable) [AsUsage] as
well as the From<u16>
, From<u32>
, TryFrom<u16>
, and TryFrom<u32>
conversions so that:
let usage_page_value: u16 = 0x01; // Generic Desktop
let usage_id_value: u16 = 0x02; // Mouse
let usage_value: u32 = (usage_page_value as u32) << 16 | usage_id_value as u32;
// Create a known Usage from a 32-bit value
let u: Usage = Usage::try_from(usage_value).unwrap();
assert!(matches!(u, Usage::GenericDesktop(GenericDesktop::Mouse)));
// Create a known Usage from the Usage Page and Usage ID values
let u2 = Usage::new_from_page_and_id(usage_page_value, usage_id_value).unwrap();
assert_eq!(u, u2);
// Create a known Usage from an individual Usage Page enum item
let u3 = Usage::from(GenericDesktop::Mouse);
assert_eq!(u, u3);
// Create a known Usage from an known Usage Page enum item
let gd_mouse = GenericDesktop::try_from(usage_id_value).unwrap();
let u4 = Usage::from(gd_mouse);
assert_eq!(u, u4);
// Convert to and fro the Usage either via u32 or the AsUsage trait
let u = GenericDesktop::Mouse;
assert_eq!(u32::from(&u), usage_value);
assert_eq!(u.usage_value(), usage_value);
// Extract the 16-bit Usage ID either via u16 or the AsUsage trait
assert_eq!(u16::from(&u), usage_id_value);
assert_eq!(u.usage_id_value(), usage_id_value);
// Extract the Usage Page from the Usage enum value
let up = u.usage_page();
assert!(matches!(up, UsagePage::GenericDesktop));
let up: UsagePage = UsagePage::from(&u);
assert!(matches!(up, UsagePage::GenericDesktop));
// Get the Usage Page numeric value is via the AsUsagePage
assert_eq!(u16::from(&up), usage_page_value);
assert_eq!(up.usage_page_value(), usage_page_value);
Naming Usages (e.g. GenericDesktop::Mouse
) above works for Defined Usage
Pages, Generated Usage Pages (see below) need to be destructured via their
individual elements:
let usage_page_value: u16 = 0x09; // Button
let usage_id_value: u16 = 8; // Button number 8
let usage_value: u32 = (usage_page_value as u32) << 16 | usage_id_value as u32;
let u = Usage::try_from(usage_value).unwrap();
let button = Usage::Button(Button::Button(8));
assert!(matches!(Usage::try_from(usage_value).unwrap(), button));
// or via from() or into()
let button: Usage = Button::Button(8).into();
assert!(matches!(Usage::try_from(usage_value).unwrap(), button));
Once a Usage is created, the [AsUsagePage] and [AsUsage] traits and conversion to and from [u16] and [u32] work the same as for a Defined Usage Page.
Names of Usage Pages and Usage IDs
All defined Usages and UsagePages implement name()
to return a string
representing that page or usage:
let up = UsagePage::GenericDesktop;
assert_eq!(up.name(), "Generic Desktop");
let up = UsagePage::SimulationControls;
assert_eq!(up.name(), "Simulation Controls");
let usage = GenericDesktop::Mouse;
assert_eq!(usage.name(), "Mouse");
let usage = SimulationControls::CyclicControl;
assert_eq!(usage.name(), "Cyclic Control");
Generated Usage Pages
The HUT differ between "Defined" and "Generated" Usage Pages. The former define Usage ID values and their meanings, the latter define a Usage ID range, with the actual Usage ID simply referring to "nth thing in this usage page". One example for this is the Button Usage Page (0x09) where a Usage ID of 3 means "Button 3".
let b = Button::Button(3);
let o = Ordinal::Ordinal(23);
Unlike Defined Usage Pages these Generated Usage Pages need to be destructured in match
statements:
let b = Button::Button(3);
match b {
Button::Button(b) => println!("Button {b}"),
_ => {},
}
The following usage pages are Generated:
- Usage Page 0x9 - [Button]
- Usage Page 0xA - [Ordinal]
- Usage Page 0x10 - [Unicode]
- Usage Page 0x81 - [MonitorEnumerated]
A further special case of this is the [Unicode] usage page which is not in the HUT document and was inserted during code generation.
Vendor Defined Usage Pages (0xFF00 to 0xFFFF)
Vendor Defined Usage Pages and VendorUsages are not autogenerated and thus
follow a different approach: the Usage inside the Usage Page is a simple
numeric usage that needs to be destructured in match
statements.
let v = Usage::VendorDefinedPage {
vendor_page: VendorPage::try_from(0xff00 as u16).unwrap(),
usage: VendorDefinedPage::VendorUsage { usage_id: 0x01 },
};
match v {
Usage::VendorDefinedPage {
vendor_page,
usage,
} => println!("Vendor Usage ID {usage}"),
_ => {},
}
A notable exception is the [Wacom] (0xFF0D
) which is technically a
Vendor-defined page but with defined Usages. Converting from a [UsagePage]
or [Usage] numeric value will produce the correct or [Wacom] Usage, not a VendorDefinedPage::VendorUsage.
Reserved Usage Pages
Reserved Usage Pages and ReservedUsages are
not autogenerated and thus follow a different approach: the Usage inside the Usage Page is a simple
numeric usage that needs to be destructured in match
statements.
Unlike the Vendor Defined Usage Pages a Reserved Usage Page may become a defined page in a later version of the HUT standard and thus in a future version of this crate. A caller must not rely on a Reserved Usage Page or Reserved Usage to remain so.
The following Usage Pages are reserved as of HUT 1.5 (see HUT Section 3, p15):
0x13
,0x15-0x1F
0x21-0x3F
0x42-0x58
0x5A-0x7F
0x83-0x83
0x86-0x8B
0x8F-0x8F
0x93-0xF1CF
0xF1D1-0xFEFF
Renames
For technical reasons, spaces, (
), dashes (-
), and slashes (/
) are
stripped out of Usage Page and Usage names. The string representation via
the Display
trait will have the unmodified value.
Dependencies
~245–700KB
~16K SLoC