USB复合设备-UVC摄像头HID设备共存的设计实现
通常做USB设备的开发,我们做的都是单一的功能设备。这种单一的功能设备只实现某种特定的功能,如只实现一个HID鼠标或键盘,只实现一个USB存储功能,或再复杂一点只实现一个UVC音频麦克风和扬声器功能或一个UVC摄像头功能。
但我们在一般的市场上看到的设备通常不只实现一种功能,如UVC摄像头功能还提供一个自定义的HID,用于对此摄像头的功能扩展,或者更有甚至用于该摄像头的固件升级。
对于这种复杂的混合设备,在与我们的PC机连接后,Windows通常使用一个叫做USB通用驱动(usbccgp.sys)来进行驱动,设备的名称叫做“USB Composite Device”,这个驱动下再使用某种规则枚举出我们的功能设备如摄像头、HID设备。
如这里本人手中的一个UVC摄像头,其在Windows下的设备树管理器中的结构如下:
可以看到,这个设备下有两个设备,一个为UVC摄像头,另一个自定义HID设备。
那么这种复合设备是怎么实现的呢?
答案是多功能接口的实现。我们知道,USB设备的功能功分是由接口描述符来界定的,对于大部分简单的USB设备,只要出现一个接口描述符,其就代表一个USB设备功能。
如我们可以使用两个接口分别实现键盘、鼠标,这们当这种设备接入PC机后,系统会对这个USB硬件加载USB通用驱动,然后再由USB通用驱动分别枚举出USB键盘和USB鼠标功能。当然以上这种情况只适用于一般的简单设备,对于复杂混合设备就有一点不同了。
如对于UVC摄像头和UAC音频设备,这种设备的功能实现是需要多接口联合才能实现其需要的功能,如对于UVC设备其包括视频控制接口VC和视频流接口VS,对于UAC音频也有其对应的音频流控制AC和音频流接口AS.而这种复合设备功能的组织就是使用一个叫做接口关联描述符来实现的。
如对于以上设备,其各子设备的硬件ID如下:
- USB\VID_1234&PID_0100&REV_0409 //USB Composite Device
- USB\VID_1234&PID_0100&REV_0409&MI_01 //CAMERA
- USB\VID_1234&PID_0100&REV_0409&MI_00 //HID
- HID\VID_1234&PID_0100&REV_0409&MI_00 //HID
这里硬件ID后的MI_xx其就代表的是其功能接口的ID,对应于配置描述符中的接口描述符ID.
这的的配置描述符结构建组织如下:
关于其它信息如端点描述符这里省略掉。
我们再通过usbtreeview工具打开设备,查看其配置描述符:
这里只选报配置描述符的部分内容:
---------------- Interface Descriptor -----------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x04 (Interface Descriptor)
bInterfaceNumber : 0x00
bAlternateSetting : 0x00
bNumEndpoints : 0x02 (2 Endpoints)
bInterfaceClass : 0x03 (HID - Human Interface Device)
bInterfaceSubClass : 0x00 (None)
bInterfaceProtocol : 0xFF (reserved)
iInterface : 0x08 (String Descriptor 8)
Data (HexDump) : 09 04 00 00 02 03 00 FF 08 .........
------------------- HID Descriptor --------------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x21 (HID Descriptor)
bcdHID : 0x0101 (HID Version 1.01)
bCountryCode : 0x00 (00 = not localized)
bNumDescriptors : 0x01
Data (HexDump) : 09 21 01 01 00 01 22 28 00 .!...."(.
Descriptor 1:
bDescriptorType : 0x22 (Class=Report)
wDescriptorLength : 0x0028 (40 bytes)
Error reading descriptor : ERROR_GEN_FAILURE
----------------- Endpoint Descriptor -----------------
bLength : 0x07 (7 bytes)
bDescriptorType : 0x05 (Endpoint Descriptor)
bEndpointAddress : 0x81 (Direction=IN EndpointID=1)
bmAttributes : 0x03 (TransferType=Interrupt)
wMaxPacketSize : 0x01F3
Bits 15..13 : 0x00 (reserved, must be zero)
Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet)
Bits 10..0 : 0x1F3 (499 bytes per packet)
bInterval : 0x01 (1 ms)
Data (HexDump) : 07 05 81 03 F3 01 01 .......
----------------- Endpoint Descriptor -----------------
bLength : 0x07 (7 bytes)
bDescriptorType : 0x05 (Endpoint Descriptor)
bEndpointAddress : 0x01 (Direction=OUT EndpointID=1)
bmAttributes : 0x03 (TransferType=Interrupt)
wMaxPacketSize : 0x01F3
Bits 15..13 : 0x00 (reserved, must be zero)
Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet)
Bits 10..0 : 0x1F3 (499 bytes per packet)
bInterval : 0x01 (1 ms)
Data (HexDump) : 07 05 01 03 F3 01 01 .......
------------------- IAD Descriptor --------------------
bLength : 0x08 (8 bytes)
bDescriptorType : 0x0B
bFirstInterface : 0x01
bInterfaceCount : 0x02
bFunctionClass : 0x0E (Video)
bFunctionSubClass : 0x03 (Video Interface Collection)
bFunctionProtocol : 0x00 (PC_PROTOCOL_UNDEFINED protocol)
iFunction : 0x0A (String Descriptor 10)
Data (HexDump) : 08 0B 01 02 0E 03 00 0A ........
---------------- Interface Descriptor -----------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x04 (Interface Descriptor)
bInterfaceNumber : 0x01
bAlternateSetting : 0x00
bNumEndpoints : 0x01 (1 Endpoint)
bInterfaceClass : 0x0E (Video)
bInterfaceSubClass : 0x01 (Video Control)
bInterfaceProtocol : 0x00
iInterface : 0x0A (String Descriptor 10)
Data (HexDump) : 09 04 01 00 01 0E 01 00 0A .........