HID report descriptor I/O library and conversion tool
C M4 Makefile Shell Other
Latest commit 1abf703 Jun 10, 2016 @jigpu jigpu Add code.h to fmt.h
Add the missing optional 'code' format ito the public fmt.h header

README.md

Hidrd

Hidrd is a library and a tool for reading, writing and converting HID report descriptors in/between various formats, with support as follows:

Format Reading Writing
Native (binary) Yes Yes
XML Yes Yes
HID specification example format No Yes
C source code No Yes

Hidrd contains hidrd-convert - a tool for converting report descriptors between formats. As it supports reading and writing XML, it is suitable for descriptor authoring and editing, on par with and in some ways better than the official HID Descriptor Tool.

Advantages over the official tool include:

  • running on *nix natively,
  • reading native (binary) descriptors,
  • smaller native descriptor output,
  • non-interactive report descriptor generation, allowing for use in software/firmware builds.

Usage

hidrd-convert --help output:

Usage: hidrd-convert [OPTION]... [INPUT [OUTPUT]]
Convert a HID report descriptor.
With no INPUT, or when INPUT is -, read standard input.
With no OUTPUT, or when OUTPUT is -, write standard output.

Options:
  -h, --help                       this help message
  --hf, --help-formats             description of formats
                                   and their options
  -i, --input-format=FORMAT        use FORMAT for input
  --io=LIST, --input-options=LIST  use LIST input format options
  -o, --output-format=FORMAT       use FORMAT for output
  --oo=LIST, --output-options=LIST use LIST output format options

Formats:

  natv [IO] - native
   xml [IO] - XML
  spec [ O] - specification example
  code [ O] - source code

Default options are -i natv -o natv.

See hidrd-convert --help-formats for format options.

Examples

Given a mouse report descriptor in binary (hex-dumped with od -An -tx1, revert with xxd -r -p):

 05 01 09 02 a1 01 09 01 a0 05 09 19 01 29 03 14
 25 01 75 01 95 03 81 02 75 05 95 01 81 01 05 01
 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 81 06
 c0 c0

Use hidrd-convert -o spec to display it in the HID specification example format:

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 Size (1),
        Report Count (3),
        Input (Variable),
        Report Size (5),
        Report Count (1),
        Input (Constant),
        Usage Page (Desktop),       ; Generic desktop controls (01h)
        Usage (X),                  ; X (30h, dynamic value)
        Usage (Y),                  ; Y (31h, dynamic value)
        Usage (Wheel),              ; Wheel (38h, dynamic value)
        Logical Minimum (-127),
        Logical Maximum (127),
        Report Size (8),
        Report Count (3),
        Input (Variable, Relative),
    End Collection,
End Collection

Convert it to XML with hidrd-convert -o xml (wrapped for readability):

<?xml version="1.0"?>
<descriptor xmlns="http://digimend.sourceforge.net"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://digimend.sourceforge.net hidrd.xsd">
  <usage_page>desktop<!-- Generic desktop controls (01h) --></usage_page>
  <usage>desktop_mouse<!-- Mouse (02h, application collection) --></usage>
  <COLLECTION type="application">
    <usage>desktop_pointer<!-- Pointer (01h, physical collection) --></usage>
    <COLLECTION type="physical">
      <usage_page>button<!-- Button (09h) --></usage_page>
      <usage_minimum>01</usage_minimum>
      <usage_maximum>03</usage_maximum>
      <logical_minimum>0</logical_minimum>
      <logical_maximum>1</logical_maximum>
      <report_size>1</report_size>
      <report_count>3</report_count>
      <input>
        <variable/>
      </input>
      <report_size>5</report_size>
      <report_count>1</report_count>
      <input>
        <constant/>
      </input>
      <usage_page>desktop<!-- Generic desktop controls (01h) --></usage_page>
      <usage>desktop_x<!-- X (30h, dynamic value) --></usage>
      <usage>desktop_y<!-- Y (31h, dynamic value) --></usage>
      <usage>desktop_wheel<!-- Wheel (38h, dynamic value) --></usage>
      <logical_minimum>-127</logical_minimum>
      <logical_maximum>127</logical_maximum>
      <report_size>8</report_size>
      <report_count>3</report_count>
      <input>
        <variable/>
        <relative/>
      </input>
    </COLLECTION>
  </COLLECTION>
</descriptor>

After editing (e.g. removing the wheel input), convert it back to binary with hidrd-convert -i xml (hex-dumped with od -An -tx1, revert with xxd -r -p):

 05 01 09 02 a1 01 09 01 a0 05 09 19 01 29 03 14
 25 01 75 01 95 03 81 02 75 05 95 01 81 01 05 01
 09 30 09 31 15 81 25 7f 75 08 95 02 81 06 c0 c0

Or use hidrd-convert -i xml -o code to produce C source code for the edited descriptor:

0x05, 0x01, /*  Usage Page (Desktop),               */
0x09, 0x02, /*  Usage (Mouse),                      */
0xA1, 0x01, /*  Collection (Application),           */
0x09, 0x01, /*      Usage (Pointer),                */
0xA0,       /*      Collection (Physical),          */
0x05, 0x09, /*          Usage Page (Button),        */
0x19, 0x01, /*          Usage Minimum (01h),        */
0x29, 0x03, /*          Usage Maximum (03h),        */
0x14,       /*          Logical Minimum (0),        */
0x25, 0x01, /*          Logical Maximum (1),        */
0x75, 0x01, /*          Report Size (1),            */
0x95, 0x03, /*          Report Count (3),           */
0x81, 0x02, /*          Input (Variable),           */
0x75, 0x05, /*          Report Size (5),            */
0x95, 0x01, /*          Report Count (1),           */
0x81, 0x01, /*          Input (Constant),           */
0x05, 0x01, /*          Usage Page (Desktop),       */
0x09, 0x30, /*          Usage (X),                  */
0x09, 0x31, /*          Usage (Y),                  */
0x15, 0x81, /*          Logical Minimum (-127),     */
0x25, 0x7F, /*          Logical Maximum (127),      */
0x75, 0x08, /*          Report Size (8),            */
0x95, 0x02, /*          Report Count (2),           */
0x81, 0x06, /*          Input (Variable, Relative), */
0xC0,       /*      End Collection,                 */
0xC0        /*  End Collection                      */

XML format

Hidrd uses its own XML schema for report descriptors, as there is no standard. It is installed to /usr/share/xml/schema/hidrd.xml and is the definite reference to the XML format. The schema is generated from lib/fmt/xml/hidrd.xsd.m4 during the build by substituting usage page and ID tokens. The template might be easier to navigate as it is much shorter. Still, it is a big schema, so a short introduction follows.

The root element of a descriptor is descriptor, and its contents are item or meta-item elements. Each item element correspond to a single report descriptor item (e.g. usage, report_count, input) and can have attributes or child elements describing their parameters (e.g. collection type attribute, or input flag elements). Most of the time, parameters are described with child elements. All item element names match the HID specification item names, only lowercased and having spaces replaced by underscores.

Meta-item elements have uppercase names and correspond to two report descriptor items - one for the opening and one for the closing tag. There are only three of them: COLLECTION, PUSH and SET. Each can be represented by a pair of item elements instead (collection/end_collection, push/pop, opening delimiter / closing delimiter). COLLECTION meta-item element has type attribute, similarly to collection. Meta-item elements are provided for convenience and can contain both item and meta-item elements.

Wherever a token value is accepted, such as for COLLECTION type attribute or usage_page element contents, a numeric value (either decimal or hexadecimal, but not both) is accepted as well. This is provided to support values not known to hidrd. See schema for details.

Arbitrary short and long items are supported as well, although not seen in practice. In fact, arbitrary binary stream should be representable with the schema, but hidrd doesn't support it at the moment.

Roadmap

Hidrd development was stagnant for a long time, however some internal redesign should be made, particularly in error reporting. Next, perhaps, specification example input support should be implemented. Then validation needs to be cleaned up (bugs #1 and #7).

After that, the time may come to implement a stream validation filter with recommendations from HID Parser Error Checking.