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

Download history 5/week @ 2024-08-19 7/week @ 2024-08-26 5/week @ 2024-09-09 9/week @ 2024-09-16 27/week @ 2024-09-23 14/week @ 2024-09-30 7/week @ 2024-10-07 2/week @ 2024-10-14 68/week @ 2024-10-28 31/week @ 2024-11-04 14/week @ 2024-11-11 244/week @ 2024-11-18 107/week @ 2024-11-25 46/week @ 2024-12-02

413 downloads per month
Used in 3 crates

MIT license

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

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