本文使用 Bus Hound 工具对 USB HID 设备数据包进行分析,并结合官方手册及网上文章进行整理。文中未提到的知识,建议移步参考资源。
以笔者经验,直接阅读协议无法直观理解,最好使用工具抓包,结合协议文档分析真实数据,ONVIF协议如是,IEEE802.3(802.11)如是,USB协议亦如是。

一、前置知识

1.1 描述符

USB 主机是通过各种描述符来识别设备的,有设备描述符,接口描述符,端点描述符,字符描述符,报告描述符等。
回到 HID,USB 主机在请求HID设备的配置描述符时,设备首先返回的描述符为:设备描述符、配置描述符、接口描述符、HID描述符、端点描述符。下文分析的数据,基本就是按照这些顺序。
USB HID 设备是通过报告来传送数据的,报告有:输入报告、输出报告、特性报告。
输入报告:是设备发送给主机的,例如 usb鼠标将鼠标移动和鼠标点击的信息返回给电脑,键盘将按键数据返回给电脑。输入报告是通过中断输入端点输入的。
输出报告:是主机发送给USB设备的,例如键盘上的数字键盘锁定灯和大写字母锁定灯等。报告是一个数据包,里面包含的是所要传送的数据。
报告描述符:是描述一个报告以及报告里面的数据是用来干什么的。通过它,USB 主机可以分析出报告里面的数据所要表达的意思。

1.2 数据格式

请求命令数据共8字节,在 Bus Hound 中,设置数据前面带有 CTL 字样。其格式如图1所示:
在这里插入图片描述
图1
bmRequestType 表示请求类型,从图中看,其组成似乎复杂,但实际分析数据后,发现也就那几个特定的值,如0x80/0x81/0x20/0x21,等等。
wValue 可以理解为2个字节的数值,具体表达的意思,取决于请求类型。注意,USB 使用小端格式,超过2字节需调整顺序
wIndex 类似,但一般表示索引或偏移量。
bRequest 使用1字节表示具体的请求码,如图2所示。
在这里插入图片描述
图2

1.3 其它

bDescriptorType 表示描述符类型,主要有:

1:设备描述符
2:配置描述符
3:字符串描述符
4:接口描述符
5:终端描述符
0x21:HID描述符
0x22:报告描述符
0x23:物理描述符

设备类别bDeviceClass:

0x00:接口描述符中提供类的值
0x02:通信类
0x09:集线器类
0xDC:用于诊断用途的设备类
0xE0:无线通信设备类
0xFF:厂商定义的设备类

接口类别bInterfaceClass:

0x01:音频类
0x02:CDC控制类
0x03:HID人机接口类
0x05:物理类
0x06:图像类
0x07:打印机类
0x08:大数据存储类
0x09:集线器类
0x0A:CDC数据类
0x0B:智能卡类
0x0D:安全类
0xDC:诊断设备类
0xE0:无线控制器类
0xFE:特定应用类(包括红外的桥接器等)
0xFF:厂商定义的设备

HID接口描述符中bInterfaceProtocol:

0:NONE
1:键盘
2:鼠标
3~255:保留

HID 报告类别:

1:输入报告
2:输出报告
3:特征报告(feature report)
04-ff:保留

二、Bus Hound 使用

该软件下载安装过程省略。
点击工具栏的各项图标中进行操作。
“Capture”为抓包界面,右下角有开始和停止按钮。
“Save”可将抓取的数据保存为文件,方便日后分析。
“Settings”需要加大数据包的长度,否则,捕获的数据显示不全,导致误解。笔者所用参数如图3所示:
在这里插入图片描述
图3

“Devices”可查看 USB 设备,并选择需要抓包的设备。如图4所示:
在这里插入图片描述
图4

三、抓包分析

本文主要使用 USB 键盘的数据进行分析。打开工具监听,再插入键盘,得到数据。其抓包如图5所示:
在这里插入图片描述
图5

保存的数据示例如下:

  Device - Device ID (followed by the endpoint for USB devices)
            (31) USB Composite Device
            (34) USB Input Device
            (35) USB Input Device
            (36) HID Keyboard Device
            (37) HID-compliant consumer control device
            (38) HID Keyboard Device
            (39) HID Keyboard Device
  Phase  - Phase Type
            CTL   USB control transfer       
            IN    Data in transfer           
            OUT   Data out transfer          
  Data   - Hex dump of the data transferred
  Descr  - Description of the phase
  Cmd... - Position in the captured data


Device  Phase  Data                      Description       Cmd.Phase.Ofs(rep)
------  -----  ------------------------  ----------------  ------------------
  31.0  CTL    80 06 00 01  00 00 12 00  GET DESCRIPTOR           1.1.0        
  31.0  IN     12 01 10 01  00 00 00 08  ........                 1.2.0        
               6d 02 02 00  01 01 00 00  m.......                 1.2.8        
               00 01                     ..                       1.2.16       

第一部分是描述保存的数据格式,第二部分是真正的数据。第2列 Phase 表示数据的类型,最后一列表示数据的序号(如1.x表示第1次发送/返回的信息,2.x表示第2次,以此类推)。为方便分析 USB 请求流程,下面从开始的数据包进行分析——亦从 USB 设备插入到 USB 主机开始(最后一列从1.x开始)。

3.1 设备描述符

一个USB设备只有一个设备描述符。设备描述符主要记录的信息有:设备所使用的USB协议版本号、设备类型、端点0的最大包大小、厂商ID(VID,由 USB 组织分配)和产品ID(PID)、设备版本号、厂商字符串索引、产品字符串索引、设备序列号索引、可能的配置数等。

数据如下:

  31.0  CTL    80 06 00 01  00 00 12 00  GET DESCRIPTOR           1.1.0        
  31.0  IN     12 01 10 01  00 00 00 08  ........                 1.2.0        
               6d 02 02 00  01 01 00 00  m.......                 1.2.8        
               00 01                     ..                       1.2.16  

请求数据为:

31.0  CTL    80 06 00 01  00 00 12 00 

解析如下:

bmRequestType 80:数据方向从设备端到主机端;标准的请求;USB设备接收
bRequest 06:请求为 GET_DESCRIPTOR
wValue 00 01:(?)
wIndex 00 00:从偏移地址0开始读取设备描述符
wLength 12 00:下一阶段数据的长度为18个字节(小端格式,实际为0x0012,即18)

返回数据字段说明如图6所示。
在这里插入图片描述
图6
数据为:

  31.0  IN     12 01 10 01  00 00 00 08  ........                 1.2.0        
               6d 02 02 00  01 01 00 00  m.......                 1.2.8        
               00 01                     ..                       1.2.16

解析如下:

12 长度为18
01 表示设备描述符
1001 转换后为0110,表示USB协议版本1.1 (注:USB协议版本使用bcd表示)
00 设备类型(USB分配)
00 设备子类
00 协议码
08 端点0的最大包为8(注:仅有8、16、32、64这几个值)
6d02 VID,转换后为0x026d
0200 PID,转换后为0x0002
0101 设备版本号

本文使用的键盘信息如图7所示(主要核对VID和PID):
在这里插入图片描述
图7

3.2 配置描述符

设备描述符里决定了该设备有多少种配置,每种配置都有一个配置描述符。配置描述符主要记录的信息有:配置所包含的接口数、配置的编号、供电方式、是否支持远程唤醒、电流需求量等。
数据如下:

  31.0  CTL    80 06 00 02  00 00 3b 00  GET DESCRIPTOR           3.1.0        
  31.0  IN     09 02 3b 00  02 01 00 a0  ..;.....                 3.2.0        
               32 09 04 00  00 01 03 01  2.......                 3.2.8        
               01 00 09 21  11 01 00 01  ...!....                 3.2.16       
               22 41 00 07  05 81 03 08  "A......                 3.2.24       
               00 0c 09 04  01 00 01 03  ........                 3.2.32       
               00 00 00 09  21 11 01 00  ....!...                 3.2.40       
               01 22 5b 00  07 05 82 03  ."[.....                 3.2.48       
               08 00 0c                  ...                      3.2.56       

(注:2.x数据与3.x有重复,故舍去)

请求:

31.0  CTL    80 06 00 02  00 00 09 00

bmRequestType 80:数据方向从设备端到主机端;标准的请求;USB设备接收
bRequest 06:请求为 GET_DESCRIPTOR
wValue 00 02:?
wIndex 00 00:从偏移地址0开始读取设备描述符
wLength 09 00:下一阶段数据的长度为9个字节(小端格式,实际为0x0009,即9)

返回数据字段说明如图8所示。
在这里插入图片描述
图8

数据:

  31.0  IN     09 02 3b 00  02 01 00 a0 
               32 09 04 00  00 01 03 01 
               01 00 09 21  11 01 00 01 
               22 41 00 07  05 81 03 08 
               00 0c 09 04  01 00 01 03 
               00 00 00 09  21 11 01 00 
               01 22 5b 00  07 05 82 03 
               08 00 0c                
09 本描述符数据长度
02 类型,表示配置描述符
3b00 即003b,表示此次数据长度。包括其它描述符(配置、接口、终端和HID)的总长度
02 本配置支持的接口数量为2
01 设置配置命令(Set Configuration)的参数值
00 字符串描述符索引值,0表示没有
a0 电源和唤醒方式 a0表示总线供电(Bus Powered),远程唤醒(Remote Wakeup)
32:耗电电流,单位为2mA,此值表示50(0x32)*2=100mA

这里要说明的是,与配置描述符一起返回的有另外6个描述符。关键数据为09 04(2个)、09 21(2个)、07 05(2个)。在接下来的小节继续分析。

3.3 接口描述符

配置描述符之后紧接着就是接口描述符,接口描述符指明了接口的类型,对应的端点的数量。
在每个配置描述符中又定义了该配置有多少个接口,每个接口都有一个接口描述符。接口描述符主要记录的信息有:接口的编号、接口的端点数、接口所使用的类、子类、协议等。
本描述符字段说明如图9所示。
在这里插入图片描述
图9

09 04 00 00 01 03 01 01 00    (1)
09 04 01 00 01 03 00 00 00    (2)

09 本描述符长度
04 类型值,表示接口描述符
00 接口数量为0
00 备用的接口描述符编号
01 接口终端数量为1
03 接口类型值,3表示HID(由USB分配)
01 子类型
01 协议码,1表示键盘。2为鼠标,0为无
00 本接口字符串描述符索引

可以看到,这里接口描述符指定的接口类别为 HID。注意,此处显示的是2个描述符数据,(1) 表示是键盘,但(2)却不是,原因为何,暂无深究。

3.4 HID描述符

HID 描述符指定了 HID 规范版本、HID 相关描述符类型(注:物理描述符不是必须的)。
本描述符字段说明如图10所示。
在这里插入图片描述
图10

09 21 11 01 00 01 22 41 00
09 21 11 01 00 01 22 5b 00

09 本描述符长度
21 类别,21为HID描述符
1101 转换后为0111,表示USB协议版本为1.11(bcd码)
00 国家码
01 HID描述符数量为1
22 描述符类型,0x22为报告描述符,0x21为HID描述符,0x23为物理描述符
4100 描述符长度,此处为0x0041

3.5 端点描述符

端点描述符描述了数据的传输类型、传输方向、数据包大小和端点号(也可称为端点地址)等。
本描述符字段说明如图11所示。
在这里插入图片描述
图11

07 05 81 03 08 00 0c
07 05 82 03 08 00 0c

07 本描述符长度
05 类别,5表示端点描述符
81 端点地址,Bit7表示方向,1为输入,0为输出,低4比特为端点号。81为输入的1号,82为输入的2号
03 端口属性,00表示控制,01为同步,02为批量,03为中断
0800 转换后为0x0008,表示最大包长度为8
0c 轮询时间间隔,单位ms

四、报告描述符

HID 特有的描述符共6种,本节分析其中的2种(另外的拿不到数据),捕获的数据来源一款 HID 设备。
请求数据遵循图1格式,具体如图12所示。
在这里插入图片描述
图12
从图中可知,HID 请求类别只有0x21或0xa1两种。6种描述符请求如图13所示。
在这里插入图片描述
图13

4.1 设置报告描述符

请求数据及解析:

  47.0  CTL    21 09 00 03  00 00 20 00  SET REPORT              19.1.0 
21 请求类别 0x21最高比特为0,表示数据方向从主机到设备(即输出)
09 请求,9表示设置报告
0003 低字节为报告ID,其值为0,高字节为报告类别,3表示 feature,1为输入报告,2为输出报告
0000 索引值
2000 转换后为0x0020,表示报告数据长度为32字节

本描述符字段说明如图14所示。
在这里插入图片描述
图14
设置的输出数据示例如下:

  47.0  OUT    55 55 01 4c  61 74 65 01  UU.Late.                19.2.0 
               02 c2 00 00  00 00 00 00  ........                19.2.8 
               00 00 00 00  00 00 00 00  ........                19.2.16
               00 00 00 00  00 00 00 00  ........                19.2.24

4.2 获取报告描述符

请求数据及解析:

  47.0  CTL    a1 01 00 03  00 00 20 00  GET REPORT              20.1.0 
a1 请求类别 0xa1最高比特为1,表示数据方向从设备到主机(即输入)
01 请求,1表示获取报告
后面数据同上

本描述符字段说明如图15所示。
在这里插入图片描述
图15
输入的数据示例如下:

  47.0  IN     55 55 01 4c  61 74 65 01  UU.Late.                20.2.0 
               03 c3 00 00  00 00 00 00  ........                20.2.8 
               00 00 00 00  00 00 00 00  ........                20.2.16
               00 00 00 00  00 00 00 00  ........                20.2.24

在开发中,报告ID是十分重要的,前面示例的ID为0,下面给出给出报告ID为9的数据:

  47.0  CTL    21 09 09 03  00 00 21 00  SET REPORT               3.1.0        
  47.0  OUT    09 55 55 04  30 00 bb 00  .UU.....                 3.2.0        
               f6 00 77 00  00 00 00 00  ..w.....                 3.2.8        
               00 00 00 00  00 00 00 00  ........                 3.2.16       
               00 00 00 00  00 00 00 00  ........                 3.2.24           
  47.0  CTL    a1 01 09 03  00 00 21 00  GET REPORT               5.1.0(3)     
  47.0  IN     09 3c 3c 3c  3c 3c 3c 3c  .<<<<<<<                 5.2.0        
               3c 3c 3c 3c  3c 3c 3c 3c  <<<<<<<<                 5.2.8        
               3c 3c 3c 3c  3c 3c 3c 3c  <<<<<<<<                 5.2.16       
               3c 3c 3c 3c  3c 3c 3c 3c  <<<<<<<<                 5.2.24       
               3c                        <                        5.2.32      

可以看到,ID 为9时,数据前面多了 ID,而 ID 为0时则没有。然而,在使用 hidapi 库设置 feature 报告时,必须额外添加1字节的 ID,否则会失败。获取时,真正数据在 ID 之后,所以要跳过1字节。

五、小结

实际上,本文只是分析了部分类别的数据,限于条件无法分析所有的数据。本着够用的目的,不再对 HID 进行深入研究。
关于报告描述符,看了几次官方文档以及网上的文章,还是摸不着头脑,就笔者不多的经验来看,其中的一个应用,可能是 USB 主机和 USB 设备进行自定义数据的传输方式。即不需要理会官方文档那些复杂的描述,当成一种固定格式的数据来分析,当然,需要双方事前约定好。
感谢网络众多的 USB 文章,本文做了很多参考,另加上个人的整理和理解,但是错误所在难免,不当之处,请方家指正。

六、参考资源

USB之(三)USB描述符和命令(请求)
USB之(四)HID设备类协议
USB描述符解析

2020.2.9 周日 深夜

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐