Windows10下开发虚拟USB鼠标之枚举子设备失败(STATUS_DEVICE_DATA_ERROR)
之前发过一篇文章:Win10使用虚拟USB鼠标实现自动挂机测试功能(文章地址:http://www.usbzh.com/article/detail-476.html ) 使用的是虚拟驱动实现的一个虚拟USB鼠标,实现了产品的自动测试功能。
生成的设备在设备管理器中如下:
但在开发过程中,并不是一帆风顺的,自己也是踩过很多坑,蓝屏问题,设备枚举失败问题…
今天本人要介绍的是本人踩过的一个坑 - 总线枚举子设备失败的问题。
然后通过设备状态的信息查看错误码为10,表示的是子设备启动时失败,错误信息为:STATUS_DEVICE_DATA_ERROR.
通过以上问题,首先本人查找IRP_MN_START_DEVICEC相关的代码时,发现并没有问题。
然后再往后查找问题,这里到了分别获取设备描述符,配置描述符,字符串描述符和报表描述符相关的代码。
在调试的过程中,发现一个很奇怪的问题,系统在别的描述符时是没有重复获取的,而到了报表描述符时,获取了三次后就就进行了URB_FUNCTION_ABORT_PIPE,然后再进入了删除设备的IRP_MN_REMOVE_DEVICE操作。
所以经过上面的调试分析,可以得出一个结论是系统获取报表描述符的问题。
但是本人仔仔细细的分析了相关的代码,这里并没有什么问题啊,报表描述符返回了56字节的描述符,里面的内容也正常啊,所以也不存在STATUS_DEVICE_DATA_ERROR啊。
UCHAR ReportDesc[46] = {
0x05, 0x01, //USAGE_PAGE (Generic Desktop)
0x09, 0x02, //USAGE (Mouse)
0xa1, 0x01, //COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x08, // REPORT_COUNT (8)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs) 8位按钮
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE(wheel)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x06, // INPUT (Data,Var,Rel) xyz3个这节
0xc0, 0xc0
};
没办法,我们继续调试。因为获取三个报表描述符肯定是不正常的,本人在调试USB HID设备抓包分析时,并没有出现如此的现象,所以这个不正常应该是突破口,但突破口在那里,还需要仔细的调试。
在windbg单步过程中,我注意到了一个很奇怪的现象。
这里的size表的的是我返回的报表描述符的长度,56字节=0x2e,但系统下发的URB中分配的数据长度为0x2e40.
从我们以往的经验来看,系统分配内存时一般是按需分配的,并不会多分配的。再看到这两个数据长度,一个是0x24,一个是0x2e40.你俩咋这么像呢?不会是数据没有对齐的锅吧?
我感紧再回看一下配置描述符,数据填充正常,那么唯一引起问题的就是结构体的定义了。
返回到HID_DESCRIPTOR的定义处:
typedef struct _HID_DESCRIPTOR {
UINT8 bLength;
UINT8 bDescriptorType;
UINT16 bcdHID;
UINT8 bCountryCode;
UINT8 bNumDescriptors;
UINT8 bClassDescriptorType;
UINT16 wClassDescriptorLength;
} HID_DESCRIPTOR, * PHID_DESCRIPTOR;
这个长度由于wClassDescriptorLength占2字节,系统默认会进行补充1字节的数据对齐。
所以修改代码,将整个数据结构进行1字节对齐,再次编译,加载驱动,一切就正常了。