Posts

Visualizing HID device reports and report descriptors

Screenshot of USBHID_desc sketch output
Human Interface Device class of USB devices has a unique property – a report descriptor which contains information about data that device is sending to the host as well as data that can be sent to the device. This property allows for variety of devices – keyboards, mice, joysticks, digital scales, uninterruptible power sources, GPS receivers, and even toy missile launchers to belong to a single class – HID. A vendor just needs to pick a usage table which contains controls similar to vendor’s device – every OS has a generic support for HID devices so in most cases specific device driver is not necessary. The report descriptor again makes this possible – it contains definitions or report fields therefore a generic parser can process reports from any arbitrary HID device. However, this generic parser will take too much space on small microcontroller systems such as Arduino due to the amount of constants that needs to be present in the program code.

It shall be noted that a HID report itself is a simple structure of fixed fields and when this structure is known a very lightweight parser can easily be developed. HID development in legacy USB library has stopped at this point; I thought people will just take a look at the spec and write report parser for their device. It soon became evident that very few are actually willing to do this and in rev.2.0 of the library the HID report parsing is semi-automatic – a report descriptor has to be analyzed first using USBHID_desc utility presented in this article and then actual reports for the device can be parsed using library facilities (an article about coding report parsing is here).

Even though report descriptor parsing is moved to a separate utility, it is still a heavy load for Arduino. The code, which at present only contains strings for keyboards, mice and joysticks, won’t fit into 32K Arduino – a Mega or 2560 is necessary. A screenshot of descriptor report output of a typical mouse can be seen on title picture. The explanation follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Usage Page Gen Desktop Ctrls(01)
Usage Mouse
Collection Application
Usage Pointer
Collection Physical
Usage Page Button(09)
Usage Min(01)
Usage Max(03)
Logical Min(00)
Logical Max(01)
Report Size(01)
Report Count(03)
Input(00000010)
Report Size(05)
Report Count(01)
Input(00000001)
Usage Page Gen Desktop Ctrls(01)
Usage X
Usage Y
Usage Wheel
Logical Min(81)
Logical Max(7F)
Report Size(08)
Report Count(03)
Input(00000110)
End Collection
End Collection

Mouse buttons are defined first. We can see that it is input item (line 13). They are buttons (line 6), which is also confirmed by logical min and max (line 9,10) – only two values, 0 and 1, are possible. The report size is one bit (line 11) and number of buttons is 3 (line 12).

The next item (line 16) is a filler for the rest of the first byte. It specifies one (line 15) 5-bit (line 14) report with no minimums or maximums.

The last report (line 25) specifies X-axis, Y-axis and wheel (lines 18-20). 3 reports (line 24) has been specified, one byte each (line 23).

That’s all we need to know about the device in order to communicate to it. For the curious, detailed explanation of these and all other fields in this report can be found in USB specifications listed on usb.org HID page. Device Class Definition and HID Usage Tables are two most important docs.

USBHID_desc also can parse HID reports on the fly. In order to generate some reports I’m going to move my mouse and observe the results. Here’s what happens when I move it vertically:

1
2
3
4
Buf: 00000100
 Mouse Pointer Btn1(00) Btn2(00) Btn3(00)
(00)
 X Y Wheel(00)(01)(00)

Line 1 shows the raw report, 4 bytes in length. Line 2 shows state of the buttons, none of them pressed. Line 3 shows state of the “filler” report; since it doesn’t have usage page assosciated with it, it doesn’t have a name. It never changes, either. Line 4 shows state of X,Y and wheel, the Y axis having moved 1 step. We can also find it easily in the raw report in the third byte.

Next output shows how the report looks like when mouse is moved diagonally. We can see that both X and Y are changed and can check that value for X-axis is output in the second byte of the report.

Buf: 00FDAB00
 Mouse Pointer Btn1(00) Btn2(00) Btn3(00)
(00)
 X Y Wheel(FD)(AB)(00)

The next output demonstrates reports for wheel and button presses. It can be seen that buttons are stored in 3 low bits of the first byte of the report.

Buf: 00000002
 Mouse Pointer Btn1(00) Btn2(00) Btn3(00)
(00)
 X Y Wheel(00)(00)(02)
 
Buf: 01000000
 Mouse Pointer Btn1(01) Btn2(00) Btn3(00)
(00)
 X Y Wheel(00)(00)(00)
 
Buf: 00000000
 Mouse Pointer Btn1(00) Btn2(00) Btn3(00)
(00)
 X Y Wheel(00)(00)(00)
 
Buf: 04000000
 Mouse Pointer Btn1(00) Btn2(00) Btn3(01)
(00)
 X Y Wheel(00)(00)(00)
 
Buf: 00000000
 Mouse Pointer Btn1(00) Btn2(00) Btn3(00)
(00)
 X Y Wheel(00)(00)(00)
 
Buf: 02000000
 Mouse Pointer Btn1(00) Btn2(01) Btn3(00)
(00)
 X Y Wheel(00)(00)(00)
 
Buf: 00000000
 Mouse Pointer Btn1(00) Btn2(00) Btn3(00)
(00)
 X Y Wheel(00)(00)(00)

The last output demonstrates the behaviour of USBHID_desc encountering unsupported device (digital scale). It tries to parse the report descriptor but doesn’t have enough information for human-readable representation of the report. As a result, only raw reports are output. The first report shows empty scale, the second – scale loaded with Arduino Mega/USB Host Shield combo on which USBHID_desc is running (~68g or 2.4oz).

Usage Page Scale(8D)
Usage
Collection Application
Usage
Collection Logical
Report Id(01)
Report Size(08)
Report Count(01)
Logical Min(01)
Logical Max(0C)
Usage
Collection Logical
Usage Min(21)
Usage Max(2A)
Feature(00000000)
End Collection
Usage
Collection Logical
Usage Min(51)
Usage Max(5C)
Feature(00000000)
End Collection
End Collection
Usage
Collection Logical
Report Id(02)
Report Size(01)
Report Count(02)
Logical Min(00)
Logical Max(01)
Usage Min(80)
Usage Max(81)
Output(00000010)
Report Size(06)
Report Count(01)
Output(00000011)
End Collection
Usage
Collection Logical
Report Id(03)
Report Size(08)
Logical Min(01)
Usage
Collection Logical
Logical Max(08)
Usage Min(71)
Usage Max(78)
Input(00000000)
End Collection
Usage
Collection Logical
Logical Max(0C)
Usage Min(51)
Usage Max(5C)
Input(00000000)
End Collection
Logical Min(81)
Logical Max(7F)
Usage
Input(00000010)
Report Size(10)
Logical Min(00)
Logical Max(FFFF0000)
Usage
Input(00000010)
End Collection
Buf: 03040BFF0000
 
 
Buf: 03040BFF2100

USBHID_desc is a useful program designed to help with analyzing reports and report descriptors of HID devices using Arduino Mega or Mega 2560 board and USB Host Shield 2.0. By the end of this week I’m hoping to post an article demonstrating how to use the information – stay tuned!

Oleg.

5 comments to Visualizing HID device reports and report descriptors

  • TreverS

    Oleg,
    I am currently having an issue accessing the scroll wheel bits of the Buf from an arduino sketch. I am able to access the buf in the MouseReportParser::Parse of hidboot.cpp, however this buf appears to only contain the bits for the three mouse pointer buttons, X, and Y values. How would I go about accessing the scroll wheel bits from the full Buf within an arduino sketch?

    • Wheel is not accessible via boot protocol, you need to use report protocol – see joystick article posted next to this one.

  • Thales

    I am using the USBHID_Desc in a barcode scanner and I saw that just changing the first parameter of the method call GetReportDescr I got different reports. For example, calling with 0 i get

    Start
    0000: 06 00 FF 09 01 A1 01 19 00 29 FF 15 00 25 FF 95
    0010: 01 75 08 81 02 19 00 29 FF 15 00 25 FF 95 01 75
    0020: 08 91 02 C0

    Usage Page Undef(00)
    Usage
    Collection Application
    Usage Min(00)
    Usage Max(FF)
    Logical Min(00)
    Logical Max(FF)
    Report Count(01)
    Report Size(08)
    Input(00000010)
    Usage Min(00)
    Usage Max(FF)
    Logical Min(00)
    Logical Max(FF)
    Report Count(01)
    Report Size(08)
    Output(00000010)
    End Collection

    now calling with 1

    Start
    0000: 05 01 09 06 A1 01 05 08 19 01 29 03 15 00 25 01
    0010: 75 01 95 03 91 02 95 05 91 01 05 07 19 E0 29 E7
    0020: 95 08 81 02 75 08 95 01 81 01 19 00 29 91 26 FF
    0030: 00 95 06 81 00 C0
    Usage Page Gen Desktop Ctrls(01)
    Usage Keypad
    Collection Application
    Usage Page LEDs(08)
    Usage Min(01)
    Usage Max(03)
    Logical Min(00)
    Logical Max(01)
    Report Size(01)
    Report Count(03)
    Output(00000010)
    Report Count(05)
    Output(00000001)
    Usage Page Kbrd/Keypad(07)
    Usage Min(E0)
    Usage Max(E7)
    Report Count(08)
    Input(00000010)
    Report Size(08)
    Report Count(01)
    Input(00000001)
    Usage Min(00)
    Usage Max(91)
    Logical Max(FF00)
    Report Count(06)
    Input(00000000)
    End Collection

    The second report makes my barcode scanner look more like a keyboard.

    There is a reason for that?

    I saw it after analyzing the communication of my barcode scanner (USB) with the computer through wireshark software.

    Thanks

    • The reason is you’re asking for the different descriptor. This is only a descriptor though, you will also need to check whether your scanner returns data in the format stated in this descriptor.

      • Thales

        Thanks Oleg. I think there is only one report,

        actually my barcode scanner is behaving strangely. that same sketch (USBHID_desc), the device is being disconnected (USB Task detecting SE0) from time to time, think of 10 to 10 seconds. Could be problem in shield or in my barcode scanner? Thanks again.