Skip to content

Latest commit

 

History

History
282 lines (211 loc) · 10.3 KB

usage.rst

File metadata and controls

282 lines (211 loc) · 10.3 KB

Usage

For the examples here, we'll use a basic Mouse report descriptor. This example descriptor will contain two relative axes (X and Y) and Three buttons.

There are 2 main formats:

  • HID Report descriptor (a stream of items) a.k.a. low level, this is the set of constructs defined in HID spec;
  • HID Descriptor (a tree of Collections and Values) a.k.a. high level this is a custom language for hrdc, see descriptor language.

Low level stream of item may be represented in three forms:

  • Binary (this is how a device puts it on the wire),
  • Hex (this is binary for humans, also outputted by Linux debugfs for HID devices),
  • Code (a C array of byte values with comments in a textual form).

Library can parse and generate those three formats.

High-level descriptor format actually is a Python script calling relevant library classes and functions, see descriptor language.

Low-level HID report descriptor stream can be converted from one representation to another with hrdc.converter entry point. You may invoke it with python3 -m:

$ python3 -m hrdc.converter -h
usage: converter.py [-h] [-i NAME] [-o NAME] [-O] [input] [output]

Convert a HID report descriptor

positional arguments:
  input                 Input file name
  output                Output file name

optional arguments:
  -h, --help            show this help message and exit
  -i NAME, --input-format NAME
                        Input parser name
  -o NAME, --output-format NAME
                        Output formatter name
  -O, --optimize        Optimize stream

Optionnally, converter may optimize stream, see optimization.

All files default to stdin / stdout.

For instance, let's say we have the following descriptor blob in its hexadecimal format in mouse.hex:

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

It may be converted to C code with the following command line:

$ python3 -m hrdc.converter -i hex -o code mouse.hex

which will output:

0x05, 0x01,                    // UsagePage (desktop)
0x09, 0x02,                    // Usage (Mouse)
0xa1, 0x01,                    // Collection (Application)
0x15, 0x81,                    //     LogicalMinimum (-127)
0x25, 0x7f,                    //     LogicalMaximum (127)
0x75, 0x08,                    //     ReportSize (8)
0x95, 0x02,                    //     ReportCount (2)
0x09, 0x30,                    //     Usage (X)
0x09, 0x31,                    //     Usage (Y)
0x81, 0x06,                    //     Input (Variable|Relative)
0x15, 0x00,                    //     LogicalMinimum (0)
0x25, 0x01,                    //     LogicalMaximum (1)
0x75, 0x01,                    //     ReportSize (1)
0x95, 0x03,                    //     ReportCount (3)
0x05, 0x09,                    //     UsagePage (button)
0x19, 0x01,                    //     UsageMinimum (Button(1))
0x29, 0x03,                    //     UsageMaximum (Button(3))
0x81, 0x02,                    //     Input (Variable)
0xc0,                          // EndCollection

Low-level converter extracts the source format to a stream of Items with attached values. It does not extract any semantic information. The only constraint is to have balanced Collection and End Collection items so that the pretty printer can indent correctly.

Because items are parsed and serialized back, the actual output may not be the exact input. For instance the following input stream:

0x26, 0x01, 0x00,                  // LogicalMaximum (1)

is outputted as:

0x25, 0x01,                        // LogicalMaximum (1)

There is no semantic validation / extraction.

This tool is invoked through python3 -m hrdc.descriptor.extractor. The output file is a Python script containing a high-level representation of the report descriptor.

$ python3 -m hrdc.descriptor.extractor -h
usage: extractor.py [-h] [-i NAME] [input] [output]

Decompile a HID report descriptor

positional arguments:
  input                 Input file name
  output                Output file name

optional arguments:
  -h, --help            show this help message and exit
  -i NAME, --input-format NAME
                        Input parser name

All files default to stdin / stdout.

The same mouse descriptor as above would be extracted with:

$ python3 -m hrdc.descriptor.extractor -i hex mouse.hex mouse.py

In turn, this file can be used as a python script. compile_main directive at end of file will handle a command line, see descriptor language.

High level descriptor compiler takes input from a Python script using the hrdc library components to model a tree of:

  • Collections,
  • Reports,
  • Values.

Values are data items in HID terminology. Values can be:

  • Single absolute/relative items,
  • Named Arrays (surrounding logical collection is implicit),
  • Data arrays,
  • Padding.

A HID mouse device descriptor source code could be:

from hrdc.usage import *
from hrdc.descriptor import *

descriptor = Collection(Collection.Application, desktop.Mouse,
    Value(Value.Input, desktop.X, 8, flags = Value.Variable | Value.Relative, logicalMin = -127, logicalMax = 127),
    Value(Value.Input, desktop.Y, 8, flags = Value.Variable | Value.Relative, logicalMin = -127, logicalMax = 127),
    Value(Value.Input, button.Button(1), 1, logicalMin = 0, logicalMax = 1),
    Value(Value.Input, button.Button(2), 1, logicalMin = 0, logicalMax = 1),
    Value(Value.Input, button.Button(3), 1, logicalMin = 0, logicalMax = 1),
)

if __name__ == "__main__":
    compile_main(descriptor)

Execution of this script will handle a command-line and allow generation of an output HID report descriptor item stream.

$ python3 mouse.py -h
usage: mouse.py [-h] [-o NAME] [-N] [output]

Compile a HID report descriptor

positional arguments:
  output                Output file name

optional arguments:
  -h, --help            show this help message and exit
  -o NAME, --output-format NAME
                        Output formatter name
  -N, --no-optimize     Dont optimize output stream

Please note default behavior is to optimize output stream, contrary to the HID Report Descriptor converter above, because non-optimized stream generated by descriptor serializer is mostly useless.

Here is an example of non-optimized report:

$ python3 mouse.py -N -o code
     0x0b, 0x02, 0x00, 0x01, 0x00,  // Usage (desktop.Mouse)
     0xa1, 0x01,                    // Collection (Application)
     0x0b, 0x30, 0x00, 0x01, 0x00,  //     Usage (desktop.X)
     0x35, 0x00,                    //     PhysicalMinimum (0)
     0x45, 0x00,                    //     PhysicalMaximum (0)
     0x15, 0x81,                    //     LogicalMinimum (-127)
     0x25, 0x7f,                    //     LogicalMaximum (127)
     0x65, 0x00,                    //     Unit (0)
     0x55, 0x00,                    //     UnitExponent (0)
     0x95, 0x01,                    //     ReportCount (1)
     0x75, 0x08,                    //     ReportSize (8)
     0x81, 0x06,                    //     Input (Variable|Relative)
     0x0b, 0x31, 0x00, 0x01, 0x00,  //     Usage (desktop.Y)
     0x35, 0x00,                    //     PhysicalMinimum (0)
     0x45, 0x00,                    //     PhysicalMaximum (0)
     0x15, 0x81,                    //     LogicalMinimum (-127)
     0x25, 0x7f,                    //     LogicalMaximum (127)
     0x65, 0x00,                    //     Unit (0)
     0x55, 0x00,                    //     UnitExponent (0)
     0x95, 0x01,                    //     ReportCount (1)
     0x75, 0x08,                    //     ReportSize (8)
     0x81, 0x06,                    //     Input (Variable|Relative)
     0x0b, 0x01, 0x00, 0x09, 0x00,  //     Usage (button.Button(1))
     0x35, 0x00,                    //     PhysicalMinimum (0)
     0x45, 0x00,                    //     PhysicalMaximum (0)
     0x15, 0x00,                    //     LogicalMinimum (0)
     0x25, 0x01,                    //     LogicalMaximum (1)
     0x65, 0x00,                    //     Unit (0)
     0x55, 0x00,                    //     UnitExponent (0)
     0x95, 0x01,                    //     ReportCount (1)
     0x75, 0x01,                    //     ReportSize (1)
     0x81, 0x02,                    //     Input (Variable)
     0x0b, 0x02, 0x00, 0x09, 0x00,  //     Usage (button.Button(2))
     0x35, 0x00,                    //     PhysicalMinimum (0)
     0x45, 0x00,                    //     PhysicalMaximum (0)
     0x15, 0x00,                    //     LogicalMinimum (0)
     0x25, 0x01,                    //     LogicalMaximum (1)
     0x65, 0x00,                    //     Unit (0)
     0x55, 0x00,                    //     UnitExponent (0)
     0x95, 0x01,                    //     ReportCount (1)
     0x75, 0x01,                    //     ReportSize (1)
     0x81, 0x02,                    //     Input (Variable)
     0x0b, 0x03, 0x00, 0x09, 0x00,  //     Usage (button.Button(3))
     0x35, 0x00,                    //     PhysicalMinimum (0)
     0x45, 0x00,                    //     PhysicalMaximum (0)
     0x15, 0x00,                    //     LogicalMinimum (0)
     0x25, 0x01,                    //     LogicalMaximum (1)
     0x65, 0x00,                    //     Unit (0)
     0x55, 0x00,                    //     UnitExponent (0)
     0x95, 0x01,                    //     ReportCount (1)
     0x75, 0x01,                    //     ReportSize (1)
     0x81, 0x02,                    //     Input (Variable)
     0xc0,                          // EndCollection

Next steps: