构建一个能够充分利用 WebUSB API 的设备。
本文介绍了如何打造可充分利用 WebUSB API。有关 API 本身的简要说明,请参阅访问 USB 设备 网页。
背景
通用串行总线 (USB) 已成为最常用的物理接口, 将外围设备连接到桌面和移动计算设备。除了 它定义了公交车的电气特性,以及通用模型 USB 规范包括一系列设备类别 。这些是适用于特定类别设备的通用模型, 存储设备、音频、视频和网络等, 实施。这些设备类别规范的优势在于 操作系统供应商可以基于 SDK 的类实现单个驱动程序, “类驱动程序”,任何实现该类的设备都将 支持。与需要编写代码的所有制造商相比, 自己的设备驱动程序。
但是,一些设备并不适合这些标准化设备类别中的任何一个。答 制造商则可以改为选择将其设备标记为实现 特定于供应商的类。在这种情况下,操作系统会自行选择 驱动程序根据供应商的驱动程序软件包中提供的信息进行加载; 通常是一组已知可实现 特定的供应商专用协议
USB 的另一个特性是,设备可以提供多个接口 连接到的主机每个接口均可实现 或特定于供应商。当操作系统选择 正确的驱动程序来处理设备;每个接口都可以由不同的 例如,USB 摄像头通常提供两个接口, 实现 USB 视频类(用于相机),另一个实现 USB 视频类 音频类(针对麦克风)。操作系统未加载单个 “摄像头驱动程序”而是会加载独立的视频和音频类驱动程序 它们负责设备的单独功能。这个 组合接口类可提供更大的灵活性。
API 基础知识
许多标准 USB 类都有对应的 Web API。例如,
页面可以使用 getUserMedia()
从视频类设备录制视频
或从人机接口 (HID) 类设备接收输入事件,方法是监听
(针对 KeyboardEvents 或 PointerEvents),或使用游戏手柄或
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 | 批量端点不适用 |
配置描述符由串联的多个描述符组成
。每个字段都以 bLength
和 bDescriptorType
字段开头,
可以识别。第一个接口是一个 HID 接口,它具有
HID 描述符和用于将输入事件传递到
操作系统第二个接口是供应商专用接口,包含两个
可用于向设备发送命令和接收响应的端点
。
WebUSB 描述符
虽然 WebUSB 无需进行固件修改即可与许多设备搭配使用, 通过将设备标记为特定 描述符,用于表示支持 WebUSB。例如,您可以指定 着陆页网址,即浏览器在访问 插上电源。
![Chrome 中的 WebUSB 通知的屏幕截图](https://developer.chrome.google.cn/static/docs/capabilities/build-for-webusb/image/screenshot-the-webusb-no-531009ef49311.png?authuser=8&hl=bg)
二进制设备对象存储 (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 描述符:
额外的平台功能描述符请务必更新 wTotalLength
和 bNumDeviceCaps
。
值 | 字段 | 说明 |
---|---|---|
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 操作系统描述符可在以下项目中找到: