Windows系统HID设备的启动过程
在做USB设备固件调试时,经常遇到在设备管理器显示设备启动失败,code:10。
这个启动失败从字面意思上,大家都懂,但实际这个启动到底都执行了什么操作了,又没有人能说的清楚。这里本人结合ReactOS的关于HIDUSB的源代码来给大家解释一下。
在Windows驱动架构中,一个启动的启动是通过IRP_MN_START_DEVICE来实现的,这个IRP执行成功,就表示设备启动成功,这时在设备管理器中会显示这个设备运转正常。所以说,这里的启动实际上是设备被系统的识别并信息验证通过,这些成功后设备就可以进行工作态了。
如果失败,在设备管理器就显示启动失败,代码为10,有时会有相关的信息提示,不过大多数是没有任何错误提示的。
不过遇到此类问题的时候,我们也不需要慌,只要认真的进行抓包分析,就可以大概判断是什么原因了,因为我们已经在很多篇文章中详细地给大家介绍过了USB的识别过程了,大家只需要按照这个识别过程来进行一一判断,结合抓包工作如BUSHOUND,USB总线分析仪等来进行判断。
下面我们来介绍HID设备的启动过程。
在ReactOS的HIDUSB.C文件中,相关的启动IRP操作为IRP_MN_START_DEVICE。其大概原理过程是这样的。
在介绍之前得先说一下,由于这时系统已经为该设备开始加载驱动并启动设备了,故在硬件上设备关于此设备的信息已经收集完成。如第一次插入时的获取设备描述符(最少8字节),设置地址,启用地址。
- 将该IRP传递给PDO,等待硬件启动成功。这一步一般没什么问题,只是USB设备栈的信息传递。
执行Hid_PnpStart来执行真正的设备“启动”
- 获取设备描述符
- 获取18字节的配置描述符,并判断是否成功。
- 解析出配置描述符的总长度wTotalLength之后,再次获取全部的配置描述符,并判断是否成功。
- 从配置描述符中解析出InterfaceClass为USB_DEVICE_CLASS_HUMAN_INTERFACE(0x03)的接口描述符。如果解析不出,则返回失败。详见http://www.usbzh.com/article/detail-221.html
对解析出的接口描述符进行必要的判断,如描述符类型(USB_INTERFACE_DESCRIPTOR_TYPE),长度(sizeof(USB_INTERFACE_DESCRIPTOR))和bInterfaceClass必须为USB_DEVICE_CLASS_HUMAN_INTERFACE(0x03)
- 使用函数Hid_SelectConfiguration选择配置。
- 获取HID设备的接口描述符
- 设备的接口配置,主要是端点配置
- SET_IDLE请求,
- 使用USB_GET_PROTOCOL_REQUEST请求获取1字节的协议号,不进行结果判断。 boot protocol active 0x00 disabled 0x1
另外大家可能会奇怪,这里不获取HID的报告描述符吗?是要获取的,不过不是HIDUSB驱动中,而是在HIDCLASS驱动中。在类驱动的IRP_MN_START_DEVICE中执行HidClassFDO_StartDevice来实现。忽略其它的代码,主要是在HidClassFDO_GetDescriptors函数中。其是通过向HIDUSB发送一个IRP_MJ_INTERNAL_DEVICE_CONTROL的IRP,CTRL CODE为IOCTL_HID_GET_DEVICE_DESCRIPTOR先获取HID描述符,然后再使用IOCTL_HID_GET_REPORT_DESCRIPTOR的CTLR CODE获取报告描述符。
当然获取报告描述符之后,也需要对报告描述符进行解析,则使用的是HidP_GetCollectionDescription函数来实现。解析成功之后,才算HIDCLASS的HidClassFDO_StartDevice函数执行成功。
至于HIDCLASS和HIDUSB之间的关系,可详见http://www.usbzh.com/article/detail-590.html
两者虽然只算一个设备驱动层,不过又有实际上的分层概念。HIDCLASS在HIDUSB之上,所以HIDCLASS经常通过一些内部的个IRP_MJ_INTERNAL_DEVICE_CONTROL的IRP来获取HIDUSB关于设备的特定信息。