Tuesday, 2 January 2018

The Simplest USB HID Report Descriptor

It is often confusing for people including myself who just started to do programming for USB devices. USB protocol is complicated, a book about USB device can easily be hundreds of pages.

HID device could be the starting point for most developers, because it is relatively the simplest to do. Even though it is still difficult to handle and could cost weeks of time to get your first device working.

For me the most confusing concept is the report descriptor, I believe it is also the case for many others. Anyway once you understood it it's not so hard to handle.

HID is designed to work as an input or output device for the operating system, all the report descriptor do is to let the operating system understand what the data it send mean to the OS. For example the key that pressed, or the movement of the mouse. But for us developers we just need to use the HID interface and we use our own software to communicate with the device (unless you are developing a mouse or keyboard), we don't need the OS to understand our data. We do the report descriptor only because it's mandatory in the HID protocol.

So what we need to do is just keep the descriptor as simple as possible. According to the HID specification, the report descriptor must have the following parts:

  • Input
  • Usage
  • Usage Page
  • Logical Minimum
  • Logical Maximum
  • Report Size
  • Report Count

All the other items are optional. To generate a simplest report descriptor, we can use the official descriptor tool by the USB organization:
The order of the items does not matter, but to arrange it in a hierarchical order will make it easy to understand. I'll put the Usage Page first followed by the Usage, then the value range and report size and counts, ended with the Input.

Assuming we are going to transfer 2 bytes of data to the PC, our specific application software will know what the data means and how to use it, so what we need to tell the OS is simply there are 2 bytes of data. The final report descriptor will look like this:
It can be saved as a .h file and be used directly in C code. Use the 'Parse Descriptor' function to check if there's any error before save.

char ReportDescriptor[15] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x00,                    // USAGE (Undefined)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              // LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    // REPORT_SIZE (8)
    0x95, 0x02,                    // REPORT_COUNT (2)
    0x81, 0x02                     // INPUT (Data,Var,Abs)