Custom HID device HID report descriptor

Amomum picture Amomum · Feb 6, 2014 · Viewed 40.4k times · Source

I have a bit of a problem with generating a HID descriptor. I want to use simple reports with ID1 for input and ID2 for output with 64 bytes of data.

I realized that despite RTFMing and googling I still do not have a clue about some fields in HID descriptor.

What is a hint or a manual where I can find the meaning of all descriptor fields? All I could find was examples for HID-mouse, joystick, and keyboard.

For example - REPORT_SIZE - is it size in bytes or in bits? And why is there also REPORT_COUNT? If I have 64 bytes in the report, must LOGICAL_MAXIMUM be 255 or 255*64?

Should I write LOGICAL_MAX and MIN for every report or not?

Or maybe will this one (generated rather by guessing) suffice?

char ReportDescriptor[39] = {
    0x05, 0x01,         // USAGE_PAGE (Generic Desktop)
    0x09, 0x00,         // USAGE (Undefined)
    0xa1, 0x01,         // COLLECTION (Application)
    0x85, 0x01,         //   REPORT_ID (1)
    0x09, 0x00,         //   USAGE (Undefined)
    0x15, 0x00,         //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,   //   LOGICAL_MAXIMUM (255)
    0x75, 0x40,         //   REPORT_SIZE (64)
    0x96, 0x00, 0x02,   //   REPORT_COUNT (512)
    0x81, 0x82,         //   INPUT (Data,Var,Abs,Vol)
    0x85, 0x02,         //   REPORT_ID (2)
    0x09, 0x00,         //   USAGE (Undefined)
    0x15, 0x00,         //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,   //   LOGICAL_MAXIMUM (255)
    0x75, 0x40,         //   REPORT_SIZE (64)
    0x96, 0x00, 0x02,   //   REPORT_COUNT (512)
    0x91, 0x82,         //   OUTPUT (Data,Var,Abs,Vol)
    0xc0                // END_COLLECTION
};

Answer

aja picture aja · Feb 10, 2014
  1. All the official documentation is available on usb.org. To understand HID Report Descriptors you need to read some of the documents on the HID Information page. In particular, you should try to understand:

    • The "Device Class Definition for HID 1.11" document - which describes the Human Interface Device report format
    • The "HID Usage Tables 1.12" document - which describes the values of many Usage Pages and Usages within those pages that can appear in a Report Descriptor

    Having said that, the documentation is notoriously obtuse and will require considerable effort to digest.

  2. REPORT_SIZE is the size of a report in bits not bytes. Think of REPORT_SIZE as the width of a field (in bits) and the REPORT_COUNT as the number of fields (of that width). This is made clear in the "Device Class Definition for HID 1.11" document, Section 6.2.2.7 "Global Items" as follows:

    Global Item Tag     One-byte Prefix    Description
    Report Size         0111 01 nn         Unsigned integer specifying the size of the report
                                           fields in bits. This allows the parser to build an
                                           item map for the report handler to use. For more
                                           information, see Section 8: Report Protocol.
    
  3. As a guide, a reasonable (i.e I haven't tested it) Report Descriptor that describes a 64-byte input buffer (to the host with a REPORT_ID of 0x01) and a 64-byte output buffer (from the host with a REPORT_ID of 0x02) could be as follows:

      0x06, 0x00, 0xFF,            // (GLOBAL) USAGE_PAGE         0xFF00 Vendor-defined 
      0xA1, 0x01,                  // (MAIN)   COLLECTION         0x01 Application (Usage=0x0: Page=, Usage=, Type=) <-- Warning: USAGE type should be CA (Application)
      0x15, 0x00,                  //   (GLOBAL) LOGICAL_MINIMUM    0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0
      0x26, 0xFF, 0x00,            //   (GLOBAL) LOGICAL_MAXIMUM    0x00FF (255) 
      0x75, 0x08,                  //   (GLOBAL) REPORT_SIZE        0x08 (8) Number of bits per field 
      0x85, 0x01,                  //   (GLOBAL) REPORT_ID          0x01 (1) 
      0x95, 0x40,                  //   (GLOBAL) REPORT_COUNT       0x40 (64) Number of fields 
      0x09, 0x01,                  //   (LOCAL)  USAGE              0xFF000001  
      0x81, 0x02,                  //   (MAIN)   INPUT              0x00000002 (64 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
      0x85, 0x02,                  //   (GLOBAL) REPORT_ID          0x02 (2) 
      0x09, 0x01,                  //   (LOCAL)  USAGE              0xFF000001  
      0x91, 0x02,                  //   (MAIN)   OUTPUT             0x00000002 (64 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
      0xC0,                        // (MAIN)   END_COLLECTION     Application
    

    Which corresponds to the following C-language structure definitions:

    //--------------------------------------------------------------------------------
    // Vendor-defined inputReport 01 (Device --> Host)
    //--------------------------------------------------------------------------------
    
    typedef struct
    {
      uint8_t  reportId;                                 // Report ID = 0x01 (1)
      uint8_t  VEN_VendorDefined0001[64];                // FF00 0001  Value = 0 to 255
    } inputReport01_t;
    
    //--------------------------------------------------------------------------------
    // Vendor-defined outputReport 02 (Device <-- Host)
    //--------------------------------------------------------------------------------
    
    typedef struct
    {
      uint8_t  reportId;                                 // Report ID = 0x02 (2)
      uint8_t  VEN_VendorDefined0001[64];                // FF00 0001  Value = 0 to 255
    } outputReport02_t;
    
  4. Should you specify LOGICAL_MINIMUM and LOGICAL_MAXIMUM for each report? No.

    Some items are GLOBAL (which means that, as the report descriptor is parsed sequentially, their values remain until they are explicitly changed by another GLOBAL item) and others are LOCAL (which means that their values are reset to defaults whenever a MAIN item is encountered). Both LOGICAL_MINIMUM and LOGICAL_MAXIMUM are GLOBAL items, so you only need to re-specify their values if you want the value to change. In my opinion, the specification would have been clearer if the official names for items were prefixed by GLOBAL_, LOCAL_ and MAIN_ but unfortunately we all have to live with the spec as it stands.

  5. The above example was decoded using a free tool on SourceForge called hidrdd