自定义HID设备HID报告描述符

时间:2014-02-06 15:26:34

标签: usb hid

我在生成HID描述符时遇到了一些问题。 我想使用ID1作为输入的简单报告,使用ID2作为64字节数据的输出。

我意识到尽管有rtfming和谷歌搜索,我仍然不知道HID描述符中的某些字段。

有人可以给我一个提示或手册,我可以找到所有描述符字段的含义吗?我所能找到的只是HID-mouse / joistick / keyboard的例子。

例如 - REPORT_SIZE - 是以字节还是以位为单位的大小?为什么还有REPORT_COUNT? 如果报告中有64个字节,LOGICAL_MAXIMUM必须是255或255 * 64?

我是否应该为每个报告编写LOGICAL_MAX和MIN?

或许这个(通过猜测生成)就足够了?

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
};

5 个答案:

答案 0 :(得分:17)

  1. 所有官方文档均可在usb.org上找到。要了解HID报告描述符,您需要阅读HID Information页面上的一些文档。特别是,您应该尝试理解:

    • “HID 1.11的设备类定义”文档 - 描述人机接口设备报告格式
    • “HID使用表1.12”文档 - 描述可以在报告描述符中显示的那些页面中的许多使用情况页面和用法的值

    话虽如此,该文件众所周知地是钝的,需要付出相当大的努力才能消化。

  2. REPORT_SIZE是报告的大小,以位为单位而非字节。将REPORT_SIZE视为字段的宽度(以位为单位),将REPORT_COUNT视为字段数(该宽度)。这在“HID 1.11的设备类定义”文档,第6.2.2.7节“全局项目”中有所阐述,如下所示:

    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. 作为指南,一个合理的(即我没有测试过)报告描述符描述了一个64字节的输入缓冲区(对于REPORT_ID为0x01的主机)和一个64字节的输出缓冲区(来自REPORT_ID为0x02的主机可以如下:

      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
    

    这对应于以下C语言结构定义:

    //--------------------------------------------------------------------------------
    // 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. 您是否应为每个报告指定LOGICAL_MINIMUM和LOGICAL_MAXIMUM?没有。

    有些项目是GLOBAL(这意味着,由于报告描述符是按顺序解析的,它们的值保持不变,直到它们被另一个GLOBAL项目显式更改)而其他项目是LOCAL(这意味着它们的值在每次MAIN时都会重置为默认值遇到项目)。 LOGICAL_MINIMUM和LOGICAL_MAXIMUM都是GLOBAL项,因此如果希望更改值,则只需重新指定它们的值。在我看来,如果项目的官方名称以GLOBAL_,LOCAL_和MAIN_作为前缀,则规范会更清楚,但遗憾的是我们都必须遵守规范。

  5. 以上示例使用SourceForge上的免费工具hidrdd

  6. 进行解码

答案 1 :(得分:6)

正如@aja所述,官方的USB文档相当迟钝。我创建了这个模板(主要是从这个页面的帮助)作为与自定义板通信的简单起点。 HID代码旨在替换虚拟COM端口协议。 HID的一大优势是不需要驱动程序。

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
}

有几点需要注意:

  • 可以轻松添加更多输入/输出对:只需为其提供另一个报告ID即可。每个消息定义由10个字节组成,因此添加起来很简单。
  • 我们跟踪描述符中的字节数,以便计算数组的大小(#define REPORT_DESC_SIZE (55))。

在Windows端,我使用Mike O'Brien's HIDLibrary。 HID报告通常附有报告ID - 在HIDLibrary中,使用HidReport.ReportID字段设置/获取值。在董事会方面,请记住报告的第一个字节是报告ID。

答案 2 :(得分:2)

我已经通过Win7检测到我的自定义隐藏设备(通过猜测和窃取来自示例):

{
    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 */

我不确定它是否能正常工作。会看到。

答案 3 :(得分:0)

以下是Spec Sheet(或&#34;手册&#34;)的链接,供您阅读。

要回答您的一些问题,REPORT_SIZE以比特指定,REPORT_COUNT可用于指定多少&#34; Usages&#34;报告具有指定的属性。例如,您可以设置XY用法的属性,并将REPORT_COUNT指定为2(一个用于X,一个用于Y),然后指定{{1将这些用法添加到报表中。然后继续描述其他用法。

另外,不要忘记使用字节对齐。由于INPUT以位为单位指定,因此很容易忘记使用字节对齐。因此,如果一次使用仅为1位,则需要指定该字节中有7位未被使用,如果需要超过7位,则转移到下一次使用。

答案 4 :(得分:0)

HID报告描述符在键值对中。要理解它们,在这种情况下,请阅读HID规范和第6章。您在哪里找到这些值的来源以及它如何影响描述符?如果我们更改它们。

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

@Trick:         如果您在理解REPORT_COUNT(0x95)和REPORT_SIZE(0x75)时遇到问题         然后,您可以想象为2D阵列。

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

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

即 结构报告 {

 uint8_t report_1;
 uint8_t report_2;
 uint8_t report_3;
 uint8_t report_4;

};