Сборка устройства для WebUSB

Создайте устройство, чтобы в полной мере воспользоваться преимуществами API WebUSB.

Рейли Грант
Reilly Grant

В этой статье объясняется, как создать устройство, чтобы в полной мере использовать преимущества WebUSB API . Краткое введение в сам API см. в разделе Доступ к USB-устройствам в Интернете .

Фон

Универсальная последовательная шина (USB) стала наиболее распространенным физическим интерфейсом для подключения периферийных устройств к настольным и мобильным вычислительным устройствам. Помимо определения электрических характеристик шины и общей модели связи с устройством, спецификации USB включают набор спецификаций классов устройств. Это общие модели для определенных классов устройств, таких как устройства хранения данных, аудио, видео, сети и т. д., которые могут реализовать производители устройств. Преимущество этих спецификаций классов устройств заключается в том, что поставщик операционной системы может реализовать один драйвер на основе спецификации класса («драйвер класса»), и любое устройство, реализующее этот класс, будет поддерживаться. Это было большим шагом вперед по сравнению с тем, что каждому производителю приходилось писать свои собственные драйверы устройств.

Однако некоторые устройства не вписываются ни в один из этих стандартизированных классов устройств. Вместо этого производитель может пометить свое устройство как реализующее класс, специфичный для поставщика. В этом случае операционная система выбирает, какой драйвер устройства загружать, на основе информации, предоставленной в пакете драйверов поставщика, обычно это набор идентификаторов поставщика и продукта, которые, как известно, реализуют определенный протокол, зависящий от поставщика.

Еще одна особенность USB заключается в том, что устройства могут предоставлять несколько интерфейсов хосту, к которому они подключены. Каждый интерфейс может реализовывать либо стандартизированный класс, либо зависеть от поставщика. Когда операционная система выбирает правильные драйверы для управления устройством, каждый интерфейс может быть востребован отдельным драйвером. Например, веб-камера USB обычно предоставляет два интерфейса: один реализует класс видео USB (для камеры), а другой — класс аудио USB (для микрофона). Операционная система не загружает ни одного «драйвера веб-камеры», а вместо этого загружает независимые драйверы видео и аудио классов, которые отвечают за отдельные функции устройства. Такой состав классов интерфейса обеспечивает большую гибкость.

Основы API

Многие стандартные классы USB имеют соответствующие веб-API. Например, страница может захватывать видео с устройства класса видео с помощью getUserMedia() или получать события ввода от устройства класса пользовательского интерфейса (HID), прослушивая KeyboardEvents или PointerEvents или используя Gamepad или WebHID API. Точно так же, как не все устройства реализуют стандартизированное определение класса, не все устройства реализуют функции, соответствующие существующим API-интерфейсам веб-платформы. В этом случае API WebUSB может восполнить этот пробел, предоставив сайтам возможность требовать интерфейс конкретного поставщика и реализовывать его поддержку непосредственно на своей странице.

Конкретные требования к устройству, доступному через WebUSB, немного различаются от платформы к платформе из-за различий в том, как операционные системы управляют USB-устройствами, но основное требование заключается в том, что на устройстве еще не должно быть драйвера, претендующего на интерфейс, которым страница хочет управлять. Это может быть либо универсальный драйвер класса, предоставленный поставщиком ОС, либо драйвер устройства, предоставленный поставщиком. Поскольку USB-устройства могут предоставлять несколько интерфейсов, каждый из которых может иметь свой собственный драйвер, можно создать устройство, для которого некоторые интерфейсы запрашиваются драйвером, а другие остаются доступными для браузера.

Например, высокопроизводительная USB-клавиатура может предоставлять интерфейс класса HID, который будет востребован подсистемой ввода операционной системы, и интерфейс, зависящий от поставщика, который остается доступным для WebUSB для использования инструментом настройки. Этот инструмент можно разместить на веб-сайте производителя, что позволяет пользователю изменять аспекты поведения устройства, такие как макроклавиши и световые эффекты, без установки какого-либо программного обеспечения для конкретной платформы. Дескриптор конфигурации такого устройства будет выглядеть примерно так:

Ценить Поле Описание
Дескриптор конфигурации
0x09 bДлина Размер этого дескриптора
0x02 бдескриптортипе Дескриптор конфигурации
0x0039 wTotalLength Общая длина этой серии дескрипторов
0x02 bNumInterfaces Количество интерфейсов
0x01 бConfigurationValue Конфигурация 1
0x00 iКонфигурация Имя конфигурации (нет)
0b1010000 бмАтрибуты Устройство с автономным питанием и удаленным пробуждением
0x32 бМаксПауэр Максимальная мощность выражается с шагом 2 мА.
Дескриптор интерфейса
0x09 bДлина Размер этого дескриптора
0x04 бдескриптортипе Дескриптор интерфейса
0x00 бИнтерфейсНомер Интерфейс 0
0x00 bАльтернативные настройки Альтернативная настройка 0 (по умолчанию)
0x01 бнумендпойнтс 1 конечная точка
0x03 бИнтерфейсКласс класс интерфейса HID
0x01 bInterfaceSubClass Подкласс загрузочного интерфейса
0x01 бПротокол интерфейса Клавиатура
0x00 iИнтерфейс Имя интерфейса (нет)
HID-дескриптор
0x09 bДлина Размер этого дескриптора
0x21 бдескриптортипе HID-дескриптор
0x0101 bcdHID СПРЯТАННАЯ версия 1.1
0x00 бКод страны Целевая страна оборудования
0x01 бнумдескрипторы Количество дескрипторов классов HID, которым необходимо следовать.
0x22 бдескриптортипе Тип дескриптора отчета
0x003F вдескрипторленгс Общая длина дескриптора отчета
Дескриптор конечной точки
0x07 bДлина Размер этого дескриптора
0x05 бдескриптортипе Дескриптор конечной точки
0b10000001 бEndpointAddress Конечная точка 1 (ИН)
0b00000011 бмАтрибуты Прерывать
0x0008 wMaxPacketSize 8-байтовые пакеты
0x0A bИнтервал интервал 10 мс
Дескриптор интерфейса
0x09 bДлина Размер этого дескриптора
0x04 бдескриптортипе Дескриптор интерфейса
0x01 бИнтерфейсНомер Интерфейс 1
0x00 bАльтернативные настройки Альтернативная настройка 0 (по умолчанию)
0x02 бнумендпойнтс 2 конечные точки
0xFF бИнтерфейсКласс Класс интерфейса, зависящий от поставщика
0x00 bInterfaceSubClass
0x00 бПротокол интерфейса
0x00 iИнтерфейс Имя интерфейса (нет)
Дескриптор конечной точки
0x07 bДлина Размер этого дескриптора
0x05 бдескриптортипе Дескриптор конечной точки
0b10000010 бEndpointAddress Конечная точка 1 (ИН)
0b00000010 бмАтрибуты Масса
0x0040 wMaxPacketSize пакеты по 64 байта
0x00 bИнтервал Н/Д для массовых конечных точек
Дескриптор конечной точки
0x07 bДлина Размер этого дескриптора
0x05 бдескриптортипе Дескриптор конечной точки
0b00000011 бEndpointAddress Конечная точка 3 (ВЫХОД)
0b00000010 бмАтрибуты Масса
0x0040 wMaxPacketSize пакеты по 64 байта
0x00 bИнтервал Н/Д для массовых конечных точек

Дескриптор конфигурации состоит из нескольких дескрипторов, объединенных вместе. Каждый из них начинается с полей bLength и bDescriptorType , чтобы их можно было идентифицировать. Первый интерфейс — это интерфейс HID со связанным дескриптором HID и единственной конечной точкой, используемой для доставки входных событий в операционную систему. Второй интерфейс — это интерфейс, зависящий от поставщика, с двумя конечными точками, которые можно использовать для отправки команд устройству и получения ответов в ответ.

Дескрипторы WebUSB

Хотя WebUSB может работать со многими устройствами без модификации прошивки, дополнительные функции включаются путем маркировки устройства специальными дескрипторами, указывающими на поддержку WebUSB. Например, вы можете указать URL-адрес целевой страницы, на которую браузер может направить пользователя, когда ваше устройство подключено.

Скриншот уведомления WebUSB в Chrome
Уведомление WebUSB.

Хранилище объектов двоичных устройств (BOS) — это концепция, представленная в USB 3.0, но также перенесенная на устройства USB 2.0 как часть версии 2.1. Объявление поддержки WebUSB начинается с включения следующего дескриптора возможностей платформы в дескриптор BOS:

Ценить Поле Описание
Дескриптор хранилища объектов двоичного устройства
0x05 bДлина Размер этого дескриптора
0x0F бдескриптортипе Дескриптор хранилища объектов двоичного устройства
0x001D wTotalLength Общая длина этой серии дескрипторов
0x01 бнумдевицекапс Количество дескрипторов возможностей устройства в BOS
Дескриптор возможностей платформы WebUSB
0x18 bДлина Размер этого дескриптора
0x10 бдескриптортипе Дескриптор возможностей устройства
0x05 бдевкапабилититипе Дескриптор возможностей платформы
0x00 bЗарезервировано
{0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} ПлатформаВозможностьUUID GUID дескриптора возможностей платформы WebUSB в формате с прямым порядком байтов
0x0100 bcdVersion Дескриптор WebUSB версии 1.0
0x01 бВендорКод bЗапросить значение для WebUSB
0x01 iLandingPage URL-адрес целевой страницы

UUID возможностей платформы идентифицирует это как дескриптор возможностей платформы WebUSB , который предоставляет базовую информацию об устройстве. Чтобы браузер мог получить дополнительную информацию об устройстве, он использует значение bVendorCode для отправки дополнительных запросов к устройству. В настоящее время указан единственный запрос — GET_URL , который возвращает дескриптор URL . Они похожи на строковые дескрипторы, но предназначены для кодирования URL-адресов в наименьшее количество байтов. Дескриптор URL-адреса "https://google.com" будет выглядеть следующим образом:

Ценить Поле Описание
URL-дескриптор
0x0D bДлина Размер этого дескриптора
0x03 бдескриптортипе URL-дескриптор
0x01 бСхема https://
"google.com" URL-адрес Содержимое URL-адреса в кодировке UTF-8

Когда ваше устройство впервые подключается к сети, браузер считывает дескриптор BOS, выдавая стандартную передачу управления GET_DESCRIPTOR :

bmRequestType бЗапрос wValue wИндекс wдлина Данные (ответ)
0b10000000 0x06 0x0F00 0x0000 * Дескриптор BOS

Этот запрос обычно выполняется дважды: первый раз с достаточно большим значением wLength , чтобы хост узнал значение поля wTotalLength без выполнения большой передачи, а затем еще раз, когда известна полная длина дескриптора.

Если в дескрипторе возможностей платформы WebUSB для поля iLandingPage установлено ненулевое значение, браузер затем выполняет запрос GET_URL , специфичный для WebUSB, выдавая передачу управления со значением bRequest , установленным в значение bVendorCode из дескриптора возможностей платформы, и wValue установленным в значение Значение iLandingPage . Код запроса GET_URL ( 0x02 ) находится в wIndex :

bmRequestType бЗапрос wValue wИндекс wдлина Данные (ответ)
0b11000000 0x01 0x0001 0x0002 * Дескриптор URL-адреса

Опять же, этот запрос может быть выдан дважды, чтобы сначала проверить длину считываемого дескриптора.

Особенности платформы

Хотя API WebUSB пытается обеспечить согласованный интерфейс для доступа к USB-устройствам, разработчикам все же следует учитывать требования, предъявляемые к приложениям, например, требования к веб-браузерам для доступа к устройствам.

macOS

Для macOS не требуется ничего особенного. Веб-сайт, использующий WebUSB, может подключиться к устройству и запросить любые интерфейсы, которые не востребованы драйвером ядра или другим приложением.

Линукс

Linux похож на macOS, но по умолчанию большинство дистрибутивов не настраивают учетные записи пользователей с разрешением на открытие USB-устройств. Системный демон udev отвечает за назначение пользователя и группы, которым разрешен доступ к устройству. Подобное правило назначит право собственности на устройство, соответствующее данному поставщику и идентификаторам продукта, группе plugdev , которая является общей группой для пользователей, имеющих доступ к периферийным устройствам:

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

Замените XXXX шестнадцатеричным идентификатором производителя и продукта для вашего устройства, например ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e11" будет соответствовать телефону Nexus One. Для правильного распознавания они должны быть записаны без обычного префикса «0x» и строчных букв. Чтобы найти идентификаторы вашего устройства, запустите инструмент командной строки lsusb .

Это правило должно быть помещено в файл в каталоге /etc/udev/rules.d и вступит в силу, как только устройство будет подключено. Нет необходимости перезапускать udev.

Андроид

Платформа Android основана на Linux, но не требует каких-либо изменений в конфигурации системы. По умолчанию браузеру доступно любое устройство, не имеющее встроенного в операционную систему драйвера. Однако разработчики должны знать, что пользователи столкнутся с дополнительным шагом при подключении к устройству. Как только пользователь выберет устройство в ответ на вызов requestDevice() , Android отобразит запрос с вопросом, разрешить ли Chrome доступ к нему. Это приглашение также появляется снова, если пользователь возвращается на веб-сайт, у которого уже есть разрешение на подключение к устройству, и веб-сайт вызывает open() .

Кроме того, на Android будет доступно больше устройств, чем на настольной Linux, поскольку по умолчанию включено меньше драйверов. Заметным упущением, например, является класс USB CDC-ACM, обычно реализуемый адаптерами USB-последовательный порт, поскольку в Android SDK нет API для связи с последовательным устройством.

ChromeOS

ChromeOS также основана на Linux и не требует каких-либо изменений в конфигурации системы. Служба Permission_broker контролирует доступ к USB-устройствам и разрешает браузеру доступ к ним, пока существует хотя бы один невостребованный интерфейс.

Окна

Модель драйвера Windows предъявляет дополнительное требование. В отличие от вышеперечисленных платформ возможность открытия USB-устройства из пользовательского приложения не используется по умолчанию, даже если драйвер не загружен. Вместо этого существует специальный драйвер WinUSB, который необходимо загрузить, чтобы обеспечить интерфейс, используемый приложениями для доступа к устройству. Это можно сделать либо с помощью специального файла информации о драйвере (INF), установленного в системе, либо путем изменения встроенного ПО устройства для предоставления дескрипторов совместимости ОС Microsoft во время перечисления.

Информационный файл драйвера (INF)

Файл информации о драйвере сообщает Windows, что делать при первом обнаружении устройства. Поскольку система пользователя уже включает в себя драйвер WinUSB, все, что необходимо, — это чтобы INF-файл связал вашего поставщика и идентификатор продукта с этим новым правилом установки. Файл ниже представляет собой базовый пример. Сохраните его в файл с расширением .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] настраивает набор DeviceInterfaceGUID для устройства. Каждый интерфейс устройства должен иметь GUID, чтобы приложение могло найти его и подключиться к нему через Windows API. Используйте командлет PowerShell New-Guid или онлайн-инструмент для создания случайного GUID.

В целях разработки инструмент Zadig предоставляет простой интерфейс для замены драйвера, загруженного для интерфейса USB, на драйвер WinUSB.

Дескрипторы совместимости ОС Microsoft

Описанный выше подход с использованием INF-файла является громоздким, поскольку требует предварительной настройки компьютера каждого пользователя. Windows 8.1 и более поздние версии предлагают альтернативу за счет использования пользовательских дескрипторов USB. Эти дескрипторы предоставляют операционной системе Windows информацию при первом подключении устройства, которая обычно включается в INF-файл.

После настройки дескрипторов WebUSB вы также можете легко добавить дескрипторы совместимости ОС Microsoft. Сначала расширите дескриптор BOS этим дополнительным дескриптором возможностей платформы. Обязательно обновите wTotalLength и bNumDeviceCaps , чтобы учесть это.

Ценить Поле Описание
Дескриптор возможностей платформы Microsoft OS 2.0
0x1C bДлина Размер этого дескриптора
0x10 бдескриптортипе Дескриптор возможностей устройства
0x05 бдевкапабилититипе Дескриптор возможностей платформы
0x00 bЗарезервировано
{0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F} ПлатформаВозможностьUUID GUID дескриптора совместимости платформы Microsoft OS 2.0 в формате с прямым порядком байтов.
0x06030000 dwWindowsVersion Минимальная совместимая версия Windows (Windows 8.1)
0x00B2 wMSOSDescriptorSetTotalLength Общая длина набора дескрипторов
0x02 bMS_VendorCode Значение bRequest для получения дополнительных дескрипторов Microsoft
0x00 бальтенумкод Устройство не поддерживает альтернативное перечисление

Как и в случае с дескрипторами WebUSB, вам необходимо выбрать значение bRequest , которое будет использоваться при передаче управления, связанной с этими дескрипторами. В этом примере я выбрал 0x02 . 0x07 , помещенный в wIndex , — это команда для получения набора дескрипторов Microsoft OS 2.0 с устройства.

bmRequestType бЗапрос wValue wИндекс wдлина Данные (ответ)
0b11000000 0x02 0x0000 0x0007 * Набор дескрипторов MS OS 2.0

USB-устройство может иметь несколько функций, поэтому первая часть набора дескрипторов описывает, с какой функцией связаны следующие свойства. В приведенном ниже примере настраивается интерфейс 1 составного устройства. Дескриптор предоставляет ОС две важные части информации об этом интерфейсе. Дескриптор совместимого идентификатора сообщает Windows, что это устройство совместимо с драйвером WinUSB. Дескриптор свойства реестра действует аналогично разделу [Dev_AddReg] приведенного выше примера INF, устанавливая свойство реестра для назначения этой функции GUID интерфейса устройства.

Ценить Поле Описание
Заголовок набора дескрипторов Microsoft OS 2.0
0x000A wдлина Размер этого дескриптора
0x0000 вдескриптортипе Дескриптор заголовка набора дескрипторов
0x06030000 dwWindowsVersion Минимальная совместимая версия Windows (Windows 8.1)
0x00B2 wTotalLength Общая длина набора дескрипторов
Заголовок подмножества конфигурации Microsoft OS 2.0
0x0008 wдлина Размер этого дескриптора
0x0001 вдескриптортипе Описание заголовка подмножества конфигурации.
0x00 бConfigurationValue Применяется к конфигурации 1 (индексируется с 0, несмотря на то, что конфигурации обычно индексируются с 1)
0x00 bЗарезервировано Должен быть установлен на 0
0x00A8 wTotalLength Общая длина подмножества, включая этот заголовок
Заголовок подмножества функций Microsoft OS 2.0
0x0008 wдлина Размер этого дескриптора
0x0002 вдескриптортипе Дескриптор заголовка подмножества функции
0x01 бПервыйИнтерфейс Первый интерфейс функции
0x00 bЗарезервировано Должен быть установлен на 0
0x00A0 wSubsetLength Общая длина подмножества, включая этот заголовок
Дескриптор идентификатора, совместимый с Microsoft OS 2.0
0x0014 wдлина Размер этого дескриптора
0x0003 вдескриптортипе Совместимый дескриптор идентификатора
"WINUSB\0\0" СовместимыйID Строка ASCII, дополненная до 8 байт
"\0\0\0\0\0\0\0\0" СубсовместимыйID Строка ASCII, дополненная до 8 байт
Дескриптор свойства реестра Microsoft OS 2.0
0x0084 wдлина Размер этого дескриптора
0x0004 вдескриптортипе Дескриптор свойства реестра
0x0007 впропертидататипе REG_MULTI_SZ
0x002A wPropertyNameLength Длина имени свойства
"DeviceInterfaceGUIDs\0" Имя свойства Имя свойства с нулевым терминатором в кодировке UTF-16LE.
0x0050 wPropertyDataLength Длина стоимости недвижимости
"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\0\0" PropertyData GUID плюс два нулевых терминатора в кодировке UTF-16LE.

Windows будет запрашивать эту информацию у устройства только один раз. Если устройство не отвечает действительными дескрипторами, оно не будет запрашивать повторно при следующем подключении устройства. Microsoft предоставила список записей реестра USB-устройств, описывающих записи реестра, созданные при перечислении устройства. При тестировании удалите записи, созданные для устройства, чтобы Windows снова попыталась прочитать дескрипторы.

Для получения дополнительной информации ознакомьтесь с сообщением в блоге Microsoft о том, как использовать эти дескрипторы.

Примеры

Пример кода, реализующего устройства с поддержкой WebUSB, включающие как дескрипторы WebUSB, так и дескрипторы ОС Microsoft, можно найти в следующих проектах: