Reporting as Ordered

The last few weeks, I’ve been showing a simple USB HID device constructed with a Microchip PIC 18F2550. The entire project is downloadable here.

Last time, I talked about how your PIC code has to describe itself to the host PC using descriptors. There was one important descriptor I didn’t mention though. At the very end of usb_descriptors.c is the HID class descriptor. The main purpose of this descriptor is to define the “reports” the device can send. Here’s what Wikipedia has to say:

The USB HID class requires that every device describes how it will communicate with the host device in order to accurately predict and define all current and future human interface devices. During enumeration the device describes how its reports are to be structured so that the host device can properly prepare to receive this information.

The host periodically polls the device’s interrupt IN endpoint during operation. When the device has data to send it forms a report and sends it as a reply to the poll token. Common devices such as keyboards and mice send reports that are well-defined by manufacturers such as Microsoft. When a vendor makes a custom USB HID class device, the reports formed by the device need to match the report description given during enumeration and the driver installed on the host system. In this way it is possible for the USB HID class to be extremely flexible.

The basic idea is that the HID device describes what it can send. That includes the amount of data and the expected range. For example, a three-button mouse might have a button item that can range from 0 to 7 (a bit for each possible switch).

There are some confusing terms used for these descriptors, but it is really simple once you get the code words in your mind. There are also some tools and documents that can help at The HID Descriptor tool on that page gives you a simple interface that can create the HID descriptor (it is a Windows program, but it runs under Wine).

For a simple device like this, however, it is easy to just define a usage page (meaning the type of device, like a mouse) and the associated reports.

Here’s the code for the test device:

//Class specific descriptor - HID
ROM struct{BYTE report[HID_RPT01_SIZE];}hid_rpt01={
    0x06, 0x00, 0xFF,       // Usage Page = 0xFF00
// (Vendor Defined Page 1)
    0x09, 0x01,             // Usage (Vendor Usage 1)
    0xA1, 0x01,             // Collection (Application)
    0x19, 0x00,             //      Usage Minimum (0)
    0x29, 0xFF,             //      Usage Maximum    (FF)
    0x15, 0x00,             //      Logical Minimum
// (data bytes in the report may have minimum value = 0x00)
    0x25, 0xFF,                 //      Logical Maximum
// (data bytes in the report may have maximum value
// = 0x00FF = unsigned 255)
    0x75, 0x08,             //      Report Size: 8-bit field size
    0x95, 0x40,             //      Report Count: Make sixty-four 8-bit fields
// (the next time the parser hits an
// "Input", "Output", or "Feature" item)
    0x81, 0x00,             //      Input (Data, Array, Abs):
// Instantiates input packet fields based on the above
// report size, count, logical min/max, and usage.
    0x19, 0x00,             //      Usage Minimum
    0x29, 0xFF,             //      Usage Maximum       // any byte
    0x91, 0x00,             //      Output (Data, Array, Abs):
// Instantiates output packet fields.
// Uses same report size and count as "Input" fields,
// since nothing new/different was specified to the parser
// since the "Input" item.
    0xC0}                   // End Collection

The second byte in the pairs above are the actual “arguments” for the specific field. For example, 0x19 is the “usage minimum” (the minimum value reported) and the value is 0. You’ll notice that when you set things like counts and minimums, they don’t do anything until you hit an input or an output “feature” (an 0x81 or 0x91). If you have several features (like the input and output above) with the same attributes, you don’t have to set them twice.

The usage range and the logical range allows you to have raw data and engineering units in a report. The physical or usage range is the actual value in the units of interest (for a pointing device, for example, this might be in units of 1/1000 of an inch. The logical units are what the device actually sends, for example 00 to 0xFF. Either unit may have a sign bit.

In this case, the data is just a 64-byte (maximum) buffer of bytes (value 00 to 0xFF). So that’s easy enough.

Earlier, I mentioned how a three button mouse might behave. A mouse report actually does look like this (in C):

   unsigned_byte buttons;
   byte x;
   byte y;

If you want to see a step-by-step breakdown of how that would translate into a descriptor, check out Frank’s Website.

Next time, I’ll show you the actual code that makes the device work and then we’ll wrap up with looking at some host code that talks to this simple device.