Windows下虚拟USB设备数据的读写请求调试笔记
到现在为止,本人已经在Windows下确切来说是Windows10 x64下开发了以下USB虚拟USB设备:
断断续续,搞了好几个月了,下一步打算继续虚拟别的设备。这里本人对以上的虚拟设备开发的过程中遇到的一个问题做一个总结:
USB虚拟子设备的控制请求如设备在工作时的UVC相机的PTZ控制,HID设备中断数据的收发的时机等。
以上的设备控制或数据收发都是设备在工作状态时,子设备的PDO收到IRP_MJ_INTERNAL_DEVICE_CONTROL控制请求实现的。
该IRP的当前IO_STACK_LOCATION堆栈区域的Parameters.Others.Argument1字段载带的URB字段,是USB设备数据通讯的核心数据结构。
PIO_STACK_LOCATION IoStack = IoGetCurrentIrpStackLocation(Irp);
PURB Urb = (PURB)IoStack->Parameters.Others.Argument1;
而URB根据判断USB控制请求类型URB_FUNCTION_CLASS_INTERFACE用于UVC特定类请求:
struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST {
struct _URB_HEADER Hdr;
PVOID Reserved;
ULONG TransferFlags;
ULONG TransferBufferLength;
PVOID TransferBuffer;
PMDL TransferBufferMDL;
struct _URB *UrbLink; // Reserved
struct _URB_HCD_AREA hca; // Reserved
UCHAR RequestTypeReservedBits;
UCHAR Request;
USHORT Value;
USHORT Index;
USHORT Reserved1;
};
这个结构体包括了常见的USB标准请求和UVC特定类请求,详情可参见相站相关的章节。
而URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER用于中断传输或批量传输。
struct _URB_BULK_OR_INTERRUPT_TRANSFER {
struct _URB_HEADER Hdr;
USBD_PIPE_HANDLE PipeHandle;
ULONG TransferFlags;
ULONG TransferBufferLength;
PVOID TransferBuffer;
PMDL TransferBufferMDL;
struct _URB *UrbLink; // Reserved
struct _URB_HCD_AREA hca; // Reserved
};
URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER用于中断数据收发。
以上的两种请求是在USB虚拟设备在工作时遇到的最常见的数据请求。(这里的UVC的同步传输这里不讨论,其和BULK传输类似,只是换了一套结构体而已)。
关于这里的处理,我见到的有人将它们全部挂入队列中,然后再设备创建时,开启线程串行处理。特别是对URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER这类需要等待数据的请求,使用KeWaitForsingleObject来处理,但是对于如UVC特定类请求又可能会因为这个等待导致如PTZ等控制请求得不到及时响应,导致很慢,很卡..
这里为什么要开启线程处理呢?是因为异步问题。如果不异步会导致比较多奇怪的问题,我遇到的最夸张的是设备子设备不能枚举完全成功。
如我们枚举一个虚拟触摸屏设备,如果在主线程中KeWaitForsingleObject,但是又由TP数据是中断方式输入给主机的,系统会下发IPR给子设备,会导致HID输入设备树下无法再枚举触摸屏设备。
所以对于这里的请求,一般是将IRP Pending,挂入队列,由于我的是虚拟的,当应用下发数据时,再获取IRP,完成该IPR.这样实现了所谓的异步。