构建支持 WebUSB 的设备

构建一个能够充分利用 WebUSB API 的设备。

本文介绍了如何打造可充分利用 WebUSB API。有关 API 本身的简要说明,请参阅访问 USB 设备 网页

背景

通用串行总线 (USB) 已成为最常用的物理接口, 将外围设备连接到桌面和移动计算设备。除了 它定义了公交车的电气特性,以及通用模型 USB 规范包括一系列设备类别 。这些是适用于特定类别设备的通用模型, 存储设备、音频、视频和网络等, 实施。这些设备类别规范的优势在于 操作系统供应商可以基于 SDK 的类实现单个驱动程序, “类驱动程序”,任何实现该类的设备都将 支持。与需要编写代码的所有制造商相比, 自己的设备驱动程序。

但是,一些设备并不适合这些标准化设备类别中的任何一个。答 制造商则可以改为选择将其设备标记为实现 特定于供应商的类。在这种情况下,操作系统会自行选择 驱动程序根据供应商的驱动程序软件包中提供的信息进行加载; 通常是一组已知可实现 特定的供应商专用协议

USB 的另一个特性是,设备可以提供多个接口 连接到的主机每个接口均可实现 或特定于供应商。当操作系统选择 正确的驱动程序来处理设备;每个接口都可以由不同的 例如,USB 摄像头通常提供两个接口, 实现 USB 视频类(用于相机),另一个实现 USB 视频类 音频类(针对麦克风)。操作系统未加载单个 “摄像头驱动程序”而是会加载独立的视频和音频类驱动程序 它们负责设备的单独功能。这个 组合接口类可提供更大的灵活性。

API 基础知识

许多标准 USB 类都有对应的 Web API。例如, 页面可以使用 getUserMedia() 从视频类设备录制视频 或从人机接口 (HID) 类设备接收输入事件,方法是监听 (针对 KeyboardEventsPointerEvents),或使用游戏手柄WebHID API。 正如并非所有设备都会实现标准化类定义,并非所有设备都会实现标准化类定义 设备实现与现有网络平台 API 对应的功能。时间 在这种情况下,WebUSB API 可以为网站提供一种 声明一个供应商专用接口,并直接从 在其网页内展示广告

可通过 WebUSB 访问设备的具体要求略有不同 由于操作系统管理 USB 的方式不同,因此在不同的平台之间 但基本要求是设备不能同时具有 声明页面想要控制的接口的驱动程序。可以是 操作系统供应商提供的通用类驱动程序或 。由于 USB 设备可以提供多个接口,其中每个接口 则可以构建一个支持某些接口的设备 其他声明由驾驶员声明,其他则可通过浏览器访问。

例如,高端 USB 键盘可能会提供 HID 类接口, 将由操作系统的输入子系统和特定于供应商的 接口仍可供 WebUSB 使用,以供配置工具使用。这个 可在制造商的网站上提供,让用户更改 例如宏键和灯光效果, 安装任何平台专用软件。此类设备的配置描述符会 如下所示:

字段 说明
配置描述符
0x09 bLength 此描述符的大小
0x02 bDescriptorType 配置描述符
0x0039 wTotalLength 此系列描述符的总长度
0x02 bNumInterfaces 接口数量
0x01 bConfigurationValue Configuration 1
0x00 iConfiguration 配置名称(无)
0b1010000 bmAttributes 具有远程唤醒功能的自供设备
0x32 bMaxPower 最大功率以 2 毫安为增量表示
接口描述符
0x09 bLength 此描述符的大小
0x04 bDescriptorType 接口描述符
0x00 bInterfaceNumber 接口 0
0x00 bAlternateSetting 备用设置 0(默认)
0x01 bNumEndpoints 1 个端点
0x03 bInterfaceClass HID 接口类
0x01 bInterfaceSubClass 启动接口子类
0x01 bInterfaceProtocol 键盘
0x00 iInterface 接口名称(无)
HID 描述符
0x09 bLength 此描述符的大小
0x21 bDescriptorType HID 描述符
0x0101 bcdHID HID 版本 1.1
0x00 bCountryCode 硬件目标国家/地区
0x01 bNumDescriptors 要遵循的 HID 类描述符数量
0x22 bDescriptorType 报告描述符类型
0x003F wDescriptorLength 报告描述符的总长度
端点描述符
0x07 bLength 此描述符的大小
0x05 bDescriptorType 端点描述符
0b10000001 bEndpointAddress 端点 1 (IN)
0b00000011 bmAttributes 中断
0x0008 wMaxPacketSize 8 字节数据包
0x0A bInterval 10 毫秒间隔
接口描述符
0x09 bLength 此描述符的大小
0x04 bDescriptorType 接口描述符
0x01 bInterfaceNumber 接口 1
0x00 bAlternateSetting 备用设置 0(默认)
0x02 bNumEndpoints 2 个端点
0xFF bInterfaceClass 特定于供应商的接口类
0x00 bInterfaceSubClass
0x00 bInterfaceProtocol
0x00 iInterface 接口名称(无)
端点描述符
0x07 bLength 此描述符的大小
0x05 bDescriptorType 端点描述符
0b10000010 bEndpointAddress 端点 1 (IN)
0b00000010 bmAttributes 批量
0x0040 wMaxPacketSize 64 字节数据包
0x00 bInterval 批量端点不适用
端点描述符
0x07 bLength 此描述符的大小
0x05 bDescriptorType 端点描述符
0b00000011 bEndpointAddress 端点 3 (OUT)
0b00000010 bmAttributes 批量
0x0040 wMaxPacketSize 64 字节数据包
0x00 bInterval 批量端点不适用

配置描述符由串联的多个描述符组成 。每个字段都以 bLengthbDescriptorType 字段开头, 可以识别。第一个接口是一个 HID 接口,它具有 HID 描述符和用于将输入事件传递到 操作系统第二个接口是供应商专用接口,包含两个 可用于向设备发送命令和接收响应的端点 。

WebUSB 描述符

虽然 WebUSB 无需进行固件修改即可与许多设备搭配使用, 通过将设备标记为特定 描述符,用于表示支持 WebUSB。例如,您可以指定 着陆页网址,即浏览器在访问 插上电源。

Chrome 中的 WebUSB 通知的屏幕截图
WebUSB 通知

二进制设备对象存储 (BOS) 是在 USB 3.0 中引入的概念, 也作为版本 2.1 的一部分向后移植到 USB 2.0 设备。声明 对 WebUSB 的支持从包括以下平台功能开始 BOS 描述符中的描述符:

字段 说明
二进制设备对象存储描述符
0x05 bLength 此描述符的大小
0x0F bDescriptorType 二进制设备对象存储描述符
0x001D wTotalLength 此系列描述符的总长度
0x01 bNumDeviceCaps BOS 中的设备功能描述符数量
WebUSB 平台功能描述符
0x18 bLength 此描述符的大小
0x10 bDescriptorType 设备功能描述符
0x05 bDevCapabilityType 平台功能描述符
0x00 bReserved
{0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} PlatformCapablityUUID 采用小端格式的 WebUSB 平台功能描述符 GUID
0x0100 bcdVersion WebUSB 描述符版本 1.0
0x01 bVendorCode WebUSB 的 bRequest 值
0x01 iLandingPage 着陆页网址

平台功能 UUID 会将此标识为 WebUSB 平台功能 描述符:提供有关设备的基本信息。对于浏览器 使用 bVendorCode 值来提取有关设备的更多信息 向设备发出其他请求。目前指定的唯一请求是 GET_URL,用于返回网址描述符。它们类似于 描述符,但旨在用最少的字节对网址进行编码。网址 "https://google.com" 的描述符会如下所示:

字段 说明
网址描述符
0x0D bLength 此描述符的大小
0x03 bDescriptorType 网址描述符
0x01 bScheme https://
"google.com" 网址 UTF-8 编码的网址内容

当您的设备首次插入浏览器时,系统会通过以下方式读取 BOS 描述符: 发出此标准 GET_DESCRIPTOR 控件传输:

bmRequestType bRequest wValue wIndex wLength 数据(响应)
0b10000000 0x06 0x0F00 0x0000 * BOS 描述符

此请求通常会发出两次,第一次时 wLength 足够大 以便主机在不使用wTotalLength 进行大量传输,然后在描述符的完整长度达到 已知。

如果 WebUSB 平台功能描述符将 iLandingPage 字段设置为 非零值,然后浏览器会执行 WebUSB 专用 GET_URL 请求 方式是发出控制传输,并将 bRequest 设置为 bVendorCode 值 并将 wValue 设置为 iLandingPage。 值。GET_URL (0x02) 的请求代码位于 wIndex 中:

bmRequestType bRequest wValue wIndex wLength 数据(响应)
0b11000000 0x01 0x0001 0x0002 * 网址描述符

同样,该请求可能会发出两次,以便先探测 要读取的描述符的 ID。

针对具体平台的注意事项

虽然 WebUSB API 会尝试提供一致的接口来访问 USB 设备开发者仍应了解 (如网络浏览器要求)才能访问设备。

macOS

对于 macOS,无需进行任何特殊操作。使用 WebUSB 的网站可以连接到 并声明内核驱动程序未声明的所有接口,或 另一个应用。

Linux

Linux 与 macOS 类似,但默认情况下,大多数发行版都不会设置用户 有权打开 USB 设备的账号。一个名为 udev 的系统守护进程 负责分配有权访问设备的用户和群组。一条规则 例如,这会分配与指定供应商匹配的设备的所有权, plugdev 群组(具有访问权限的用户的常用群组) 至外围设备:

SUBSYSTEM=="usb", ATTR{idVendor}=="XXXX", ATTR{idProduct}=="XXXX", GROUP="plugdev"

XXXX 替换为您的设备的十六进制供应商 ID 和产品 ID。 例如ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e11" 将会匹配 Nexus One 手机。编写这些代码时,必须不包含常规的“0x”前缀全部小写 进行正确识别。如需查找设备的 ID,请运行命令行 工具 lsusb

此规则应放在 /etc/udev/rules.d 目录下的一个文件中, 操作会立即生效。无需重启 udev。

Android

Android 平台基于 Linux,但不需要对 系统配置。默认情况下,未构建驱动程序的所有设备 推送到操作系统中开发者应 但用户在连接到 。在用户选择设备以响应对 的调用后, requestDevice(),Android 会显示提示,询问是否允许 Chrome 即可访问它。如果用户回访网站,系统也会重新显示此提示 并且网站调用 open()

此外,Android 设备将比 Linux 桌面设备更多 因为默认情况下包含的驱动程序较少。例如,值得注意的是 是 USB CDC-ACM 类,通常由 USB 转串行适配器实现, 不是 Android SDK 中用于与串行设备通信的 API。

ChromeOS

ChromeOS 也是基于 Linux 的,因此无需任何修改 系统配置。Permissions_broker 服务用于控制对 USB 的访问权限 并会允许浏览器在至少存在 一个未声明的接口。

Windows

Windows 驱动程序模型引入了一项额外的要求。取消 不支持从用户应用程序打开 USB 设备的平台 即使未加载驱动程序也仍采用默认值。它有一个特殊的 一个驱动程序,即 WinUSB 驱动程序,以便提供 访问设备所使用的应用。这可以通过自定义 通过修改设备安装的驱动程序信息文件 (INF) 以便在测试期间提供 Microsoft OS 兼容性描述符 枚举。

驱动程序信息文件 (INF)

驱动程序信息文件会告诉 Windows 在遇到设备时该做什么 我们第一次开发这个应用由于用户的系统已包含 WinUSB 驱动程序 只需让 INF 文件将您的供应商 ID 和产品 ID 相关联即可 应用此新安装规则下面是一个基本示例文件。将其保存到 扩展名为 .inf 的文件,更改标有“X”的部分,然后向右 点击它,然后选择“安装”从上下文菜单中操作

[Version]
Signature   = "$Windows NT$"
Class       = USBDevice
ClassGUID   = {88BAE032-5A81-49f0-BC3D-A4FF138216D6}
Provider    = %ManufacturerName%
CatalogFile = WinUSBInstallation.cat
DriverVer   = 09/04/2012,13.54.20.543

; ========== Manufacturer/Models sections ===========

[Manufacturer]
%ManufacturerName% = Standard,NTx86,NTia64,NTamd64

[Standard.NTx86]
%USB\MyCustomDevice.DeviceDesc% = USB_Install,USB\VID_XXXX&PID_XXXX

[Standard.NTia64]
%USB\MyCustomDevice.DeviceDesc% = USB_Install,USB\VID_XXXX&PID_XXXX

[Standard.NTamd64]
%USB\MyCustomDevice.DeviceDesc% = USB_Install,USB\VID_XXXX&PID_XXXX

; ========== Class definition ===========

[ClassInstall32]
AddReg = ClassInstall_AddReg

[ClassInstall_AddReg]
HKR,,,,%ClassName%
HKR,,NoInstallClass,,1
HKR,,IconPath,%REG_MULTI_SZ%,"%systemroot%\system32\setupapi.dll,-20"
HKR,,LowerLogoVersion,,5.2

; =================== Installation ===================

[USB_Install]
Include = winusb.inf
Needs   = WINUSB.NT

[USB_Install.Services]
Include = winusb.inf
Needs   = WINUSB.NT.Services

[USB_Install.HW]
AddReg = Dev_AddReg

[Dev_AddReg]
HKR,,DeviceInterfaceGUIDs,0x10000,"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"

; =================== Strings ===================

[Strings]
ManufacturerName              = "Your Company Name Here"
ClassName                     = "Your Company Devices"
USB\MyCustomDevice.DeviceDesc = "Your Device Name Here"

[Dev_AddReg] 部分用于为 设备。每个设备接口都必须有 GUID,应用才能 并通过 Windows API 找到并连接该网络。使用 New-Guid PowerShell cmdlet 或在线工具生成随机 GUID。

出于开发目的,Zadig 工具提供了一个简单的界面,用于执行 用 WinUSB 驱动程序替换为 USB 接口加载的驱动程序。

Microsoft 操作系统兼容性描述符

上述 INF 文件方法非常麻烦, 启动了预测Windows 8.1 及更高版本也为 自定义 USB 描述符。这些描述符会提供 会在设备首次插入时连接到 Windows 操作系统, 通常包含在 INF 文件中。

设置 WebUSB 描述符后,您就可以轻松添加 Microsoft 的操作系统 兼容性描述符首先使用以下代码扩展 BOS 描述符: 额外的平台功能描述符请务必更新 wTotalLengthbNumDeviceCaps

字段 说明
Microsoft OS 2.0 平台功能描述符
0x1C bLength 此描述符的大小
0x10 bDescriptorType 设备功能描述符
0x05 bDevCapabilityType 平台功能描述符
0x00 bReserved
{0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F} PlatformCapablityUUID 采用小端格式的 Microsoft OS 2.0 平台兼容性描述符 GUID
0x06030000 dwWindowsVersion 最低兼容的 Windows 版本 (Windows 8.1)
0x00B2 wMSOSDescriptorSetTotalLength 描述符集的总长度
0x02 bMS_VendorCode 用于检索更多 Microsoft 描述符的 bRequest 值
0x00 bAltEnumCode 设备不支持替代枚举

与 WebUSB 描述符一样,您必须选择一个 bRequest 值以供使用 与这些描述符相关的控制传输。在此示例中,我选择 0x02。位于 wIndex 中的 0x07 是用于检索 Microsoft OS 的命令 2.0 描述符集。

bmRequestType bRequest wValue wIndex wLength 数据(响应)
0b11000000 0x02 0x0000 0x0007 * MS OS 2.0 描述符集

USB 设备可能具有多种功能,因此描述符的第一部分 set 说明其跟随的属性与哪个函数相关联。通过 以下示例配置了复合设备的接口 1。该描述符提供 关于此接口的两条重要信息兼容的 ID 描述符可告知 Windows 此设备与 WinUSB 兼容。 注册表属性描述符的功能与 [Dev_AddReg] 部分,将注册表属性设置为 为此函数分配一个设备接口 GUID。

字段 说明
Microsoft OS 2.0 描述符集标头
0x000A wLength 此描述符的大小
0x0000 wDescriptorType 描述符集标头描述符
0x06030000 dwWindowsVersion 最低兼容的 Windows 版本 (Windows 8.1)
0x00B2 wTotalLength 描述符集的总长度
Microsoft OS 2.0 配置子集标头
0x0008 wLength 此描述符的大小
0x0001 wDescriptorType 配置子集标题说明。
0x00 bConfigurationValue 适用于配置 1(无论配置如何,索引值均为 0) 正常从 1 开始指数
0x00 bReserved 必须设置为 0
0x00A8 wTotalLength 包含此标头的子集的总长度
Microsoft OS 2.0 函数子集标头
0x0008 wLength 此描述符的大小
0x0002 wDescriptorType 函数子集标头描述符
0x01 bFirstInterface 函数的第一个接口
0x00 bReserved 必须设置为 0
0x00A0 wSubsetLength 包含此标头的子集的总长度
与 Microsoft OS 2.0 兼容的 ID 描述符
0x0014 wLength 此描述符的大小
0x0003 wDescriptorType 兼容的 ID 描述符
"WINUSB\0\0" CompatibileID ASCII 字符串已填充为 8 个字节
"\0\0\0\0\0\0\0\0" SubCompatibleID ASCII 字符串已填充为 8 个字节
Microsoft OS 2.0 注册表属性描述符
0x0084 wLength 此描述符的大小
0x0004 wDescriptorType 注册表属性描述符
0x0007 wPropertyDataType REG_MULTI_SZ
0x002A wPropertyNameLength 属性名称的长度
"DeviceInterfaceGUIDs\0" PropertyName 带有 null 终止符(采用 UTF-16LE 编码)的属性名称
0x0050 wPropertyDataLength 属性值的长度
"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\0\0" PropertyData GUID 加两个采用 UTF-16LE 编码的 null 终止符

Windows 只会向设备查询一次此信息。如果设备 没有使用有效描述响应,那么系统下次就不再询问 设备已连接。Microsoft 提供了一系列 USB 设备注册表 描述枚举设备时创建的注册表条目的条目。时间 测试,删除为设备创建的条目,以强制 Windows 尝试读取 描述符。

如需了解详情,请参阅 Microsoft 的博文,了解如何使用这些功能 描述符。

示例

用于实现同时包含 WebUSB 的 WebUSB 感知设备的示例代码 描述符和 Microsoft 操作系统描述符可在以下项目中找到: