| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- .. SPDX-License-Identifier: GPL-2.0
- ======================================
- Introduction to HID report descriptors
- ======================================
- This chapter is meant to give a broad overview of what HID report
- descriptors are, and of how a casual (non-kernel) programmer can deal
- with HID devices that are not working well with Linux.
- .. contents::
- :local:
- :depth: 2
- .. toctree::
- :maxdepth: 2
- hidreport-parsing
- Introduction
- ============
- HID stands for Human Interface Device, and can be whatever device you
- are using to interact with a computer, be it a mouse, a touchpad, a
- tablet, a microphone.
- Many HID devices work out the box, even if their hardware is different.
- For example, mice can have any number of buttons; they may have a
- wheel; movement sensitivity differs between different models, and so
- on. Nonetheless, most of the time everything just works, without the
- need to have specialized code in the kernel for every mouse model
- developed since 1970.
- This is because modern HID devices do advertise their capabilities
- through the *HID report descriptor*, a fixed set of bytes describing
- exactly what *HID reports* may be sent between the device and the host
- and the meaning of each individual bit in those reports. For example,
- a HID Report Descriptor may specify that "in a report with ID 3 the
- bits from 8 to 15 is the delta x coordinate of a mouse".
- The HID report itself then merely carries the actual data values
- without any extra meta information. Note that HID reports may be sent
- from the device ("Input Reports", i.e. input events), to the device
- ("Output Reports" to e.g. change LEDs) or used for device configuration
- ("Feature reports"). A device may support one or more HID reports.
- The HID subsystem is in charge of parsing the HID report descriptors,
- and converts HID events into normal input device interfaces (see
- Documentation/hid/hid-transport.rst). Devices may misbehave because the
- HID report descriptor provided by the device is wrong, or because it
- needs to be dealt with in a special way, or because some special
- device or interaction mode is not handled by the default code.
- The format of HID report descriptors is described by two documents,
- available from the `USB Implementers Forum <https://www.usb.org/>`_
- `HID web page <https://www.usb.org/hid>`_ address:
- * the `HID USB Device Class Definition
- <https://www.usb.org/document-library/device-class-definition-hid-111>`_ (HID Spec from now on)
- * the `HID Usage Tables <https://usb.org/document-library/hid-usage-tables-14>`_ (HUT from now on)
- The HID subsystem can deal with different transport drivers
- (USB, I2C, Bluetooth, etc.). See Documentation/hid/hid-transport.rst.
- Parsing HID report descriptors
- ==============================
- The current list of HID devices can be found at ``/sys/bus/hid/devices/``.
- For each device, say ``/sys/bus/hid/devices/0003\:093A\:2510.0002/``,
- one can read the corresponding report descriptor::
- $ hexdump -C /sys/bus/hid/devices/0003\:093A\:2510.0002/report_descriptor
- 00000000 05 01 09 02 a1 01 09 01 a1 00 05 09 19 01 29 03 |..............).|
- 00000010 15 00 25 01 75 01 95 03 81 02 75 05 95 01 81 01 |..%.u.....u.....|
- 00000020 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 |...0.1.8..%.u...|
- 00000030 81 06 c0 c0 |....|
- 00000034
- Optional: the HID report descriptor can be read also by
- directly accessing the hidraw driver [#hidraw]_.
- The basic structure of HID report descriptors is defined in the HID
- spec, while HUT "defines constants that can be interpreted by an
- application to identify the purpose and meaning of a data field in a
- HID report". Each entry is defined by at least two bytes, where the
- first one defines what type of value is following and is described in
- the HID spec, while the second one carries the actual value and is
- described in the HUT.
- HID report descriptors can, in principle, be painstakingly parsed by
- hand, byte by byte.
- A short introduction on how to do this is sketched in
- Documentation/hid/hidreport-parsing.rst; you only need to understand it
- if you need to patch HID report descriptors.
- In practice you should not parse HID report descriptors by hand; rather,
- you should use an existing parser. Among all the available ones
- * the online `USB Descriptor and Request Parser
- <http://eleccelerator.com/usbdescreqparser/>`_;
- * `hidrdd <https://github.com/abend0c1/hidrdd>`_,
- that provides very detailed and somewhat verbose descriptions
- (verbosity can be useful if you are not familiar with HID report
- descriptors);
- * `hid-tools <https://gitlab.freedesktop.org/libevdev/hid-tools>`_,
- a complete utility set that allows, among other things,
- to record and replay the raw HID reports and to debug
- and replay HID devices.
- It is being actively developed by the Linux HID subsystem maintainers.
- Parsing the mouse HID report descriptor with `hid-tools
- <https://gitlab.freedesktop.org/libevdev/hid-tools>`_ leads to
- (explanations interposed)::
- $ ./hid-decode /sys/bus/hid/devices/0003\:093A\:2510.0002/report_descriptor
- # device 0:0
- # 0x05, 0x01, // Usage Page (Generic Desktop) 0
- # 0x09, 0x02, // Usage (Mouse) 2
- # 0xa1, 0x01, // Collection (Application) 4
- # 0x09, 0x01, // Usage (Pointer) 6
- # 0xa1, 0x00, // Collection (Physical) 8
- # 0x05, 0x09, // Usage Page (Button) 10
- what follows is a button ::
- # 0x19, 0x01, // Usage Minimum (1) 12
- # 0x29, 0x03, // Usage Maximum (3) 14
- first button is button number 1, last button is button number 3 ::
- # 0x15, 0x00, // Logical Minimum (0) 16
- # 0x25, 0x01, // Logical Maximum (1) 18
- each button can send values from 0 up to including 1
- (i.e. they are binary buttons) ::
- # 0x75, 0x01, // Report Size (1) 20
- each button is sent as exactly one bit ::
- # 0x95, 0x03, // Report Count (3) 22
- and there are three of those bits (matching the three buttons) ::
- # 0x81, 0x02, // Input (Data,Var,Abs) 24
- it's actual Data (not constant padding), they represent
- a single variable (Var) and their values are Absolute (not relative);
- See HID spec Sec. 6.2.2.5 "Input, Output, and Feature Items" ::
- # 0x75, 0x05, // Report Size (5) 26
- five additional padding bits, needed to reach a byte ::
- # 0x95, 0x01, // Report Count (1) 28
- those five bits are repeated only once ::
- # 0x81, 0x01, // Input (Cnst,Arr,Abs) 30
- and take Constant (Cnst) values i.e. they can be ignored. ::
- # 0x05, 0x01, // Usage Page (Generic Desktop) 32
- # 0x09, 0x30, // Usage (X) 34
- # 0x09, 0x31, // Usage (Y) 36
- # 0x09, 0x38, // Usage (Wheel) 38
- The mouse has also two physical positions (Usage (X), Usage (Y))
- and a wheel (Usage (Wheel)) ::
- # 0x15, 0x81, // Logical Minimum (-127) 40
- # 0x25, 0x7f, // Logical Maximum (127) 42
- each of them can send values ranging from -127 up to including 127 ::
- # 0x75, 0x08, // Report Size (8) 44
- which is represented by eight bits ::
- # 0x95, 0x03, // Report Count (3) 46
- and there are three of those eight bits, matching X, Y and Wheel. ::
- # 0x81, 0x06, // Input (Data,Var,Rel) 48
- This time the data values are Relative (Rel), i.e. they represent
- the change from the previously sent report (event) ::
- # 0xc0, // End Collection 50
- # 0xc0, // End Collection 51
- #
- R: 52 05 01 09 02 a1 01 09 01 a1 00 05 09 19 01 29 03 15 00 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
- N: device 0:0
- I: 3 0001 0001
- This Report Descriptor tells us that the mouse input will be
- transmitted using four bytes: the first one for the buttons (three
- bits used, five for padding), the last three for the mouse X, Y and
- wheel changes, respectively.
- Indeed, for any event, the mouse will send a *report* of four bytes.
- We can check the values sent by resorting e.g. to the `hid-recorder`
- tool, from `hid-tools <https://gitlab.freedesktop.org/libevdev/hid-tools>`_:
- The sequence of bytes sent by clicking and releasing button 1, then button 2, then button 3 is::
- $ sudo ./hid-recorder /dev/hidraw1
- ....
- output of hid-decode
- ....
- # Button: 1 0 0 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000000.000000 4 01 00 00 00
- # Button: 0 0 0 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000000.183949 4 00 00 00 00
- # Button: 0 1 0 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000001.959698 4 02 00 00 00
- # Button: 0 0 0 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000002.103899 4 00 00 00 00
- # Button: 0 0 1 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000004.855799 4 04 00 00 00
- # Button: 0 0 0 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000005.103864 4 00 00 00 00
- This example shows that when button 2 is clicked,
- the bytes ``02 00 00 00`` are sent, and the immediately subsequent
- event (``00 00 00 00``) is the release of button 2 (no buttons are
- pressed, remember that the data values are *absolute*).
- If instead one clicks and holds button 1, then clicks and holds button
- 2, releases button 1, and finally releases button 2, the reports are::
- # Button: 1 0 0 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000044.175830 4 01 00 00 00
- # Button: 1 1 0 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000045.975997 4 03 00 00 00
- # Button: 0 1 0 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000047.407930 4 02 00 00 00
- # Button: 0 0 0 | # | X: 0 | Y: 0 | Wheel: 0
- E: 000049.199919 4 00 00 00 00
- where with ``03 00 00 00`` both buttons are pressed, and with the
- subsequent ``02 00 00 00`` button 1 is released while button 2 is still
- active.
- Output, Input and Feature Reports
- ---------------------------------
- HID devices can have Input Reports, like in the mouse example, Output
- Reports, and Feature Reports. "Output" means that the information is
- sent to the device. For example, a joystick with force feedback will
- have some output; the led of a keyboard would need an output as well.
- "Input" means that data come from the device.
- "Feature"s are not meant to be consumed by the end user and define
- configuration options for the device. They can be queried from the host;
- when declared as *Volatile* they should be changed by the host.
- Collections, Report IDs and Evdev events
- ========================================
- A single device can logically group data into different independent
- sets, called a *Collection*. Collections can be nested and there are
- different types of collections (see the HID spec 6.2.2.6
- "Collection, End Collection Items" for details).
- Different reports are identified by means of different *Report ID*
- fields, i.e. a number identifying the structure of the immediately
- following report.
- Whenever a Report ID is needed it is transmitted as the first byte of
- any report. A device with only one supported HID report (like the mouse
- example above) may omit the report ID.
- Consider the following HID report descriptor::
- 05 01 09 02 A1 01 85 01 05 09 19 01 29 05 15 00
- 25 01 95 05 75 01 81 02 95 01 75 03 81 01 05 01
- 09 30 09 31 16 00 F8 26 FF 07 75 0C 95 02 81 06
- 09 38 15 80 25 7F 75 08 95 01 81 06 05 0C 0A 38
- 02 15 80 25 7F 75 08 95 01 81 06 C0 05 01 09 02
- A1 01 85 02 05 09 19 01 29 05 15 00 25 01 95 05
- 75 01 81 02 95 01 75 03 81 01 05 01 09 30 09 31
- 16 00 F8 26 FF 07 75 0C 95 02 81 06 09 38 15 80
- 25 7F 75 08 95 01 81 06 05 0C 0A 38 02 15 80 25
- 7F 75 08 95 01 81 06 C0 05 01 09 07 A1 01 85 05
- 05 07 15 00 25 01 09 29 09 3E 09 4B 09 4E 09 E3
- 09 E8 09 E8 09 E8 75 01 95 08 81 02 95 00 81 01
- C0 05 0C 09 01 A1 01 85 06 15 00 25 01 75 01 95
- 01 09 3F 81 06 09 3F 81 06 09 3F 81 06 09 3F 81
- 06 09 3F 81 06 09 3F 81 06 09 3F 81 06 09 3F 81
- 06 C0 05 0C 09 01 A1 01 85 03 09 05 15 00 26 FF
- 00 75 08 95 02 B1 02 C0
- After parsing it (try to parse it on your own using the suggested
- tools!) one can see that the device presents two ``Mouse`` Application
- Collections (with reports identified by Reports IDs 1 and 2,
- respectively), a ``Keypad`` Application Collection (whose report is
- identified by the Report ID 5) and two ``Consumer Controls`` Application
- Collections, (with Report IDs 6 and 3, respectively). Note, however,
- that a device can have different Report IDs for the same Application
- Collection.
- The data sent will begin with the Report ID byte, and will be followed
- by the corresponding information. For example, the data transmitted for
- the last consumer control::
- 0x05, 0x0C, // Usage Page (Consumer)
- 0x09, 0x01, // Usage (Consumer Control)
- 0xA1, 0x01, // Collection (Application)
- 0x85, 0x03, // Report ID (3)
- 0x09, 0x05, // Usage (Headphone)
- 0x15, 0x00, // Logical Minimum (0)
- 0x26, 0xFF, 0x00, // Logical Maximum (255)
- 0x75, 0x08, // Report Size (8)
- 0x95, 0x02, // Report Count (2)
- 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
- 0xC0, // End Collection
- will be of three bytes: the first for the Report ID (3), the next two
- for the headphone, with two (``Report Count (2)``) bytes
- (``Report Size (8)``), each ranging from 0 (``Logical Minimum (0)``)
- to 255 (``Logical Maximum (255)``).
- All the Input data sent by the device should be translated into
- corresponding Evdev events, so that the remaining part of the stack can
- know what is going on, e.g. the bit for the first button translates into
- the ``EV_KEY/BTN_LEFT`` evdev event and relative X movement translates
- into the ``EV_REL/REL_X`` evdev event".
- Events
- ======
- In Linux, one ``/dev/input/event*`` is created for each ``Application
- Collection``. Going back to the mouse example, and repeating the
- sequence where one clicks and holds button 1, then clicks and holds
- button 2, releases button 1, and finally releases button 2, one gets::
- $ sudo libinput record /dev/input/event1
- # libinput record
- version: 1
- ndevices: 1
- libinput:
- version: "1.23.0"
- git: "unknown"
- system:
- os: "opensuse-tumbleweed:20230619"
- kernel: "6.3.7-1-default"
- dmi: "dmi:bvnHP:bvrU77Ver.01.05.00:bd03/24/2022:br5.0:efr20.29:svnHP:pnHPEliteBook64514inchG9NotebookPC:pvr:rvnHP:rn89D2:rvrKBCVersion14.1D.00:cvnHP:ct10:cvr:sku5Y3J1EA#ABZ:"
- devices:
- - node: /dev/input/event1
- evdev:
- # Name: PixArt HP USB Optical Mouse
- # ID: bus 0x3 vendor 0x3f0 product 0x94a version 0x111
- # Supported Events:
- # Event type 0 (EV_SYN)
- # Event type 1 (EV_KEY)
- # Event code 272 (BTN_LEFT)
- # Event code 273 (BTN_RIGHT)
- # Event code 274 (BTN_MIDDLE)
- # Event type 2 (EV_REL)
- # Event code 0 (REL_X)
- # Event code 1 (REL_Y)
- # Event code 8 (REL_WHEEL)
- # Event code 11 (REL_WHEEL_HI_RES)
- # Event type 4 (EV_MSC)
- # Event code 4 (MSC_SCAN)
- # Properties:
- name: "PixArt HP USB Optical Mouse"
- id: [3, 1008, 2378, 273]
- codes:
- 0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] # EV_SYN
- 1: [272, 273, 274] # EV_KEY
- 2: [0, 1, 8, 11] # EV_REL
- 4: [4] # EV_MSC
- properties: []
- hid: [
- 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
- 0x15, 0x00, 0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31,
- 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, 0x81, 0x06, 0xc0, 0xc0
- ]
- udev:
- properties:
- - ID_INPUT=1
- - ID_INPUT_MOUSE=1
- - LIBINPUT_DEVICE_GROUP=3/3f0/94a:usb-0000:05:00.3-2
- quirks:
- events:
- # Current time is 12:31:56
- - evdev:
- - [ 0, 0, 4, 4, 30] # EV_MSC / MSC_SCAN 30 (obfuscated)
- - [ 0, 0, 1, 272, 1] # EV_KEY / BTN_LEFT 1
- - [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms
- - evdev:
- - [ 1, 207892, 4, 4, 30] # EV_MSC / MSC_SCAN 30 (obfuscated)
- - [ 1, 207892, 1, 273, 1] # EV_KEY / BTN_RIGHT 1
- - [ 1, 207892, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +1207ms
- - evdev:
- - [ 2, 367823, 4, 4, 30] # EV_MSC / MSC_SCAN 30 (obfuscated)
- - [ 2, 367823, 1, 272, 0] # EV_KEY / BTN_LEFT 0
- - [ 2, 367823, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +1160ms
- # Current time is 12:32:00
- - evdev:
- - [ 3, 247617, 4, 4, 30] # EV_MSC / MSC_SCAN 30 (obfuscated)
- - [ 3, 247617, 1, 273, 0] # EV_KEY / BTN_RIGHT 0
- - [ 3, 247617, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +880ms
- Note: if ``libinput record`` is not available on your system try using
- ``evemu-record``.
- When something does not work
- ============================
- There can be a number of reasons why a device does not behave
- correctly. For example
- * The HID report descriptor provided by the HID device may be wrong
- because e.g.
- * it does not follow the standard, so that the kernel
- will not able to make sense of the HID report descriptor;
- * the HID report descriptor *does not match* what is actually
- sent by the device (this can be verified by reading the raw HID
- data);
- * the HID report descriptor may need some "quirks" (see later on).
- As a consequence, a ``/dev/input/event*`` may not be created
- for each Application Collection, and/or the events
- there may not match what you would expect.
- Quirks
- ------
- There are some known peculiarities of HID devices that the kernel
- knows how to fix - these are called the HID quirks and a list of those
- is available in `include/linux/hid.h`.
- Should this be the case, it should be enough to add the required quirk
- in the kernel, for the HID device at hand. This can be done in the file
- `drivers/hid/hid-quirks.c`. How to do it should be relatively
- straightforward after looking into the file.
- The list of currently defined quirks, from `include/linux/hid.h`, is
- .. kernel-doc:: include/linux/hid.h
- :doc: HID quirks
- Quirks for USB devices can be specified while loading the usbhid module,
- see ``modinfo usbhid``, although the proper fix should go into
- hid-quirks.c and **be submitted upstream**.
- See Documentation/process/submitting-patches.rst for guidelines on how
- to submit a patch. Quirks for other busses need to go into hid-quirks.c.
- Fixing HID report descriptors
- -----------------------------
- Should you need to patch HID report descriptors the easiest way is to
- resort to eBPF, as described in Documentation/hid/hid-bpf.rst.
- Basically, you can change any byte of the original HID report
- descriptor. The examples in samples/hid should be a good starting point
- for your code, see e.g. `samples/hid/hid_mouse.bpf.c`::
- SEC("fmod_ret/hid_bpf_rdesc_fixup")
- int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx)
- {
- ....
- data[39] = 0x31;
- data[41] = 0x30;
- return 0;
- }
- Of course this can be also done within the kernel source code, see e.g.
- `drivers/hid/hid-aureal.c` or `drivers/hid/hid-samsung.c` for a slightly
- more complex file.
- Check Documentation/hid/hidreport-parsing.rst if you need any help
- navigating the HID manuals and understanding the exact meaning of
- the HID report descriptor hex numbers.
- Whatever solution you come up with, please remember to **submit the
- fix to the HID maintainers**, so that it can be directly integrated in
- the kernel and that particular HID device will start working for
- everyone else. See Documentation/process/submitting-patches.rst for
- guidelines on how to do this.
- Modifying the transmitted data on the fly
- -----------------------------------------
- Using eBPF it is also possible to modify the data exchanged with the
- device. See again the examples in `samples/hid`.
- Again, **please post your fix**, so that it can be integrated in the
- kernel!
- Writing a specialized driver
- ----------------------------
- This should really be your last resort.
- .. rubric:: Footnotes
- .. [#hidraw] read hidraw: see Documentation/hid/hidraw.rst and
- file `samples/hidraw/hid-example.c` for an example.
- The output of ``hid-example`` would be, for the same mouse::
- $ sudo ./hid-example
- Report Descriptor Size: 52
- Report Descriptor:
- 5 1 9 2 a1 1 9 1 a1 0 5 9 19 1 29 3 15 0 25 1 75 1 95 3 81 2 75 5 95 1 81 1 5 1 9 30 9 31 9 38 15 81 25 7f 75 8 95 3 81 6 c0 c0
- Raw Name: PixArt USB Optical Mouse
- Raw Phys: usb-0000:05:00.4-2.3/input0
- Raw Info:
- bustype: 3 (USB)
- vendor: 0x093a
- product: 0x2510
- ...
|