HID 启动跟踪与获取报表描术符失败的问题
问题
今天改了一下代码,就把从线程中取数改成了直接调用,设备就枚举不成功了~~
让人怀疑人生了啊,看来看去没干啥啊,咋就不成功了
本着不放弃的精神,然后抓包分析
发现SetIdle命令返回异常,就查看REACTOS,源代码~~
Status = Hid_GetDescriptor(DeviceObject,
URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
(PVOID *)&HidDeviceExtension->ConfigurationDescriptor,
&DescriptorLength,
USB_CONFIGURATION_DESCRIPTOR_TYPE,
0,
0);
if (!NT_SUCCESS(Status))
{
//
// failed to obtain device descriptor
//
DPRINT1("[HIDUSB] failed to get device descriptor %x\n", Status);
return Status;
}
//
// now parse the descriptors
//
InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(HidDeviceExtension->ConfigurationDescriptor,
HidDeviceExtension->ConfigurationDescriptor,
-1,
-1,
USB_DEVICE_CLASS_HUMAN_INTERFACE,
-1,
-1);
if (HidDescriptor->bLength == sizeof(HID_DESCRIPTOR) && HidDescriptor->bDescriptorType == HID_HID_DESCRIPTOR_TYPE)
{
//
// found
//
HidDeviceExtension->HidDescriptor = HidDescriptor;
//
// select configuration
//
Status = Hid_SelectConfiguration(DeviceObject);
//
// done
//
DPRINT("[HIDUSB] SelectConfiguration %x\n", Status);
if (NT_SUCCESS(Status))
{
//
// now set the device idle
//
Hid_SetIdle(DeviceObject);
//
// get protocol
//
Hid_GetProtocol(DeviceObject);
return Status;
}
}
从这个源代码来看,当从配置描述符中解析出HID描述符后,做两件事
1.选择配置描述符。这个没啥说的,USB都要做这程流。
2.当配置选择完成后,需要置IDLE,然后再获取HID Protocol
当时看代码的时候,是忽视一个问题就是,这两个函数的并没有返回值,所以对整个USB的初始化流程有任何影响。
所以自己当时也是2,就跟踪SetIdle,并强行IDLE返回SUCCESS.
else if (Urb->UrbHeader.Function == URB_FUNCTION_CLASS_INTERFACE)//idle and protocol
{
KdPrint(("URB_FUNCTION_CLASS_INTERFACE[%x] %x=%08x\n", URB_FUNCTION_CLASS_INTERFACE,Urb->UrbHeader.Function, STATUS_SUCCESS));
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
idle定义如下:
#define USB_SET_IDLE_REQUEST 0xA
#define USB_GET_PROTOCOL_REQUEST 0x3
(urb)->UrbControlVendorClassRequest.Request = (request);
绕了地整一大圈,发现问题依旧,设备还中启动失败.设备管理器中为code 10
,搜索了一下,无果。
转机
太纠结了不是啥好事,最好的排除法就是有个正确的,进行数据对比。
所以拿出大神BUSBOUDD,就和系统驱动抓包对比.
和系统驱动抓包对比,发现SET_IDLE,也返回失败.
Device Phase Data Description Cmd.Phase.Ofs(rep)
------ ----- ------------------------------------------------------------------
20.0 CTL 21 0a 00 00 00 00 00 00 SET IDLE 7.1.0
20.0 USTS c0000004 stall pid 7.2.0
但是和自己的驱动对比,无错。这说明自己强行返回成功了,但如果又改成
IoSkipCurrentIrpStackLocation(Irp);
Status = IoCallDriver(PDODeviceExtension->parent_fdo, Irp);
就要系统驱动的结果一样了
不过,并没有GetProtocol的请求,估计就是windows和Reactos的差别了吧,不纠结了。
问题所在
发现了一个最重大的问题就是获取描述符0X22,失败。
这玩意最终是由类驱动 HidClassFDO_StartDevice -> HidClassFDO_GetDescriptors 实现的。
而且这个IOCTL是发送给接口的。
所以驱动中需要加上
else if (Urb->UrbHeader.Function == URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE
|| Urb->UrbHeader.Function == URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE)
{
if (Urb->UrbControlDescriptorRequest.DescriptorType == 0x22)
{
}
}
修改代码,发现成功启动设备。而之前失败,是由于 HID CLASS创建的FDO在IRP_MN_START_DEVICE中失败的。
未解决的奇怪问题
在调用SET_IDLE_REQUEST
时,下层驱动返回C0000010 STATUS_INVALID_DEVICE_REQUEST
,而BUSBOUND返回显示的是另一个值C0000004
,至于这个值是不是STATUS_INFO_LENGTH_MISMATCH,现在就不清楚了。
3: kd> dt _URB_CONTROL_VENDOR_OR_CLASS_REQUEST 0xffffb40c`e46d88b0
bulkusb!_URB_CONTROL_VENDOR_OR_CLASS_REQUEST
+0x000 Hdr : _URB_HEADER
+0x018 Reserved : (null)
+0x020 TransferFlags : 0
+0x024 TransferBufferLength : 0
+0x028 TransferBuffer : (null)
+0x030 TransferBufferMDL : (null)
+0x038 UrbLink : (null)
+0x040 hca : _URB_HCD_AREA
+0x080 RequestTypeReservedBits : 0x22 '"'
+0x081 Request : 0xa '' //idle
+0x082 Value : 0
+0x084 Index : 0
+0x086 Reserved1 : 0
3: kd> dx -id 0,0,ffffb40c9f0cb2c0 -r1 (*((bulkusb!_URB_HEADER *)0xffffb40ce46d88b0))
(*((bulkusb!_URB_HEADER *)0xffffb40ce46d88b0)) [Type: _URB_HEADER]
[+0x000] Length : 0x88 [Type: unsigned short]
[+0x002] Function : 0x1b [Type: unsigned short]
[+0x004] Status : 0 [Type: long]
[+0x008] UsbdDeviceHandle : 0x0 [Type: void *]
[+0x010] UsbdFlags : 0x0 [Type: unsigned long]