Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom HID device HID report descriptor

Tags:

usb

hid

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
};
like image 357
Amomum Avatar asked Feb 06 '14 15:02

Amomum


People also ask

What is HID command?

Human Interface Devices (HID) is a device class definition to replace PS/2-style connectors with a generic USB driver to support HID devices such as keyboards, mice, game controllers, and so on.

What is HID usage page?

Usage Page HID usages are organized into usage pages of related controls. A specific control usage is defined by its usage page, a usage ID, a name, and a description. A usage page value is a 16-bit unsigned value. Examples of usage pages include: Page ID.

What is human interface device?

What Does Human Interface Device (HID) Mean? A human interface device (HID) is a method by which a human interacts with an electronic information system either by inputting data or providing output. A myriad of HID devices exist. The most common are the keyboards, mice, computer speakers, webcams and headsets.

What is HID telephony?

Human Interface Device profile. A Bluetooth profile (mode) for remote control of one device from another, using human-interface hardware such as a keyboard, keypad, joystick, d-pad, etc. Not all Bluetooth devices support HID, and many phones with HID only support control in one direction.


3 Answers

  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.3" 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.

  1. The above example was decoded using a free tool on SourceForge called hidrdd
like image 65
aja Avatar answered Sep 19 '22 09:09

aja


As @aja stated above, the official USB documentation is rather obtuse. I created this template (mostly with help from this page) as a simple starting point to communicate with a custom board. The HID code is intended to replace the Virtual COM Port protocol. The big advantage with HID is that no driver is required.

uint8_t CUSTOM_HID_ReportDesc[REPORT_DESC_SIZE] =
{
   0x06, 0x00, 0xFF,    // Global  Usage page = 0xFF00 (Vendor-defined pages are in the range 0xFF00 through 0xFFFF)
   0x09, 0x01,          // Local   Usage (vendor usage 1)
   0xA1, 0x01,          // Main    Collection (application) begin
   0x15, 0x00,          // Global  Logical minimum (0) applies to each byte
   0x26, 0xFF, 0x00,    // Global  Logical maximum (255)
   0x75, 0x08,          // Global  Report Size (8 bits)

   // 14 bytes | Output message 1 (sent from host to device)
   0x85,  1,            // Global  Report ID (cannot be 0)
   0x98, 64,            // Global  Report Count (number of Report Size fields, in this case 64 bytes)
   0x19, 0x01,          // Local   Usage Minimum (each Report Count must be associated with a Usage)
   0x19, 0x40,          // Local   Usage Maximum
   0x91, 0x02,          // Main    Output (data, array, absolute)

   // 24 bytes | Input message 1 (sent from device to host)
   0x85,  1,            // Global  Report ID (cannot be 0)
   0x98, 64,            // Global  Report Count (number of Report Size fields)
   0x19, 0x01,          // Local   Usage Minimum (each Report Count must be associated with a Usage)
   0x19, 0x40,          // Local   Usage Maximum
   0x81, 0x02,          // Main    Input (data, array, absolute)

   // 34 bytes | Output message 2 (sent from host to device)
   0x85,  2,            // Global  Report ID (cannot be 0)
   0x98, 12,            // Global  Report Count (number of Report Size fields)
   0x19, 0x01,          // Local   Usage Minimum (each Report Count must be associated with a Usage)
   0x19, 0x40,          // Local   Usage Maximum
   0x91, 0x02,          // Main    Output (data, array, absolute)

   // 44 bytes | Input message 2 (sent from device to host)
   0x85,  2,            // Global  Report ID (cannot be 0)
   0x98, 57,            // Global  Report Count (number of Report Size fields)
   0x19, 0x01,          // Local   Usage Minimum (each Report Count must be associated with a Usage)
   0x19, 0x40,          // Local   Usage Maximum
   0x81, 0x02,          // Main    Input (data, array, absolute)

   // 54 bytes | End (add one byte)
   0xC0                 // Main    Collection (application) end
}

A couple of things to note:

  • More input/output pairs can easily be added: just give them another Report ID. Each message definition consists of 10 bytes, so it's simple to add up.
  • We keep track of the number of bytes in the descriptor so that the size of the array can be computed (#define REPORT_DESC_SIZE (55)).

On the Windows side, I use Mike O'Brien's HIDLibrary. HID reports are typically prepended with the Report ID--in HIDLibrary, use the HidReport.ReportID field to set/get the value. On the board side, remember that the first byte of the report will be the Report ID.

like image 31
D. Shinobi Avatar answered Sep 18 '22 09:09

D. Shinobi


I've got my custom HID device detected by Windows 7 with this (built by guess and stealing from examples):

{
    0x05, 0x01,         // USAGE_PAGE (Generic Desktop)
    0x09, 0x00,         // USAGE (Undefined)
    0xa1, 0x01,         // COLLECTION (Application)
    0x15, 0x00,         //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,   //   LOGICAL_MAXIMUM (255)
    0x85, 0x01,         //   REPORT_ID (1)
    0x75, 0x08,         //   REPORT_SIZE (8)
    0x95, 0x40,         //   REPORT_COUNT (64)
    0x09, 0x00,         //   USAGE (Undefined)
    0x81, 0x82,         //   INPUT (Data,Var,Abs,Vol) - to the host
    0x85, 0x02,         //   REPORT_ID (2)
    0x75, 0x08,         //   REPORT_SIZE (8)
    0x95, 0x40,         //   REPORT_COUNT (64)
    0x09, 0x00,         //   USAGE (Undefined)
    0x91, 0x82,         //   OUTPUT (Data,Var,Abs,Vol) - from the host
    0xc0                // END_COLLECTION
}; /* CustomHID_ReportDescriptor */

I'm not sure if it will work properly though. We will see.

like image 39
Amomum Avatar answered Sep 21 '22 09:09

Amomum