使用微软系统描述符2.0制作免驱动自定义USB设备


前言

在《使用微软系统描述符1.0制作免驱动自定义USB设备》一文中,介绍了如何使用1.0版本的系统描述符来制作免驱动设备,这里将介绍如何使用2.0版本的系统描述符来制作免驱动设备。无论是1.0还是2.0,都是为了让系统给设备安装WinUSB驱动,并且给接口指定GUID。2.0版本的系统描述符处理流程更加精简一些。2.0不再需要OS字符串描述符,而是使用了USB标准的BOS描述符来获取设备的vendor code。然后再通过一个叫做描述符集的描述符一次性返回所有接口所有配置的compat ID和属性。

BOS描述符

Binary Object Store二进制对象数据描述符,需要设备的bcdUSB大于等于0x0210,在此描述符中上报设备支持的类型。BOS描述符的头是在USB标准中指定。下面描述符中bDeviceCapabilityType为5,在标准中5是保留值,在这里由微软定义为Platform Capability BOS Descriptor,里面包含了uuid,操作系统版本,vendor code信息:

WEAK __ALIGN_BEGIN const uint8_t WINUSB20_WCIDBOS [33] __ALIGN_END = {
  ///////////////////////////////////////
  /// WCID20 BOS descriptor
  ///////////////////////////////////////
  0x05,                                             /* bLength */
  USB_DESC_TYPE_BOS,                                /* bDescriptorType */
  0x21, 0x00,                                       /* wTotalLength */
  0x01,                                             /* bNumDeviceCaps */
  
  ///////////////////////////////////////
  /// WCID20 device capability descriptor
  ///////////////////////////////////////
  0x1c,                                             /* bLength */
  0x10,                                             /* bDescriptorType */
  0x05,                                             /* bDevCapabilityType */
  0x00,                                             /* bReserved */
  0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c,   /* bPlatformCapabilityUUID_16 */
  0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, 0x9f,   /* bPlatformCapabilityUUID_16 */
  0x00, 0x00, 0x03, 0x06,                           /* dwWindowsVersion */
  LO_BYTE(WINUSB20_WCID_DESC_SET_SIZE), HI_BYTE(WINUSB20_WCID_DESC_SET_SIZE),/* wDescriptorSetTotalLength */
  WCID_VENDOR_CODE,                                 /* bVendorCode */
  0x00,                                             /* bAltEnumCode */
};

这个描述符的格式固定,在倒数第二个字节指定了vendor code。这个vendor code在后面的请求中会用到。

下图是请求BOS描述符的过程

OS2.0描述符集

Windows系统从BOS描述符中得到设备支持的功能后,通过vendor code请求OS2.0描述符集,这个描述符集会一次性将所有配置的所有接口信息都上报给操作系统。如果只有一个配置的时候,可以省略掉配置子集头。如果只有一个接口,可以省略掉功能子集头。

单配置单接口的OS2.0描述符集如下:

WEAK __ALIGN_BEGIN const uint8_t WINUSB20_WCIDDescriptorSet [162] __ALIGN_END = {
  ///////////////////////////////////////
  /// WCID20 descriptor set descriptor
  ///////////////////////////////////////
  0x0a, 0x00,                                       /* wLength */
  0x00, 0x00,                                       /* wDescriptorType */
  0x00, 0x00, 0x03, 0x06,                           /* dwWindowsVersion */
  0xa2, 0x00,                                       /* wDescriptorSetTotalLength */
  
  ///////////////////////////////////////
  /// WCID20 compatible ID descriptor
  ///////////////////////////////////////
  0x14, 0x00,                                       /* wLength */
  0x03, 0x00,                                       /* wDescriptorType */
  /* WINUSB */
  'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,         /* cCID_8 */
  /*  */
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   /* cSubCID_8 */
  
  ///////////////////////////////////////
  /// WCID20 registry property descriptor
  ///////////////////////////////////////
  0x84, 0x00,                                       /* wLength */
  0x04, 0x00,                                       /* wDescriptorType */
  0x07, 0x00,                                       /* wPropertyDataType */
  0x2a, 0x00,                                       /* wPropertyNameLength */
  /* DeviceInterfaceGUIDs */
  'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00,       /* wcPropertyName_21 */
  'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00,       /* wcPropertyName_21 */
  't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00,       /* wcPropertyName_21 */
  'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00,       /* wcPropertyName_21 */
  'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00,       /* wcPropertyName_21 */
  0x00, 0x00,                                       /* wcPropertyName_21 */
  0x50, 0x00,                                       /* wPropertyDataLength */
  /* {1D4B2365-4749-48EA-B38A-7C6FDDDD7E26} */
  '{', 0x00, '1', 0x00, 'D', 0x00, '4', 0x00,       /* wcPropertyData_40 */
  'B', 0x00, '2', 0x00, '3', 0x00, '6', 0x00,       /* wcPropertyData_40 */
  '5', 0x00, '-', 0x00, '4', 0x00, '7', 0x00,       /* wcPropertyData_40 */
  '4', 0x00, '9', 0x00, '-', 0x00, '4', 0x00,       /* wcPropertyData_40 */
  '8', 0x00, 'E', 0x00, 'A', 0x00, '-', 0x00,       /* wcPropertyData_40 */
  'B', 0x00, '3', 0x00, '8', 0x00, 'A', 0x00,       /* wcPropertyData_40 */
  '-', 0x00, '7', 0x00, 'C', 0x00, '6', 0x00,       /* wcPropertyData_40 */
  'F', 0x00, 'D', 0x00, 'D', 0x00, 'D', 0x00,       /* wcPropertyData_40 */
  'D', 0x00, '7', 0x00, 'E', 0x00, '2', 0x00,       /* wcPropertyData_40 */
  '6', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00,     /* wcPropertyData_40 */
};

在上面的描述符中,省略掉了配置子集头和功能子集头,在里面直接写入了Compatible ID和注册表属性。如果有多个接口,需要通过功能子集头指定后面的内容属于哪一个接口。

单配置多接口的OS2.0描述符集可以通过在线的描述符生成工具TeenyDT进行查看,选择WinUSB20,点击【==> TeenyUSB .c】生成C语言格式描述符。

下图是请求OS2.0描述符集的过程

从上图可以看到,描述符集并没有像其它描述符那样先取头获得总长度,再取完整的描述符,这是因为在BOS描述符中包含描述符集的长度信息。

使用OS2.0描述符的一些注意事项

当使用OS2.0描述符时,会将bcdUSB设置为0x0210,即USB版本2.1。在此版本下,高速设备的Bulk端点最大包长必须为512,否则会报报告描述符错误。

如上图所示,当端点类型为Bulk,最大包长不为512时,在任务管理器中会报【配置描述符无效】的错误。

关于bcdUSB = 0x210

这个值说明USB设备的版本号是2.1,实际上并没有任何关于USB 2.1的文档。在《USB 2.0 Link Power Management Addendum》这个文档的第3节中,提到如果要支持BOS描述符,需要将bcdUSB设置为0x201或更高。

在USB3.0的规范文档的9.2.6.6章节中,提到如果要支持BOS描述符,需要bcdUSB大于等于0x210。3.0设备工作在高速模式时会前向兼容2.0,因此这个地方的bcdUSB大于等于0x210对于2.0设备也是有效的。

两个文档中一个是大于等于0x201,一个是大于等于0x210,为了方便兼容,这里就取了较大的那个值0x210,实际上bcdUSB设置为0x201也是可以识别的。

无论这里的bcdUSB取0x210还是0x201,都只是为了说明这个设备支持BOS描述符,用的还是2.0的规范。

 

附录

完整的USB设备端代码

完整的Windows端代码及VS2019工程

单接口WinUSB2.0设备枚举过程抓包使用USB Packet Viewer查看

多接口WinUSB2.0设备枚举过程抓包使用USB Packet Viewer查看

参考文档

https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon

https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb

https://msdn.microsoft.com/zh-cn/windows/hardware/gg463179

https://github.com/pbatard/libwdi/wiki/WCID-Devices

Microsoft OS 2.0 Descriptor Specification

USB 3.0 Specification

 

关于Windows系统上免驱动设备更详细的内容请阅读《使用微软系统描述符1.0制作免驱动自定义USB设备》和《使用微软系统描述符2.0制作免驱动自定义USB设备》