本文翻译自 Pete Batard 发表于 libwdi wiki 页面的 《WCID Device》 。
原文版权信息:CC BY-SA 4.0 许可,Copyright © 2012-2021 Pete Batard

什么是 WCID 设备?

WCID 代表的是“Windows Compatible ID”,即Windows 兼容 ID。它是一种向 Windows 系统提供额外信息的 USB 设备,以便于自动安装驱动程序,在大多数情况下即插即用。

在通常的情况下,除非是人体学输入设备 (HID) 或是大容量存储设备 (Mass Storage) 的 USB 设备,其他类型都需要由用户手动安装驱动程序。WCID 的加入则允许设备在插入时就可被 Windows 应用程序使用。因此,WCID 可以将 HID 和大容量存储设备“即插即用”的特性带到任何 USB 设备上(只要固件有提供 WCID)。

WCID 是 WinUSB 设备功能的扩展,由微软在 Windows 8 开发者预览版期间提出,使用了微软操作系统描述符 (Microsoft OS Descriptors, MOD)。该描述符从 Windows XP SP2 起一直是 Windows 的一部分。

自 2012 年 5 月起,Windows Vista 后续的所有平台上都提供了一个自动安装的 WinUSB WCID 驱动程序。在 Windows 8 或更高版本中,它内置于系统中,这意味着不需要联网安装驱动程序。而 Vista 和 Windows 7,则需要通过 Windows Update 获得驱动。

历史

在 WCID 应用之前,只有属于 HID(USB键盘、鼠标、摇杆等)或大容量存储(USB 磁盘、基于闪存的存储、媒体播放器,如 iPod 等)这些得到良好支持的 USB 设备才不需要干预驱动安装。因为在设备第一次插入时,Windows 会自动处理驱动问题。此外,其他设备通常需要用户自己手动提供和安装驱动程序。这不利于使用的方便性,并阻碍了那些不熟悉 Windows 上驱动安装的人使用通用 USB 应用程序,如基于 libusbLibUsbDotNetlibusb-win32libusbKWinUSB 的应用程序。

不过,自 Windows XP SP2 以来,HID 或大容量存储设备的自动驱动安装方法(依靠一个共同的类,而不是一个特定的设备 ID)和提供特定的操作系统相关设备信息的方法(微软操作系统描述符),实际上可以结合起来,实现任何类型 USB 设备的自动驱动安装。

2011年,WCID 被微软通过更新的 WinUSB 驱动在 Windows 8开发者预览版中提出,并确认不仅仅只用于 WinUSB 设备。

2012年,微软还将 WinUSB WCID 驱动文件添加到 Windows Update 中,其效果是将 WCID 功能和 WinUSB 驱动的自动安装带到其他最新版本的 Windows 中。

由于 WCID 极大地提高了用户体验,它现在被用于 USB 设备,如安卓设备、医疗设备、游戏控制器和各种 USB小 工具,预计还会有更多的 USB 设备跟进。实际上,WCID 是微软在其 XBox 360 USB 手柄中使用的,以提供 XBox 工具栏驱动程序的无缝安装。

示例

下面是一张 Zadig 检测到的 WINUSB WCID 设备的截图。

在 Windows Vista 或更高版本中,这个既不是 HID 也不是 Mass Storage 的设备,可以看到通过 Windows Update(Windows Vista, Windows 7)或本机(Windows 8)自动安装的驱动程序,不需要任何形式的用户干预。

如果至少安装过一次 WinUSB WCID 驱动(即通用驱动),在 Windows XP SP2 上也可以实现同样的效果。

WCID 于最终使用者

WCID 对最终使用者的意义在于,只要设备是 WCID,并且系统上或 Windows Update 上有相同的通用兼容 ID 的 WCID驱动程序,那么在插入设备时就不需要手动安装驱动程序,Windows会自动进行驱动安装。

此外,像 Zadig 这样的应用程序可以确保,即使在微软不提供 WCID 驱动的平台上(如 XP SP2),或者使用非微软的USB驱动(libusb0,libusbK),WCID 仍然可以使你只需安装一次驱动。这样当你将来插入另一个 WCID 设备时,你就不会有这些麻烦了。

因此,如果你是一个最终使用者,而你想使用的设备既不是 HID 也不是大容量存储设备,你应该询问制造商它是否兼容 WCID。如果不是,你应该问他们是否可以更新设备固件并将其转换为 WCID,这对他们来说是非常容易做到的,因为这意味着将大大提升你的用户体验。

总而言之,一旦制造商开始使用 WCID,你应该发现,Windows 上几乎所有的USB设备都可以真正实现即插即用,繁琐的 USB 驱动安装将成为过去。

WCID 于制造商

显然,WCID 对设备制造商的主要好处是,它减轻了提供你自己的驱动程序或你自己的自定义驱动程序安装程序的需要,以及就不属于完善的 USB 类的设备的驱动程序要求对用户进行教学。如果你的用户想通过 Windows Update 获得你的驱动程序,它也减少了你生产的每一种设备需要通过 WHQL(Windows 硬件质量实验室测试)的需要。

对于任何运行 Windows Vista 或更高版本的用户来说,只要你在 WCID 类型中使用 "WINUSB"(在兼容ID描述符中,参见下文),就不需要再做什么了。WinUSB WCID 驱动程序将被自动安装,它可以是操作系统自带的,也可以从 Windows Update中获取的。因此,你可以开始开发基于 WinUSBlibusb 的应用程序,而不必担心驱动程序的安装。

对于没有运行 Windows Vista 或更高版本系统的最终用户,或者如果你选择使用 libusb-win32libusbK 来开发你的应用程序,你可以简单地提供一个只需安装一次的 WCID 驱动安装程序,如 Zadiglibwdiwdi-simple(这两个都是开源的,可以根据你的需要进行定制)。这些程序可以安装 WinUSB、libusb-win32 或 libusbK 驱动的 WCID 版本。

另另外,虽然 WCID 需要添加额外的描述符,但本页面已经投入了大量的精力,以确保你拥有快速和无痛地部署 WCID 所需的所有信息(接近零成本)。尤其是下面详述的简单注册表检查、Zadig 的使用或其他测试应用程序的使用,如 libusb 示例应用程序 xusb(其 Windows 二进制版本也可在此获得)或 Linux 的 usb-utils(当设置为转储微软操作系统描述符 MOD 时),可以帮助验证你的描述符是否已正确设置。

关于 AVR 和 PIC 的实际 WCID 设备实现的例子,以及一个典型的 WCID USB 查询跟踪,请看示例小节。

实现

你可以在下面找到关于 WCID 驱动程序在 Windows 上如何安装的扩展描述,以及你可以用来确认你的设备是否为 WCID 正确设置的方法。

重要:基于下面的说法,只有USB2.0或更高的设备才能被识别为WCID。

如果 USB 设备描述符的 bcdUSB 字段等于 0x01000x0110,集线器驱动程序将跳过对微软操作系统描述符的查询,转到“序列号字符串描述符查询 (Serial Number String Descriptor Query)”状态。

因此,你的设备描述符的bcdUSB字段必须设置为 0x0200 或者更高。
当然,这并不意味着你的设备必须以高速运行,它只是意味着你的设备符合 USB 2.0 规范或更高的标准。

微软操作系统字符串描述符

第一次插入 USB 设备时,微软 USB 端口驱动程序(usbport.sys)将读取标准的 USB 描述符,其中包括标准的设备、配置、接口和字符串描述符。它还会尝试读取位于索引 0xEE 的额外的字符串描述符。这个字符串描述符不是 USB 规范规定的,而是微软的一个扩展,叫做操作系统字符串描述符。确定一个设备是否为 WCID 需要两个元素,它是其中的第一个。

当 Windows 检查这个字符串描述符时,首先会在 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags 下创建一个注册表项,即 VID+PID+BCD_RELEASE_NUMBER 的连接(此键永远不会被删除)。在这个新的部分,创建了一个名叫 osvc 的 2 字节 REG_BINARY 类型键,它表示是否找到了合适的微软操作系统字符串描述符。

  • 如果 0xEE 描述符不存在,或者它不符合微软的签名("MSFT100"),那么 osvc 被设置为 0x0000。在我们的例子中,这意味着该设备不是 WCID。
  • 如果 0xEE 描述符存在,并且与微软操作系统字符串描述符的预期结构相匹配,那么 oscv 被设置为 0x01##。其中 01 表示设备满足微软操作系统供应商扩展,## 是供应商代码的字节值(见下文)。

下表详细说明了微软操作系统的字符串描述符在你的固件中应如何设置:

类型描述
0x12BYTE描述符的长度(18字节)
0x03BYTE描述符类型(3 = 字符串)
0x4D, 0x00, 0x53, 0x00,
0x46, 0x00, 0x54, 0x00,
0x31, 0x00, 0x30, 0x00,
0x30, 0x00
7 WORDS
Unicode字符串(LE)
签名:"MSFT100"
0x##BYTE供应商代码
0x00BYTE填充
表1: 微软操作系统字符串描述符

下面是一个 Windows 7 为 WCID 设备(LibusbDotNet 的 Benchmark 设备,VID 为 0x04D8,PID 为 0xFA2E,BCD 发行号为 0x0001)创建键值的例子。

在上面的例子中,固件中的供应商代码被设置为 0x20。因此,通过检查注册表,你可以很容易地发现 Windows 是否已经能够从你的设备中读取有效的操作系统供应商代码。

微软兼容 ID 功能描述符

如果 osvc 是有效的,那么就会查询额外的微软定义的功能描述符。每个调用都被操作系统分解为头检索,以找到大小,然后是完整的有效载荷检索(因此,设备可以看到系统的请求是实际调用的两倍)。

这些调用中至少有一个是针对 Compatible ID Feature Descriptor (兼容 ID 功能描述符)的。

这是由操作系统发出的供应商代码请求(控制转移),wIndex 设置为 0x0004bRequest 设置为先前返回的供应商代码,以及 bmRequestType 的接收者部分设置为设备(0x00,完整的 bmRequestType 被设置为0xC0)。这些调用是由 Windows 发出的,你不应该重复这个过程。如果你想要一个使用 libusb 检索兼容 ID 以及 Extended Properties Feature Descriptor (扩展属性功能描述符)的例子,请看 xusb.c 示例源中的 read_ms_winsub_feature_descriptors() 函数。

因此,使你的设备成为 WCID 的第二部分是确保你的固件用 wIndex=0x0004bRequest=0x## 响应供应商请求,以返回一个兼容 ID。

以其最简单的形式,如此处所述,数据包的格式如下:

类型描述
0x28, 0x00, 0x00, 0x00DWORD (LE)描述符的长度(40 字节)
0x00, 0x01BCD WORD (LE)版本号 ('1.0')
0x04, 0x00WORD (LE)兼容性 ID 描述符索引 (0x0004)
0x01BYTE小节数 (1)
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
7 BYTES保留
0x00BYTE接口号 (接口 #0)
0x01BYTE保留
0x57, 0x49, 0x4E, 0x55,
0x53, 0x42, 0x00, 0x00
8 BYTES
(以 NUL 结尾?)
ASCII 字符串
兼容 ID ("WINUSB\0\0")
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
8 BYTES
( 以 NUL 结尾? )
ASCII 字符串
次兼容 ID (未使用)
0x00, 0x00, 0x00, 0x00,
0x00, 0x00
6 BYTES保留
表2:微软兼容 ID 功能描述符

然后,您可以通过浏览注册表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB 树的设备位置来检查兼容 ID 是否从您的设备正确读取。与首次插入时创建的 osvc 键类似,你应该在 USB 下找到一个带设备 VID 和 PID 的条目(例如:VID_04D8&PID_FA2E)。

在这个条目下,可能在一个额外的子条目下,你应该验证 CompatibleIDs 键的内容(它是 REG_MULTI_SZ 类型)。如果 Windows 能够从你的设备上读取 Compatible ID,这个键是一个包含 USB\MS_COMP_XXXXXX 的字符串,其中 XXXXXXX 是你在报告中定义的内容。例如,如果你定义了 LIBUSB0,你应该可以看到 USB\MS_COMP_LIBUSB0

下面是一个通过兼容 ID 特征描述符("WINUSB")创建的注册表项的例子:

如果你能为你的设备找到一个 USB\MS_COMP_XXXXXX 条目(这里是 USB\MS_COMP_WINUSB),那么你的设备就是一个 WCID 设备。换句话说,在你的固件中定义一个微软操作系统字符串描述符和一个微软兼容 ID 特征描述符,就可以把它变成一个 WCID 设备,并受益于 Windows 的自动驱动安装功能。

除了 "WINSUB" 之外,还可以根据你想自动安装的驱动程序使用其他兼容ID。下表提供了您应该使用哪个兼容 ID 字符串的参考,具体取决于您希望将设备用于的应用程序类型:

兼容 ID 字符串应用类型
"WINUSB\0\0"libusb 应用
LibUsbDotNet 应用
原生 WinUSB 应用
"LIBUSB0\0"libusb-win32 应用
LibUsbDotNet 应用
"LIBUSBK\0"libusbK 应用
LibUsbDotNet 应用
表3:兼容 ID 对应的应用类型

Windows 驱动自动安装

通过 Windows 8 的原生 WinUSB 驱动自动安装,WCID 驱动自动安装过程得到了最好的说明。

在这里,同样有两个要求必须满足,以允许设备驱动程序的自动安装。

首先,inf 文件必须包括 USBMS_COMP_XXXXXX 作为设备标识符字符串。在 Windows 8 (x64) 中,如果你从你的 \Windows\System32\DriverStore\FileRepository\winusb.inf_amd64_50cb26e12c0e99f2\(或类似)目录中查看 winusb.inf,你会发现确实是这样:

相比之下,你在 Windows 7 的同一位置找到的 winusb.inf 缺少一个指向兼容 ID 的 [Manufacturer] (制造商)部分(但从 Windows Update 下载的 winusbcompat.inf 有这个部分)。

其次,驱动程序必须被预装到系统中,这主要意味着它必须在 \Windows\System32\DriverStore\FileRepository\ 中存在一个条目。在非 Windows 8 或以上的平台上,这种预安装会在试图找到一个驱动程序时通过 Windows Update 来完成。

由于 Windows 8 上的 WinUSB 驱动默认满足这两个条件,因此有可能插入 WinUSB WCID 设备(即兼容ID描述符返回 "WINUSB" 的WCID设备)并自动安装其驱动:一旦 Windows 在注册表中填充了 CompatibleIDs 列表,它将搜索 inf 库中的匹配项,如果找到一个,就安装相关驱动。

这整个过程其实并不新鲜,因为在 USB 类设备(如 HID 或大容量存储)上也是如此,其中 USB/Class_… 条目被用于匹配。例如,如果你查看 DriverStore\ 下的 input.inf,将会看到:

例如,其中 USB\Class_03 就是 HID 摇杆的 CompatibleIDs 字符串之一。

定义设备接口 GUID 或其他设备特定属性

如果你阅读微软的文档,你会发现微软的操作系统描述符不仅仅可以用于 WCID,因为 WCID 只是它的一个子集。举例来说,它们也可以被用来提供图标、URL或其他数据。

关于 WCID 设备,你可能感兴趣的一个项目是提供额外的设备注册表设置,尤其是提供您希望用于访问设备的设备接口 GUID。就像兼容 ID 一样,这也是通过一个特性描述符完成的,称为 Extended Properties Feature Descriptor (扩展属性特性描述符),位于 wIndex = 0x0005。然而,与兼容ID不同的是,这应该使用接口请求来查询,并将完整的 bmRequestType 设置为 0xC1

重要说明1:在 WinUSB 中可能存在一个错误或限制,它会强制任何接口请求的 wIndex 为接口号。这意味着,如果你使用 WinUSB 来验证你的扩展属性特征描述符的内容,你将无法检索它(除非它只为接口#5定义)。
如果你在 Windows 上使用 WinUSB 来验证你的描述符,那么我们的建议是,当扩展属性特征描述符被查询时,让你的设备固件同时回答设备和接口请求,这样你就可以使用设备请求而不是接口请求来验证你的描述符是否设置正确。作为参考,libusb 的最新 xusb 示例应用程序有一个 -w 开关,它将强制 wIndex = 0x0005 使用设备模式。

重要说明2:微软官方文档显示 DeviceInterfaceGUID 的定义为 REG_SZ,这也是我们在最后面提供的样本固件的定义。但是,如果你使用的设备有多个接口,你可能会遇到这样的问题:当 WCID 接口不是第一个接口时,发出注册表查询以获得设备接口 GUID 将不会返回 WCID 接口的 GUID,除非你将扩展属性设置为返回 DeviceInterfaceGUIDs 属性(带s)为 REG_MULTI_SZ,以定义你的 GUID。更糟糕的是,我们得到报告说,试图在一个单一的接口设备上使用DeviceInterfaceGUIDs(定义一个GUID)可能无法工作,因此你应该:

  • 如果你的设备只有一个接口,请使用带有单个 NUL 结尾 GUID 的 DeviceInterfaceGUID
类型描述
0x8e, 0x00, 0x00, 0x00DWORD (LE)描述符的长度
(142字节)
0x00, 0x01BCD WORD (LE)版本号 ('1.0')
0x05, 0x00WORD (LE)扩展属性描述符索引 (0x0005)
0x01, 0x00WORD (LE)小节数 (1)
0x84, 0x00, 0x00, 0x00DWORD (LE)属性部分的大小 (132 字节)
0x01, 0x00, 0x00, 0x00DWORD (LE)属性数据类型
(1 = Unicode
REG_SZ,
见下表)
0x28, 0x00WORD (LE)属性名称长度 (40 字节)
0x44, 0x00, 0x65, 0x00,
(...)
0x74, 0x00, 0x00, 0x00
以 NUL 结尾的
Unicode 字符串 (LE)
属性名称
"DeviceInterfaceGUID"
0x4e, 0x00, 0x00, 0x00DWORD (LE)属性数据长度 (78 字节)
0x7b, 0x00, 0x46, 0x00,
(...)
0x7d, 0x00, 0x00, 0x00
以 NUL 结尾的
Unicode 字符串 (LE)
属性数据
"{xxxxxxxx-xxxx-
xxxx-xxxx-
xxxxxxxxxxxx}\0"
表4a:微软扩展属性特征描述符
  • 如果你的设备有两个或更多的接口,则使用至少有两个 GUID 的列表的 DeviceInterfaceGUIDs,以双 NUL 结尾。
类型描述
0xe0, 0x00, 0x00, 0x00DWORD (LE)描述符的长度
(2 个 GUID 共 224字节)
0x00, 0x01BCD WORD (LE)版本号 ('1.0')
0x05, 0x00WORD (LE)扩展属性描述符索引 (0x0005)
0x01, 0x00WORD (LE)小节数 (1)
0xd6, 0x00, 0x00, 0x00DWORD (LE)属性部分的大小 (214 字节)
0x01, 0x00, 0x00, 0x00DWORD (LE)属性数据类型
(7 = Unicode
REG_MULTI_SZ,
见下表)
0x2a, 0x00WORD (LE)属性名称长度 (42 字节)
0x44, 0x00, 0x65, 0x00,
(...)
0x73, 0x00, 0x00, 0x00
以 NUL 结尾的
Unicode 字符串 (LE)
属性名称
"DeviceInterfaceGUIDs"
0x4e, 0x00, 0x00, 0x00DWORD (LE)属性数据长度 (158 字节)
0x7b, 0x00, 0x46, 0x00,
(...)
0x00, 0x00, 0x00, 0x00
双 NUL 结尾的
Unicode 字符串 (LE)
属性数据
"{xxxxxxxx-xxxx-
xxxx-xxxx-
xxxxxxxxxxxx}\0
{xxxxxxxx-xxxx-
xxxx-xxxx-
xxxxxxxxxxxx}\0\0"
表4b:微软扩展属性特征描述符

你可以使用的属性数据类型定义在下表中:

类型
1以 NUL 结尾的 Unicode 字符串 (REG_SZ)
2以 NUL 结尾的 Unicode 字符串,可能包括环境变量 (REG_EXPAND_SZ)
3二进制数据 (REG_BINARY)
4DWORD 值,小字节端 (REG_DWORD_LITTLE_ENDIAN)
5DWORD 值,大字节端 (REG_DWORD_BIG_ENDIAN)
6以 NUL 结尾的 Unicode 字符串,可能包括一个符号链接 (REG_LINK)
7多个以 NUL 结尾的 Unicode 字符串 (REG_MULTI_SZ)
表5:微软扩展属性特征描述符的数据类型

如果你按照上面强调的那样定义 DeviceInterfaceGUIDs,或者在索引 0x0005 处定义任何其他扩展属性,你会发现它会被复制到注册表中你设备的 Device Parameters (设备参数)子项下:

正如你所看到的,在扩展属性描述符中定义的 {F70242C7-FB25-443B-9E7E-A4260F373982} 已经在注册表中正确设置了。因此,若您的设备需要任何额外的注册表项,您可以使用扩展属性描述符来定义。之后再使用通常的微软 API,如 SetupDi,在应用程序中读取这些数据。

一个完整的操作系统特征描述符查询的例子

为了进一步了解 WCID 设备应该返回的数据,以及定义了 DeviceInterfaceGUID (可选)的设备,下面是一个针对使用 WCID 的 Benchmark 设备的 xusb 例子。上面描述的所有屏幕截图和数据部分都是用这个设备产生的。你可以从http://libusb.info 下载最新的 xusb 在 Windows 下的可执行程序。

WCID 的优势

  • 对最终用户来说是真正的即插即用
  • 一劳永逸的零次(Windows 8 即以上本机,Windows 7 通过 Windows Update)或一次手动安装驱动
  • 不需要通过 WHQL 测试或提交驱动程序至 Windows Update
  • 缩短了上市时间,应用程序可以支持未来的设备
  • 驱动程序的安装时间要快得多(在 Windows 8 或更高版本上,或者在 Windows 7 或更早版本上预装 WCID 驱动程序后)
  • 所有从 Windows XP SP2 开始的 Windows 版本上工作

WCID 的弊端

  • 在 Windows 7 或更早的版本中,在驱动安装过程中没有已知的方法来填充设备名称。这意味着 WCID 设备将在设备管理器中以相同的通用名称(WinUsb Device,即 WinUsb 设备)列出。在 Windows 8 或更高版本上,如果已经定义了产品字符串描述符,设备将被显示为描述符中定义的名称。
  • 只有 Windows Vista 或更高版本上的 WinUSB 不需要任何用户干预。对于 libusb-win32 或 libusbK,或者 Windows XP 上的 WinUSB,WCID 驱动必须至少被手动安装一次(但仅一次)

WCID 设备实例

PIC

作为参考,一个典型的 Microchip PIC USB 设备固件的实现(这个 diff 提供了一个实际需要多少工作的准确指示)。

AVR (LUFA)

另外,这里还提供了一个用于 Atmel AVR 芯片基于 LUFA 的简单 WCID 固件,可以使用 AVR Studio 5 进行编译,以供参考。

STM32

另一个例子是基于 STM32(32 位 ARM Cortex)的 WCID 设备,由 Immense NetworksDarren Kattan 提供。该样本基于 USB 设备库 V3.3.0,可在 ST 网站上免费获得。下面是 Darren 的说明:

我对核心的 USB 代码没有做任何改动。该技术栈的设计方式是,对于核心代码无法识别的 USB 请求,会回调给用户代码。我们只需要修改 4 个文件以使其工作。

- USB_prop.c/.h
- USB_desc.c/.h

我改变了 PLL 设置以适应我的硬件,所以使用其他开发板的人将需要将其修改回来。我的硬件使用的是一个 16MHz 的时钟,而不是像大多数 Connectivity Line 开发板那样的 25MHz。

这里可以找到 v3.3.0 库档案,名称为 STM32F1XX V3.3.0 (WinUSB).rar。在同一目录下还有名称为 STM32_USB-Host-Device_Lib_V2.1.0 (WINUSB HID).rar 的v2.1.0库文件。

请注意,每个库中唯一被修改的项目是 HID 项目。

其他

最后,这里还提供了一个微软 NetMon 跟踪(使用 USB 扩展--更多细节,请看这篇博文),说明你的设备第一次插在 Windows 机器上时,将收到何种 USB 查询,以及应该如何响应。

其他链接

Hintay

Hintay

帝王鸽

发表评论

表情 图片 粗体 删除线 居中 斜体 下划线 段落引用 代码