Custom HID device HID report descriptor

后端 未结 5 1822
误落风尘
误落风尘 2020-12-24 04:45

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

相关标签:
5条回答
  • 2020-12-24 04:50

    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.

    0 讨论(0)
  • 2020-12-24 05:05

    Here is a link to the Spec Sheet (or "manual") for your reading purposes.

    To answer some of your questions, REPORT_SIZE is specified in bits and REPORT_COUNT can be used to specify how many "Usages" are being reported with the indicated properties. For example, you can set the properties for the X and Y usages and the have the REPORT_COUNT specified as 2 (one for X and one for Y) and then specify the INPUT to add those usages to the report. Then continue on describing other usages.

    Also, don't forget to have the usages byte aligned. Since REPORT_COUNT is specified in bits, it is easy to forget to have usages byte aligned. So if one usage is only 1 bit, then you need to specify that there will be 7 bits in that byte not being used before moving to the next usage if it requires more than 7 bits.

    0 讨论(0)
  • 2020-12-24 05:14

    HID report descriptors are in key-value pairs. To understand them, read the HID specification and chapter 6 is helpful in this case. Where do you find how these values came from and how is it affecting the descriptor? If we change them:

    Key   Value
    --------------------------------------
    0x85, 0x02,     //   REPORT_ID (2)
    0x75, 0x08,     //   REPORT_SIZE (8)
    0x95, 0x40,     //   REPORT_COUNT (64)
    

    @Trick:

    If you are facing a problem to understand REPORT_COUNT (0x95) AND REPORT_SIZE (0x75), then you can imagine it as a two-dimensional array.

    Array[count][size] = Array[4][8] /* Values are in [Bytes][bit] */
    

    Where 4 represents the total number of reports to be send and 8 shows the size of each report.

    That is,

    struct reports
    {
         uint8_t report_1;
         uint8_t report_2;
         uint8_t report_3;
         uint8_t report_4;
    };
    
    0 讨论(0)
  • 2020-12-24 05:15
    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

    0 讨论(0)
  • 2020-12-24 05:16

    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.

    0 讨论(0)
提交回复
热议问题