This crate provides tools for working with the Human Interface Devices (HID) protocol. It can parse, build and display Report Descriptors.
Following example parses a raw HID Report Descriptor
use hid_tools::parse;
fn main() {
let bytes = [
0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x07,
0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
0x95, 0x08, 0x81, 0x02, 0x19, 0x01, 0x29, 0x97, 0x15, 0x00,
0x25, 0x01, 0x75, 0x01, 0x95, 0x98, 0x81, 0x02, 0xc0,
];
let parsed = parse::report_descriptor(&bytes).unwrap();
println!("{}", parsed);
}
Following is an example output for a 2-factor authentication usb-stick.
[06, d0, f1] Usage Page (FIDO Alliance)
[09, 01] Usage (U2F Authenticator Device)
[a1, 01] Collection (Application)
[09, 20] Usage (Input Report Data)
[15, 00] Logical Minimum (0)
[26, ff, 00] Logical Maximum (255)
[75, 08] Report Size (8)
[95, 40] Report Count (64)
[81, 02] Input (Data, Var, Abs)
[09, 21] Usage (Output Report Data)
[15, 00] Logical Minimum (0)
[26, ff, 00] Logical Maximum (255)
[75, 08] Report Size (8)
[95, 40] Report Count (64)
[91, 02] Output (Data, Var, Abs)
[c0] End Collection (0)
The HID Report Descriptor is a hard coded array of bytes that describe the device's data packets. This package provides a builder to create a Report Descriptor.
use hid_tools::report_builder::ReportDescriptorBuilder;
use hid_tools::hid::Collection;
use hid_tools::usage_table::{UsagePage};
use hid_tools::usage_table::generic_desktop::GenericDesktopControlsUsage;
fn main() {
let raw_report = ReportDescriptorBuilder::new()
.usage_page(UsagePage::GenericDesktopControls)
.usage(GenericDesktopControlsUsage::Mouse)
.item(Collection::Application)
// add more items here (see also examples)
.end_collection()
.build()
.bytes();
println!("{:02x?}", raw_report)
}
With the parsed or built Report Descriptor we know which data Reports to expect. With this we can parse a Report.
use hid_tools::parse::report_descriptor;
use hid_tools::report::{expected_input_reports, parse_raw_input_report};
fn main() {
let keyboard_report_descriptor_bytes: Vec<u8> = vec![0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x08, 0x19, 0x01,0x29, 0x03, 0x15, 0x00, 0x25,
0x01, 0x75, 0x01, 0x95, 0x03,0x91, 0x02, 0x95, 0x05, 0x91, 0x01, 0x05, 0x07, 0x19, 0xe0,0x29,
0xe7, 0x95, 0x08, 0x81, 0x02, 0x75, 0x08, 0x95, 0x01,0x81, 0x01, 0x19, 0x00, 0x29, 0x91,
0x26, 0xff, 0x00, 0x95, 0x06, 0x81, 0x00, 0xc0];
let event_report: Vec<u8> = vec![0x02, 0, 0x04, 0x05, 0, 0, 0, 0]; // Left shift, a and b pressed on keyboard
let report_descriptor = report_descriptor(&keyboard_report_descriptor_bytes).unwrap();
let expected_reports = expected_input_reports(&report_descriptor).unwrap();
let parsed_report = parse_raw_input_report(&event_report, &expected_reports).unwrap();
println!("Event: {:?} \n\n{}", event_report, parsed_report);
}
will print
Event: [2, 0, 4, 5, 0, 0, 0, 0]
Keyboard - Keyboard Left Control(0)
Keyboard - Keyboard Left Shift(1)
Keyboard - Keyboard Left Alt(0)
Keyboard - Keyboard Left GUI(0)
Keyboard - Keyboard Right Control(0)
Keyboard - Keyboard Right Shift(0)
Keyboard - Keyboard Right Alt(0)
Keyboard - Keyboard Right GUI(0)
Constant(0)
Keyboard - Keyboard a and A
Keyboard - Keyboard b and B
See also the parse_raw_report_keyboard
example.
- Logical minimum/maximum
- Convert more Usage tables (help wanted)