仅根据 DESCRIPTOR HID 报告访问 USB 设备数据

Accessing USB device data based only on DESCRIPTOR HID Report

我有一个带 USB 的数字声级计(声压计)GM1356。 Windows 上有一些软件可以处理它,但我没有 CD,也无法在 Internet 上使用。我想要做的是读取它关于 Linux 上当前噪音水平的数据。

我发现已经有一个库允许我使用我知道的语言(ruby、libusb)来执行此操作。在下一步中,我安装了 wireshark 来检查它发送给 pc 的内容。它不会发送太多。我发现的最有趣的数据包是 DESCRIPTOR HID Report。我想知道接下来我应该采取什么步骤来阅读我感兴趣的数据。我如何确定我应该发送哪些请求来获取它?

HID Report
    Global item (Usage)
        Header
            .... ..10 = bSize: 2 bytes (2)
            .... 01.. = bType: Global (1)
            0000 .... = bTag: Usage (0x0)
        Usage page: [Vendor-defined] (0xffa0)
    Local item (Usage)
        Header
            .... ..01 = bSize: 1 byte (1)
            .... 10.. = bType: Local (2)
            0000 .... = bTag: Usage (0x0)
        Usage: [Vendor-defined] (0xffa00001)
    Main item (Collection)
        Header
            .... ..01 = bSize: 1 byte (1)
            .... 00.. = bType: Main (0)
            1010 .... = bTag: Collection (0xa)
        Collection type: Application (0x01)
        Local item (Usage)
            Header
                .... ..01 = bSize: 1 byte (1)
                .... 10.. = bType: Local (2)
                0000 .... = bTag: Usage (0x0)
            Usage: [Vendor-defined] (0xffa00002)
        Main item (Collection)
            Header
                .... ..01 = bSize: 1 byte (1)
                .... 00.. = bType: Main (0)
                1010 .... = bTag: Collection (0xa)
            Collection type: Physical (0x00)
            Global item (Usage)
                Header
                    .... ..10 = bSize: 2 bytes (2)
                    .... 01.. = bType: Global (1)
                    0000 .... = bTag: Usage (0x0)
                Usage page: [Vendor-defined] (0xffa1)
            Local item (Usage)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 10.. = bType: Local (2)
                    0000 .... = bTag: Usage (0x0)
                Usage: [Vendor-defined] (0xffa10003)
            Local item (Usage)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 10.. = bType: Local (2)
                    0000 .... = bTag: Usage (0x0)
                Usage: [Vendor-defined] (0xffa10004)
            Global item (Logical minimum)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0001 .... = bTag: Logical minimum (0x1)
                Logical minimum: 128
            Global item (Logical maximum)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0010 .... = bTag: Logical maximum (0x2)
                Logical maximum: 127
            Global item (Physical minimum)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0011 .... = bTag: Physical minimum (0x3)
                Physical minimum: 0
            Global item (Physical maximum)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0100 .... = bTag: Physical maximum (0x4)
                Physical maximum: 255
            Global item (Report size)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0111 .... = bTag: Report size (0x7)
                Report size: 8
            Global item (Report count)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    1001 .... = bTag: Report count (0x9)
                Report count: 8
            Main item (Input)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 00.. = bType: Main (0)
                    1000 .... = bTag: Input (0x8)
                .... .... 0 = Data/constant: Data
                .... ...1 . = Data type: Variable
                .... ..0. . = Coordinates: Absolute
                .... .0.. . = Min/max wraparound: No Wrap
                .... 0... . = Physical relationship to data: Linear
                ...0 .... . = Preferred state: Preferred State
                ..0. .... . = Has null position: No Null position
                .0.. .... . = [Reserved]: False
                0... .... . = Bits or bytes: Buffered bytes (default, no second byte present)
            Local item (Usage)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 10.. = bType: Local (2)
                    0000 .... = bTag: Usage (0x0)
                Usage: [Vendor-defined] (0xffa10005)
            Local item (Usage)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 10.. = bType: Local (2)
                    0000 .... = bTag: Usage (0x0)
                Usage: [Vendor-defined] (0xffa10006)
            Global item (Logical minimum)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0001 .... = bTag: Logical minimum (0x1)
                Logical minimum: 128
            Global item (Logical maximum)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0010 .... = bTag: Logical maximum (0x2)
                Logical maximum: 127
            Global item (Physical minimum)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0011 .... = bTag: Physical minimum (0x3)
                Physical minimum: 0
            Global item (Physical maximum)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0100 .... = bTag: Physical maximum (0x4)
                Physical maximum: 255
            Global item (Report size)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    0111 .... = bTag: Report size (0x7)
                Report size: 8
            Global item (Report count)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 01.. = bType: Global (1)
                    1001 .... = bTag: Report count (0x9)
                Report count: 8
            Main item (Output)
                Header
                    .... ..01 = bSize: 1 byte (1)
                    .... 00.. = bType: Main (0)
                    1001 .... = bTag: Output (0x9)
                .... .... 0 = Data/constant: Data
                .... ...1 . = Data type: Variable
                .... ..0. . = Coordinates: Absolute
                .... .0.. . = Min/max wraparound: No Wrap
                .... 0... . = Physical relationship to data: Linear
                ...0 .... . = Preferred state: Preferred State
                ..0. .... . = Has null position: No Null position
                .0.. .... . = (Non)-volatile: Non Volatile
                0... .... . = Bits or bytes: Buffered bytes (default, no second byte present)
            Main item (End collection)
                Header
                    .... ..00 = bSize: 0 bytes (0)
                    .... 00.. = bType: Main (0)
                    1100 .... = bTag: End collection (0xc)
        Main item (End collection)
            Header
                .... ..00 = bSize: 0 bytes (0)
                .... 00.. = bType: Main (0)
                1100 .... = bTag: End collection (0xc)

当您解码 HID 描述符时,它将显示数据包格式。不幸的是,在这种情况下,用法页面是 vendor-defined,因此无法准确说明每种用法是如何解释的。

我使用 hidrdd 解码了它(免责声明:我写了它,但它是免费开源的,所以我没有利益冲突)作为:

//--------------------------------------------------------------------------------
// Decoded Application Collection
//--------------------------------------------------------------------------------

/*
06 A0FF      (GLOBAL) USAGE_PAGE         0xFFA0 Vendor-defined 
09 01        (LOCAL)  USAGE              0xFFA00001 <-- Warning: Undocumented usage (document it by inserting 0001 into file FFA0.conf)
A1 01        (MAIN)   COLLECTION         0x01 Application (Usage=0xFFA00001: Page=Vendor-defined, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE
09 02          (LOCAL)  USAGE              0xFFA00002 <-- Warning: Undocumented usage (document it by inserting 0002 into file FFA0.conf)
A1 00          (MAIN)   COLLECTION         0x00 Physical (Usage=0xFFA00002: Page=Vendor-defined, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE
06 A1FF          (GLOBAL) USAGE_PAGE         0xFFA1 Vendor-defined 
09 03            (LOCAL)  USAGE              0xFFA10003 <-- Warning: Undocumented usage (document it by inserting 0003 into file FFA1.conf)
09 04            (LOCAL)  USAGE              0xFFA10004 <-- Warning: Undocumented usage (document it by inserting 0004 into file FFA1.conf)
15 80            (GLOBAL) LOGICAL_MINIMUM    0x80 (-128)  
25 7F            (GLOBAL) LOGICAL_MAXIMUM    0x7F (127)  
35 00            (GLOBAL) PHYSICAL_MINIMUM   0x00 (0)  <-- Info: Consider replacing 35 00 with 34
45 FF            (GLOBAL) PHYSICAL_MAXIMUM   0xFF (-1)  
75 08            (GLOBAL) REPORT_SIZE        0x08 (8) Number of bits per field  
95 08            (GLOBAL) REPORT_COUNT       0x08 (8) Number of fields  
81 02            (MAIN)   INPUT              0x00000002 (8 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap  <-- Error: PHYSICAL_MAXIMUM (-1) is less than PHYSICAL_MINIMUM (0)
09 05            (LOCAL)  USAGE              0xFFA10005 <-- Warning: Undocumented usage (document it by inserting 0005 into file FFA1.conf)
09 06            (LOCAL)  USAGE              0xFFA10006 <-- Warning: Undocumented usage (document it by inserting 0006 into file FFA1.conf)
15 80            (GLOBAL) LOGICAL_MINIMUM    0x80 (-128) <-- Redundant: LOGICAL_MINIMUM is already -128 
25 7F            (GLOBAL) LOGICAL_MAXIMUM    0x7F (127) <-- Redundant: LOGICAL_MAXIMUM is already 127 
35 00            (GLOBAL) PHYSICAL_MINIMUM   0x00 (0) <-- Redundant: PHYSICAL_MINIMUM is already 0 <-- Info: Consider replacing 35 00 with 34
45 FF            (GLOBAL) PHYSICAL_MAXIMUM   0xFF (-1) <-- Redundant: PHYSICAL_MAXIMUM is already -1 
75 08            (GLOBAL) REPORT_SIZE        0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8 
95 08            (GLOBAL) REPORT_COUNT       0x08 (8) Number of fields <-- Redundant: REPORT_COUNT is already 8 
91 02            (MAIN)   OUTPUT             0x00000002 (8 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap  <-- Error: PHYSICAL_MAXIMUM (-1) is less than PHYSICAL_MINIMUM (0)
C0             (MAIN)   END_COLLECTION     Physical  <-- Warning: Physical units are still in effect PHYSICAL(MIN=0,MAX=-1) UNIT(0x,EXP=0)
C0           (MAIN)   END_COLLECTION     Application  <-- Warning: Physical units are still in effect PHYSICAL(MIN=0,MAX=-1) UNIT(0x,EXP=0)
*/

//--------------------------------------------------------------------------------
// Vendor-defined inputReport (Device --> Host)
//--------------------------------------------------------------------------------

typedef struct
{
                                                     // No REPORT ID byte
                                                     // Collection: CA: CP:
  int8_t   VEN_0003;                                 // Usage 0xFFA10003: , Value = -128 to 127, Physical = (Value + 128) x -1 / 255
  int8_t   VEN_0004[7];                              // Usage 0xFFA10004: , Value = -128 to 127, Physical = (Value + 128) x -1 / 255
} inputReport_t;


//--------------------------------------------------------------------------------
// Vendor-defined outputReport (Device <-- Host)
//--------------------------------------------------------------------------------

typedef struct
{
                                                     // No REPORT ID byte
                                                     // Collection: CA: CP:
  int8_t   VEN_0005;                                 // Usage 0xFFA10005: , Value = -128 to 127, Physical = (Value + 128) x -1 / 255
  int8_t   VEN_0006[7];                              // Usage 0xFFA10006: , Value = -128 to 127, Physical = (Value + 128) x -1 / 255
} outputReport_t;

如您所见,上述 HID 描述符存在一些问题(例如,物理最大值 45 FF 为 -1,但我认为它们意味着 255 - 应表示为 46 FF 00)但问题仍然存在它没有告诉您有关用法的含义。顺便说一句,即使是 Wireshark 也没有正确报告逻辑最小值:15 80 是 -128 而不是 128。

我们只能从中看出报告有 8 个字节长,第一个字节似乎是某种 id(好吧,它的用法与其余 7 个字节不同)。

只有供应商的驱动程序知道如何解释报告,但如果在受控条件下获得足够数量的 Wireshark 数据包捕获,您可能能够对可行的解释进行逆向工程。

抱歉,这是我能做的最好的了。

我也买了分贝仪,刚好适合你的型号。我目前正在尝试将此代码移植到 bash 脚本:https://github.com/dobra-noc/gm1356 它适用于我的设备(顺便说一句,它甚至不是 gm1356)我猜它对你有用也是。