요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. 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
...
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
HID report descriptor 개요
1-64이 장은 HID report descriptor가 무엇인지 폭넓게 설명하고, Linux에서 제대로 동작하지 않는 HID device를 일반적인 비-kernel programmer가 다루는 방법을 소개합니다. 이어지는 상세 parsing 문서는 `hidreport-parsing`입니다.
HID는 Human Interface Device의 약자이며 mouse, touchpad, tablet, microphone처럼 computer와 상호작용하는 여러 device를 뜻합니다.
Hardware가 달라도 많은 HID device는 별도 설정 없이 동작합니다. Mouse는 button 수, wheel 유무, movement sensitivity가 서로 다르지만 1970년 이후의 모든 model마다 kernel 전용 code를 만들지 않아도 대부분 동작합니다.
그 이유는 현대 HID device가 고정 byte 집합인 HID report descriptor로 capability를 알리기 때문입니다. Descriptor는 device와 host 사이에서 주고받을 수 있는 HID report와 report의 각 bit 의미를 정확히 설명합니다. 예를 들어 report ID 3의 bit 8~15가 mouse X 좌표 변화량이라고 지정할 수 있습니다.
HID report 자체는 추가 metadata 없이 실제 data 값만 담습니다. Device에서 host로 오는 input event는 Input Report, host에서 device로 LED 등을 바꾸는 것은 Output Report, device 설정은 Feature Report입니다. Device는 report를 하나 이상 지원할 수 있습니다.
HID subsystem은 descriptor를 parse하고 HID event를 일반 input device interface로 변환합니다. Transport 설명은 `Documentation/hid/hid-transport.rst`에 있습니다.
Device가 잘못 동작하는 원인은 제공한 descriptor가 틀렸거나, device를 특별한 방식으로 다뤄야 하거나, 특수 device·interaction mode를 기본 code가 처리하지 못하기 때문일 수 있습니다.
Descriptor 형식은 USB Implementers Forum의 HID USB Device Class Definition, 이후 HID Spec이라 부르는 문서와 HID Usage Tables, 이후 HUT라 부르는 문서에 정의됩니다.
HID subsystem은 USB, I2C, Bluetooth 등 여러 transport driver를 처리할 수 있습니다.
Capability metadata와 runtime data를 구분합니다.
다양한 hardware가 공통 kernel code를 사용하는 이유입니다.
.. 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.
Descriptor 읽기와 parser 선택
65-115현재 HID device 목록은 `/sys/bus/hid/devices/`에 있습니다. 예시 device `/sys/bus/hid/devices/0003\:093A\:2510.0002/`의 `report_descriptor`를 `hexdump -C`로 읽을 수 있습니다.
$ 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
선택적으로 hidraw driver에 직접 접근해 descriptor를 읽을 수도 있습니다.
HID Spec은 descriptor의 기본 구조를 정의하고, HUT는 application이 HID report의 data field 목적과 의미를 식별할 수 있도록 해석 가능한 상수를 정의합니다.
각 entry는 최소 두 byte로 정의됩니다. 첫 byte는 뒤따르는 값의 type을 나타내며 HID Spec에 설명되어 있고, 둘째 byte는 실제 값을 담으며 HUT에 설명되어 있습니다.
원칙적으로 descriptor를 byte 단위로 수동 parse할 수 있습니다. 간단한 방법은 `Documentation/hid/hidreport-parsing.rst`에 있으며 descriptor를 patch해야 할 때만 이해하면 됩니다.
실제로는 수동 parse 대신 기존 parser를 사용해야 합니다. Online USB Descriptor and Request Parser, 상세하고 다소 장황한 설명을 제공하는 `hidrdd`, raw HID report 기록·재생과 HID device debugging·replay를 제공하는 `hid-tools`가 있습니다.
`hid-tools`는 완전한 utility set이며 Linux HID subsystem maintainer가 활발히 개발합니다. 다음 절은 이 도구의 `hid-decode`로 mouse descriptor를 해석합니다.
문서가 권장하는 도구와 쓰임새입니다.
Sysfs에서 읽은 byte를 의미 있는 item으로 바꿉니다.
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)::
Mouse descriptor를 item별로 해독
116-197`hid-decode` 출력은 먼저 Generic Desktop usage page, Mouse usage와 Application Collection을 선언하고, 그 안에서 Pointer usage의 Physical Collection을 엽니다. 이어 Button usage page로 전환합니다.
Usage Minimum 1과 Usage Maximum 3은 첫 button이 1, 마지막 button이 3임을 뜻합니다. Logical Minimum 0과 Logical Maximum 1은 각 button이 0 또는 1의 binary 값을 보냄을 뜻합니다.
Report Size 1은 button 하나가 정확히 1bit임을 나타내고 Report Count 3은 세 button에 대응하는 bit 세 개가 있음을 나타냅니다.
`Input (Data,Var,Abs)`는 constant padding이 아닌 실제 data이고, 각 bit가 하나의 variable을 나타내며 값이 relative가 아니라 absolute임을 뜻합니다. 자세한 flag는 HID Spec 6.2.2.5를 참조합니다.
다음 Report Size 5, Report Count 1, `Input (Cnst,Arr,Abs)`는 byte 경계를 맞추는 5bit padding 하나를 정의합니다. Constant 값이므로 무시할 수 있습니다.
Generic Desktop page로 돌아와 X, Y, Wheel usage를 선언합니다. 각 값의 logical range는 -127~127이고 Report Size는 8bit, Report Count는 3이므로 X·Y·wheel 각각 1byte입니다.
이 field의 `Input (Data,Var,Rel)`은 이전 report에서의 변화량을 나타내는 relative data입니다. 마지막 두 `End Collection`이 Physical과 Application collection을 닫습니다.
`hid-decode`는 끝에 descriptor의 52byte raw sequence와 device metadata도 출력합니다.
Descriptor가 정의한 button·axis bit 배치입니다.
Global·local item이 다음 main item의 field를 정의합니다.
$ ./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
4-byte mouse report의 실제 값
198-247이 descriptor에 따르면 mouse input은 4byte로 전송됩니다. 첫 byte는 button으로 3bit를 사용하고 5bit는 padding이며, 나머지 세 byte는 각각 X, Y, wheel 변화량입니다.
실제 event마다 mouse는 4byte report를 보냅니다. `hid-tools`의 `hid-recorder`로 전송 값을 확인할 수 있습니다.
Button 1을 누르면 `01 00 00 00`, 놓으면 `00 00 00 00`이 나옵니다. Button 2는 `02 00 00 00`, button 3은 `04 00 00 00`입니다.
Button 2 click 직후의 `00 00 00 00`은 button 2 release입니다. Button data가 absolute이므로 bit가 0이면 그 시점에 button이 눌리지 않았음을 뜻합니다.
Button 1을 누른 채 button 2를 누르면 `03 00 00 00`으로 두 bit가 함께 설정됩니다. 그 뒤 button 1만 놓으면 `02 00 00 00`이 되어 button 2는 계속 active이고, 마지막 button 2 release는 `00 00 00 00`입니다.
Button 1 press: 01 00 00 00
Button 1 + 2: 03 00 00 00
Button 1 release: 02 00 00 00
Button 2 release: 00 00 00 00
Button 3 press: 04 00 00 00
첫 byte의 absolute button state를 정리했습니다.
Absolute button bitmap이 누적 state를 표현합니다.
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.
Report type, Collection, Report ID와 evdev mapping
248-332Mouse 예제처럼 device는 Input Report를 가질 수 있고 Output Report와 Feature Report도 가질 수 있습니다. Output은 host에서 device로 보내는 정보로 force-feedback joystick과 keyboard LED가 예입니다. Input은 device에서 오는 data입니다.
Feature는 end user가 소비하기 위한 것이 아니라 device configuration option을 정의합니다. Host가 query할 수 있으며 `Volatile`로 선언된 feature는 host가 변경해야 합니다.
하나의 device는 data를 Collection이라는 독립된 논리 집합으로 묶을 수 있습니다. Collection은 중첩 가능하고 여러 type이 있으며 HID Spec 6.2.2.6에 자세히 설명되어 있습니다.
서로 다른 report는 바로 뒤따르는 report 구조를 식별하는 숫자인 Report ID field로 구분됩니다. Report ID가 필요하면 모든 report의 첫 byte로 전송됩니다. Mouse 예처럼 report 하나만 지원하는 device는 ID를 생략할 수 있습니다.
제시된 긴 descriptor를 parse하면 Report ID 1과 2의 Mouse Application Collection 두 개, Report ID 5의 Keypad Application Collection, Report ID 6과 3의 Consumer Controls Application Collection 두 개가 있습니다. 같은 Application Collection이 서로 다른 Report ID를 가질 수도 있습니다.
Report data는 ID byte로 시작한 뒤 그 ID의 구조에 맞는 정보가 옵니다.
마지막 Consumer Control은 Consumer usage page, Consumer Control Application Collection, Report ID 3, Headphone usage를 선언합니다. Logical range는 0~255, Report Size는 8, Report Count는 2인 non-volatile Feature입니다.
따라서 이 report는 세 byte입니다. 첫 byte는 Report ID 3이고 다음 두 byte는 headphone field 두 개이며, 각각 0~255 범위입니다.
Device가 보내는 모든 Input data는 stack의 나머지 부분이 이해할 수 있도록 대응하는 evdev event로 변환되어야 합니다. 첫 button bit는 `EV_KEY/BTN_LEFT`, 상대 X movement는 `EV_REL/REL_X`로 변환됩니다.
예제 descriptor를 parse한 결과입니다.
첫 byte가 뒤따르는 payload 구조를 선택합니다.
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".
Application Collection별 evdev event
333-413Linux는 각 Application Collection마다 `/dev/input/event*` 하나를 만듭니다.
Mouse 예에서 `libinput record /dev/input/event1`을 실행하면 device metadata, 지원 event type·code, HID descriptor, udev property와 실제 evdev event sequence를 기록합니다.
예제 device는 `EV_SYN`, `EV_KEY`, `EV_REL`, `EV_MSC`를 지원합니다. Key code는 `BTN_LEFT`, `BTN_RIGHT`, `BTN_MIDDLE`, relative code는 `REL_X`, `REL_Y`, `REL_WHEEL`, `REL_WHEEL_HI_RES`, scan code는 `MSC_SCAN`입니다.
기록된 sequence는 left press, right press, left release, right release입니다. 각 변화에는 `MSC_SCAN`, 해당 `EV_KEY` 값 1 또는 0과 `SYN_REPORT`가 함께 기록됩니다.
Device는 `ID_INPUT=1`, `ID_INPUT_MOUSE=1` udev property를 가지며 예제의 device name은 `PixArt HP USB Optical Mouse`입니다.
System에 `libinput record`가 없으면 `evemu-record`를 사용할 수 있습니다.
Descriptor에서 생성된 주요 event code입니다.
Button report가 userspace event frame으로 바뀝니다.
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``.
오동작 원인과 HID quirk
414-457Device가 올바르게 동작하지 않는 데는 여러 이유가 있습니다.
Device가 제공한 descriptor가 표준을 따르지 않아 kernel이 의미를 해석하지 못할 수 있습니다. 또는 descriptor가 device가 실제로 보내는 data와 일치하지 않을 수 있으며, raw HID data를 읽어 이를 검증할 수 있습니다. Descriptor에 특정 quirk가 필요할 수도 있습니다.
그 결과 각 Application Collection에 `/dev/input/event*`가 만들어지지 않거나 event가 예상과 다를 수 있습니다.
Kernel이 수정 방법을 알고 있는 HID device의 알려진 특이점을 HID quirk라 부르며 목록은 `include/linux/hid.h`에 있습니다.
해당하는 quirk가 있다면 대상 HID device를 위해 kernel에 quirk를 추가하면 됩니다. 위치는 `drivers/hid/hid-quirks.c`이며 file의 기존 항목을 보면 방법을 파악할 수 있습니다.
현재 정의된 quirk 목록은 `include/linux/hid.h`의 kernel-doc에서 문서에 포함됩니다.
USB device quirk는 `modinfo usbhid`에 나온 module load parameter로 지정할 수도 있습니다. 그러나 올바른 수정은 `hid-quirks.c`에 넣고 upstream에 제출해야 합니다.
Patch 제출 지침은 `Documentation/process/submitting-patches.rst`에 있습니다. 다른 bus의 quirk는 반드시 `hid-quirks.c`에 들어가야 합니다.
증상과 확인 지점을 연결합니다.
Local 확인에서 upstream 반영까지의 경로입니다.
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.
HID-BPF 또는 kernel에서 descriptor 수정
458-490HID report descriptor를 patch해야 한다면 가장 쉬운 방법은 `Documentation/hid/hid-bpf.rst`에 설명된 eBPF를 사용하는 것입니다.
원본 descriptor의 어떤 byte든 바꿀 수 있습니다. `samples/hid` 예제가 시작점이며 `samples/hid/hid_mouse.bpf.c`는 `fmod_ret/hid_bpf_rdesc_fixup` program에서 특정 byte를 교체합니다.
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;
}
Kernel source에서 직접 수정할 수도 있습니다. 간단한 예는 `drivers/hid/hid-aureal.c`, 조금 더 복잡한 예는 `drivers/hid/hid-samsung.c`입니다.
HID manual 탐색과 descriptor hex number의 정확한 의미에 도움이 필요하면 `Documentation/hid/hidreport-parsing.rst`를 확인합니다.
어떤 해결책을 만들든 HID maintainer에게 fix를 제출해야 합니다. 그래야 kernel에 직접 통합되어 같은 HID device가 다른 사용자에게도 동작합니다. 제출 방법은 `Documentation/process/submitting-patches.rst`를 따릅니다.
검증·배포 범위에 따른 방법입니다.
잘못된 byte를 수정하고 공유하는 과정입니다.
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.
전송 data 수정과 specialized driver
491-506eBPF를 사용하면 device와 교환하는 data도 실행 중에 수정할 수 있습니다. 관련 예제는 다시 `samples/hid`에서 확인합니다.
이 수정도 kernel에 통합될 수 있도록 반드시 게시하고 제출해야 합니다.
Specialized driver 작성은 정말 마지막 수단이어야 합니다.
유지보수 범위가 작은 방법부터 적용합니다.
필요한 범위만 확장하고 upstream으로 공유합니다.
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.
Hidraw descriptor read 예제
507-524Hidraw 사용법은 `Documentation/hid/hidraw.rst`와 예제 `samples/hidraw/hid-example.c`를 참조합니다.
같은 mouse에서 `hid-example`을 실행하면 report descriptor size `52`, 전체 descriptor byte, raw name과 physical path, USB bus type, vendor `0x093a`, product `0x2510`을 출력합니다.
$ sudo ./hid-example
Report Descriptor Size: 52
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
Footnote 예제에서 확인하는 값입니다.
Kernel input mapping 전의 raw 정보를 확인합니다.
.. 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
...
요약·해설
hidintro.rst:1-524HID report descriptor는 runtime report의 각 bit를 usage와 값 범위, 방향, absolute·relative semantics에 연결합니다. 이 문서는 mouse descriptor를 byte 단위로 해석하고 raw report, evdev event, quirk와 HID-BPF 수정까지 이어지는 실전 조사 절차를 제공합니다.
Source와 핵심 도구·개념입니다.
Descriptor와 실제 data를 함께 검증합니다.