This C-header (hid_def.h) declares simple DSL, which helps to define HID report descriptors immediately in C source code in human readable form without using any additional tools like hidrd.
The language itself is quite similar to the examples from specification (HID1_11.pdf), so it is easy to sense and use to declare the HID reports of any complexity.
Of course, you must understand how HID report descriptors arranged and how to construct it using elements, but more you don't need to think about its internal binary representation.
IMPORTANT NOTE: Because this DSL was developed for and using GCC/CLang so it haven't tested with other kind compilers. So PRs with support for other compilers are welcome.
In order to get first notion about this library look the example below.
This is HID report descriptor from official specification HID 1.11 for standard three button mouse:
Usage Page (Generic Desktop),
Usage (Mouse),
Collection (Application),
Usage (Pointer),
Collection (Physical),
Usage Page (Buttons),
Usage Minimum (01),
Usage Maximun (03),
Logical Minimum (0),
Logical Maximum (1),
Report Count (3),
Report Size (1),
Input (Data, Variable, Absolute),
Report Count (1),
Report Size (5),
Input (Constant),
Usage Page (Generic Desktop),
Usage (X),
Usage (Y),
Logical Minimum (-127),
Logical Maximum (127),
Report Size (8),
Report Count (2),
Input (Data, Variable, Relative),
End Collection,
End Collection
This is same example, but rewritten to C using our HID reports definition:
static const uint8_t mouse_hid_report_descriptor[] = {
HID_USAGE_PAGE (GENERIC_DESKTOP),
HID_USAGE (MOUSE),
HID_COLLECTION (APPLICATION),
HID_USAGE (POINTER),
HID_COLLECTION (PHYSICAL),
HID_USAGE_PAGE (BUTTONS),
HID_USAGE_MINIMUM (1, 1),
HID_USAGE_MAXIMUM (1, 3),
HID_LOGICAL_MINIMUM (1, 0),
HID_LOGICAL_MAXIMUM (1, 1),
HID_REPORT_COUNT (3),
HID_REPORT_SIZE (1),
HID_INPUT (DATA, VARIABLE, ABSOLUTE),
HID_REPORT_COUNT (1),
HID_REPORT_SIZE (5),
HID_INPUT (CONSTANT),
HID_USAGE_PAGE (GENERIC_DESKTOP),
HID_USAGE (X),
HID_USAGE (Y),
HID_LOGICAL_MINIMUM (1, -127),
HID_LOGICAL_MAXIMUM (1, 127),
HID_REPORT_SIZE (8),
HID_REPORT_COUNT (2),
HID_INPUT (DATA, VARIABLE, RELATIVE),
HID_END_COLLECTION (PHYSICAL),
HID_END_COLLECTION (APPLICATION),
};
This is quite similar to original example, but elements have a prefix HID_ to avoid collisions with other identifiers in your sources. Also the elements with values (like HID_LOGICAL_MINIMUM
, HID_LOGICAL_MAXIMUM
and etc.) get two arguments: the first means a size of value in bytes, the second is the value itself. The elements, which defines reports (like HID_INPUT
, HID_OUTPUT
and HID_FEATURE
), can get any number of flags. The argument of HID_END_COLLECTION
elements was added in usability purposes, so it is optional and can be away.
This is a binary output which get for that descriptor as result of compilation:
00000000 05 01 09 02 a1 01 09 01 a1 00 05 09 19 01 29 03 |..............).|
00000010 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 |..%...u.....u...|
00000020 05 01 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 |...0.1..%.u.....|
00000030 c0 c0 |..|
This is result of converting binary representation back to text using hidrd-convert
utility:
Usage Page (Desktop), ; Generic desktop controls (01h)
Usage (Mouse), ; Mouse (02h, application collection)
Collection (Application),
Usage (Pointer), ; Pointer (01h, physical collection)
Collection (Physical),
Usage Page (Button), ; Button (09h)
Usage Minimum (01h),
Usage Maximum (03h),
Logical Minimum (0),
Logical Maximum (1),
Report Count (3),
Report Size (1),
Input (Variable),
Report Count (1),
Report Size (5),
Input (Constant),
Usage Page (Desktop), ; Generic desktop controls (01h)
Usage (X), ; X (30h, dynamic value)
Usage (Y), ; Y (31h, dynamic value)
Logical Minimum (-127),
Logical Maximum (127),
Report Size (8),
Report Count (2),
Input (Variable, Relative),
End Collection,
End Collection
As you can see, both textual representation of report for mouse from example and result of decompilation is identical.
In some report descriptors you need use measurement units. The HID specification define some basic units which can be used to derive any derivative units. See example below:
/* Ampere is a basic unit */
HID_UNIT(SI(LIN), A(1)), HID_UNIT_EXPONENT(0),
/* Volt can be derived as cm^2 x g^1 x s^-3 x A^-1 x 10^7 */
HID_UNIT(SI(LIN), CM(2), G(1), S(-3), A(-1)), HID_UNIT_EXPONENT(7),
/* Watt can be derived as cm^2 x g^1 x s^-3 */
HID_UNIT(SI(LIN), CM(2), G(1), S(-3)), HID_UNIT_EXPONENT(7),
/* Joule can be derived as cm^2 x g^1 x s^-2 */
HID_UNIT(SI(LIN), CM(2), G(1), S(-2)), HID_UNIT_EXPONENT(7),
In example above we used linear Si measurement system (SI(LIN)) and the next basic units: Centimeter (CM), Gram (G), Second (S), Ampere (A). With it we derived the next derivative units: Volt (V), Watt (W), Joule (J). Moreover we required to use unit exponent to fit value order.
To get more usage examples look in tests.
The implementation uses some advanced C pre-processor techniques, which defined by macros from macro.h header.