【整理】Windows,Linux,Mac系统中,USB HID枚举过程的异同

【整理】Windows,Linux,Mac系统中,USB HID枚举过程的异同

【背景】
遇到一个问题,即一个设备,用的Silicon的C8051F347的MCU,其中的USB的驱动的HID部分,在Hid_Class_Request函数中,
把GET_REPORT对应的代码,注释掉了,即没有实现对应的GET_REPORT的功能。
由此,导致设备连接到某台Linux PC中,设备无法响应HID的GET_REPORT,因此无法正常工作。
此问题,已经被别人debug后,找到此处代码的原因了。
而我此处的疑问是,经过查找代码修改的历史记录,发现最原始的代码,是实现了GET_REPORT部分的功能的,但是却在后来某个版本注释掉了此部分代码。
所以很是困惑,为何代码作者会注释掉此部分的代码,因为,看起来,USB的HID的spec:
HID1_11 – USB Device Class Definition for Human Interface Devices(HID).pdf
中,
是有说明的:
7.2.1 Get_Report Request
The Get_Report request allows the host to receive a report via the Control pipe.
没有说明是可选的,就说明此功能,是必须支持的。意味着,按照HID的spec来说,代码中,必须要实现此功能的。

【解决过程】
后来去网上找了版本,最后终于找到有人解释的很清楚了:
Difference on HID enumeration on Win, Linux and Mac
http://www.cygnal.org/ubb/Forum9/HTML/001325.html
简单来说,就是,不同的操作系统,USB HID枚举时候的做法,都不太相同。
而对于此处的问题来说,就是:
Windows中对于USB HID的枚举,不会发送GET_REPOR给设备,即用不到GET_REPOR,所以设备端的驱动,可以不实现此功能;
而Linux中对于USB HID的枚举,会发送GET_REPOR去获取Input的内容,所以设备端的驱动,必须要实现此功能。
其中,Linux的做法,是符合USB的HID的规范的。
但是,现实就是现实,由于Windows的用户足够多,所以,很多驱动开发者,往往就默认地接受了Windows的很多做法,
导致其功能的实现,是按照Windows的做法,而不是协议规范的做法。

【总结】
借用上面那个帖子中所说的,以后写驱动的话,对于USB的特定的枚举过程或顺序,不能完全指望协议,即,也要了解现实世界的做法,即Linux,Windows,Mac中,USB HID的枚举过程,是不太一样的,作为驱动开发者,最好都要清楚,然后尽可能地都支持这些系统。

下面对
Difference on HID enumeration on Win, Linux and Mac
http://www.cygnal.org/ubb/Forum9/HTML/001325.html
进行简单翻译如下,以对不同系统中,USB HID枚举过程的异同做出一些总结:
【Windows,Linux,Mac系统中,USB HID枚举过程的异同】
对于不同的OS之间,枚举顺序的不同,下面以SiLabs HID/Blinky(vendor specific HID设备)为例,一一列出来:
注意,此处列出来的,只是设备连接到PC上之后,PC端的程序还没有运行期间的设备枚举的过程。
1. 总线重置(Bus Reset)
WinXp和Linux都会reset两次。

2. Get_Descriptor( Device ) and Get_Descriptor( Config )
WinXP会发送两次Get_Descriptor

3. SET_IDLE
WinXP是在SET_CONFIGURATION之后,发送SET_IDLE

4. interrupt IN/ GET_REPORT
WinXP和Mac OS,使用中断输入端点(interrupt IN EP)发送IN传输(IN transfer),然后继续执行,而忽略主机端程序。
而Linux发送GET_REPORT( input ),且在主机端程序开始运行之前,不会向发送中断端点( interrupt EP)发送IN传输

上述中,最主要的区别,不是枚举过程的顺序本身,而是枚举之后的interrupt IN/ GET_REPORT。

而虽然USB HID的规范中,已经说明了GET_REPORT(input) 是必须支持的一个功能,但是由于WinXP在枚举期间,不发送GET_REPORT(input),
所以实际上,驱动实现者,常常都不去实现此GET_REPORT(input)的功能。

同时,要注意的是,由于Linux在主机端程序运行之前,是不会向中断端点(interrput EP)发送IN请求(IN request )的,所以,固件(firmware)端,如果期望的是Host端是周期性地会发送请求到中断端点(IN EP)的话,那么程序就会出错了。
即,USB设备端的程序开发者,在写USB驱动程序的时候,要知道上面那些特殊情况,即Host端,即如果是PC端运行的是Linux系统的话,那么在Linux系统程序执行之前,
USB设备端的IN EP,是收不到对应的IN请求的,这点,写驱动的时候,要自己注意,不能自己假定,设备端,会周期性地,从Host端收到IN请求,如果你是按照这样的逻辑实现的驱动,那么遇到Host端运行的是Linux系统的话,由于收不到对应IN请求,所以程序就不是按照你所期望的去运行了,就出错了。

注:原帖有各个OS中枚举过程的详细的列表对比,现粘贴如下,如果格式很乱,就自己去原帖看格式清晰的:

WinXP sp2 | Linux Kernel 2.6.19 | MacOSX 10.4.9-10.4.10
value index len | value index len | value index len
RESET | RESET | RESET
GET_DESCRIPTOR DEVICE 0100 0000 64 | GET_DESCRIPTOR DEVICE 0100 0000 64 | GET_DESCRIPTOR DEVICE 0100 0000
8
RESET | RESET |
SET_ADDRESS 0002 0000 0 | SET_ADDRESS 0003 0000 0 | SET_ADDRESS 0003 0000 0
GET_DESCRIPTOR DEVICE 0100 0000 18 | GET_DESCRIPTOR DEVICE 0100 0000 18 | GET_DESCRIPTOR DEVICE 0100 0000 18

| | GET_DESCRIPTOR STRING 0302 0409 2
| | GET_DESCRIPTOR STRING 0302 0409 10
| | GET_DESCRIPTOR STRING 0301 0409 2
| | GET_DESCRIPTOR STRING 0301 0409 10
GET_DESCRIPTOR CONFIG 0200 0000 9 | GET_DESCRIPTOR CONFIG 0200 0000 9 | GET_DESCRIPTOR CONFIG 0200 0000 4
GET_DESCRIPTOR CONFIG 0200 0000 255 | GET_DESCRIPTOR CONFIG 0200 0000 34 | GET_DESCRIPTOR CONFIG 0200 0000 34

GET_DESCRIPTOR STRING 0300 0000 255 | GET_DESCRIPTOR STRING 0300 0000 255 |
GET_DESCRIPTOR STRING 0302 0409 255 | GET_DESCRIPTOR STRING 0302 0409 255 |
GET_DESCRIPTOR STRING 0300 0000 255 | GET_DESCRIPTOR STRING 0301 0409 255 |
GET_DESCRIPTOR STRING 0302 0409 255 | |
GET_DESCRIPTOR DEVICE 0100 0000 18 | |
GET_DESCRIPTOR CONFIG 0200 0000 9 | |
GET_DESCRIPTOR CONFIG 0200 0000 34 | |
SET_CONFIGURATION 0001 0000 0 | SET_CONFIGURATION 0001 0000 0 | SET_CONFIGURATION 0001 0000 0
SET_IDLE 0000 0000 0 | |
GET_DESCRIPTOR HID REP 2200 0000 147 | GET_DESCRIPTOR HID REP 2200 0000 83 | GET_DESCRIPTOR HID REP 2200 0000 83
| | GET_DESCRIPTOR STRING 0301 0409 2
| | GET_DESCRIPTOR STRING 0301 0409 10
| | GET_DESCRIPTOR STRING 0302 0409 2
| | GET_DESCRIPTOR STRING 0302 0409 10
IN EP=81 | GET_REPORT 0104 0000 2 | IN EP=81
IN EP=81 | GET_REPORT 0105 0000 3 | IN EP=81
SOF IN/NAK | SOF | SOF IN/NAK


发表评论

电子邮件地址不会被公开。 必填项已用*标注

无觅相关文章插件,快速提升流量