Créer un appareil pour WebUSB

Créez un appareil pour exploiter pleinement l'API WebUSB.

Cet article explique comment créer un appareil pour exploiter pleinement l'API WebUSB. Pour une brève présentation de l'API elle-même, consultez Accéder aux appareils USB sur le Web.

Contexte

L'USB (Universal Serial Bus) est devenu l'interface physique la plus courante pour connecter des périphériques à des ordinateurs de bureau et mobiles. En plus de définir les caractéristiques électriques du bus et un modèle général de communication avec un appareil, les spécifications USB incluent un ensemble de spécifications de classe d'appareil. Il s'agit de modèles généraux que les fabricants d'appareils peuvent mettre en œuvre pour des classes spécifiques d'appareils telles que le stockage, l'audio, la vidéo, la mise en réseau, etc. L'avantage de ces spécifications de classe d'appareils est qu'un fournisseur de système d'exploitation peut implémenter un seul pilote basé sur la spécification de classe (un "pilote de classe") et que tout appareil implémentant cette classe sera compatible. Cela représente une nette amélioration par rapport à chaque fabricant qui devait écrire ses propres pilotes d'appareils.

Cependant, certains appareils ne correspondent pas à l'une de ces classes d'appareils standardisées. Un fabricant peut choisir d'étiqueter son appareil comme implémentant la classe spécifique au fournisseur. Dans ce cas, le système d'exploitation choisit le pilote d'appareil à charger en fonction des informations fournies dans le package de pilotes du fournisseur, généralement un ensemble d'ID de fournisseur et de produit connus pour implémenter un protocole spécifique au fournisseur.

Autre caractéristique de l'USB : les appareils peuvent fournir plusieurs interfaces à l'hôte auquel ils sont connectés. Chaque interface peut implémenter une classe standardisée ou être spécifique au fournisseur. Lorsqu'un système d'exploitation choisit les pilotes appropriés pour gérer l'appareil, chaque interface peut être revendiquée par un pilote différent. Par exemple, une webcam USB fournit généralement deux interfaces, l'une implémentant la classe vidéo USB (pour la caméra) et l'autre implémentant la classe audio USB (pour le micro). Le système d'exploitation ne charge pas un seul "pilote de webcam", mais des pilotes de classe vidéo et audio indépendants qui sont chargés des fonctions distinctes de l'appareil. Cette composition de classes d'interface offre une plus grande flexibilité.

Principes de base des API

De nombreuses classes USB standards ont des API Web correspondantes. Par exemple, une page peut capturer une vidéo à partir d'un appareil de classe vidéo à l'aide de getUserMedia() ou recevoir des événements d'entrée à partir d'un appareil de classe d'interface homme-machine (HID) en écoutant KeyboardEvents ou PointerEvents, ou à l'aide de l'API Gamepad ou WebHID. Tout comme tous les appareils n'implémentent pas une définition de classe standardisée, tous les appareils n'implémentent pas de fonctionnalités correspondant aux API de plates-formes Web existantes. Dans ce cas, l'API WebUSB peut combler cette lacune en permettant aux sites de revendiquer une interface spécifique au fournisseur et de l'implémenter directement sur leur page.

Les exigences spécifiques pour qu'un appareil soit accessible via WebUSB varient légèrement d'une plate-forme à l'autre en raison des différences de gestion des appareils USB par les systèmes d'exploitation. Toutefois, l'exigence de base est qu'un appareil ne doit pas déjà disposer d'un pilote revendiquant l'interface que la page souhaite contrôler. Il peut s'agir d'un pilote de classe générique fourni par le fournisseur de l'OS ou d'un pilote d'appareil fourni par le fournisseur. Étant donné que les appareils USB peuvent fournir plusieurs interfaces, chacune pouvant avoir son propre pilote, il est possible de créer un appareil pour lequel certaines interfaces sont revendiquées par un pilote et d'autres sont laissées accessibles au navigateur.

Par exemple, un clavier USB haut de gamme peut fournir une interface de classe HID qui sera revendiquée par le sous-système d'entrée du système d'exploitation, et une interface spécifique au fournisseur accessible via WebUSB pour être utilisée par un outil de configuration. Cet outil peut être diffusé sur le site Web du fabricant, ce qui permet à l'utilisateur de modifier certains aspects du comportement de l'appareil, tels que les touches de macro et les effets d'éclairage, sans avoir à installer de logiciel spécifique à la plate-forme. Le descripteur de configuration d'un tel appareil se présente comme suit:

Valeur Champ Description
Descripteur de configuration
0x09 bLength Taille de ce descripteur
0x02 bDescriptorType Descripteur de configuration
0x0039 wTotalLength Longueur totale de cette série de descripteurs
0x02 bNumInterfaces Nombre d'interfaces
0x01 bConfigurationValue Configuration 1
0x00 iConfiguration Nom de la configuration (aucun)
0b1010000 bmAttributes Appareil autonome avec activation à distance
0x32 bMaxPower La puissance maximale est exprimée par incréments de 2 mA.
Descripteur d'interface
0x09 bLength Taille de ce descripteur
0x04 bDescriptorType Descripteur d'interface
0x00 bInterfaceNumber Interface 0
0x00 bAlternateSetting Paramètre alternatif 0 (par défaut)
0x01 bNumEndpoints 1 point de terminaison
0x03 bInterfaceClass Classe d'interface HID
0x01 bInterfaceSubClass Sous-classe d'interface de démarrage
0x01 bInterfaceProtocol Clavier
0x00 iInterface Nom de l'interface (aucun)
Descripteur HID
0x09 bLength Taille de ce descripteur
0x21 bDescriptorType Descripteur HID
0x0101 bcdHID HID version 1.1
0x00 bCountryCode Pays cible du matériel
0x01 bNumDescriptors Nombre de descripteurs de classe HID à suivre
0x22 bDescriptorType Type de descripteur de rapport
0x003F wDescriptorLength Longueur totale du descripteur de rapport
Descripteur de point de terminaison
0x07 bLength Taille de ce descripteur
0x05 bDescriptorType Descripteur de point de terminaison
0b10000001 bEndpointAddress Point de terminaison 1 (Inde)
0b00000011 bmAttributes Interrompre
0x0008 wMaxPacketSize des paquets de 8 octets ;
0x0A bInterval Intervalle de 10 ms
Descripteur d'interface
0x09 bLength Taille de ce descripteur
0x04 bDescriptorType Descripteur d'interface
0x01 bInterfaceNumber Interface 1
0x00 bAlternateSetting Paramètre alternatif 0 (par défaut)
0x02 bNumEndpoints 2 points de terminaison
0xFF bInterfaceClass Classe d'interface spécifique au fournisseur
0x00 bInterfaceSubClass
0x00 bInterfaceProtocol
0x00 iInterface Nom de l'interface (aucun)
Descripteur de point de terminaison
0x07 bLength Taille de ce descripteur
0x05 bDescriptorType Descripteur de point de terminaison
0b10000010 bEndpointAddress Point de terminaison 1 (Inde)
0b00000010 bmAttributes Modifications groupées
0x0040 wMaxPacketSize Paquets de 64 octets
0x00 bInterval N/A pour les points de terminaison groupés
Descripteur de point de terminaison
0x07 bLength Taille de ce descripteur
0x05 bDescriptorType Descripteur de point de terminaison
0b00000011 bEndpointAddress Point de terminaison 3 (SORTIE)
0b00000010 bmAttributes Modifications groupées
0x0040 wMaxPacketSize Paquets de 64 octets
0x00 bInterval N/A pour les points de terminaison groupés

Le descripteur de configuration se compose de plusieurs descripteurs concatenatés. Chacun commence par les champs bLength et bDescriptorType afin de pouvoir être identifié. La première interface est une interface HID avec un descripteur HID associé et un seul point de terminaison utilisé pour transmettre des événements d'entrée au système d'exploitation. La deuxième interface est une interface spécifique au fournisseur avec deux points de terminaison qui peuvent être utilisés pour envoyer des commandes à l'appareil et recevoir des réponses en retour.

Descripteurs WebUSB

Bien que WebUSB puisse fonctionner avec de nombreux appareils sans modification du micrologiciel, des fonctionnalités supplémentaires sont activées en marquant l'appareil avec des descripteurs spécifiques indiquant la prise en charge de WebUSB. Par exemple, vous pouvez spécifier une URL de page de destination vers laquelle le navigateur peut rediriger l'utilisateur lorsque votre appareil est branché.

Capture d'écran de la notification WebUSB dans Chrome
Notification WebUSB.

Le BOS (Binary device Object Store) est un concept introduit dans USB 3.0, mais il a également été rétroporté vers les appareils USB 2.0 dans la version 2.1. La déclaration de la prise en charge de WebUSB commence par l'inclusion du descripteur de capacité de plate-forme suivant dans le descripteur BOS:

Valeur Champ Description
Descripteur du magasin d'objets de l'appareil binaire
0x05 bLength Taille de ce descripteur
0x0F bDescriptorType Descripteur du magasin d'objets de l'appareil binaire
0x001D wTotalLength Longueur totale de cette série de descripteurs
0x01 bNumDeviceCaps Nombre de descripteurs de capacité de l'appareil dans la BOS
Descripteur de capacité de la plate-forme WebUSB
0x18 bLength Taille de ce descripteur
0x10 bDescriptorType Descripteur de fonctionnalités de l'appareil
0x05 bDevCapabilityType Descripteur de capacité de la plate-forme
0x00 bReserved
{0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} PlatformCapablityUUID GUID de descripteur des fonctionnalités de la plate-forme WebUSB au format Little-Endian
0x0100 bcdVersion Déscripteur WebUSB version 1.0
0x01 bVendorCode Valeur bRequest pour WebUSB
0x01 iLandingPage URL de la page de destination

L'UUID des fonctionnalités de la plate-forme l'identifie comme un descripteur de capacité WebUSB, qui fournit des informations de base sur l'appareil. Pour que le navigateur récupère plus d'informations sur l'appareil, il utilise la valeur bVendorCode pour envoyer des requêtes supplémentaires à l'appareil. La seule requête actuellement spécifiée est GET_URL, qui renvoie un descripteur d'URL. Ils sont semblables aux descripteurs de chaîne, mais sont conçus pour encoder les URL avec un minimum d'octets. Un descripteur d'URL pour "https://google.com" ressemblerait à ceci:

Valeur Champ Description
Descripteur d'URL
0x0D bLength Taille de ce descripteur
0x03 bDescriptorType Descripteur d'URL
0x01 bScheme https://
"google.com" URL Contenu de l'URL encodé en UTF-8

Lorsque votre appareil est branché pour la première fois, le navigateur lit le descripteur BOS en émettant ce transfert de contrôle GET_DESCRIPTOR standard:

bmRequestType bRequest wValue wIndex wLength Données (réponse)
0b10000000 0x06 0x0F00 0x0000 * Descripteur BOS

Cette requête est généralement effectuée deux fois, la première fois avec un wLength suffisamment grand pour que l'hôte détermine la valeur du champ wTotalLength sans s'engager dans un transfert volumineux, puis une nouvelle fois lorsque la longueur complète du descripteur est connue.

Si le champ iLandingPage du descripteur de capacités de la plate-forme WebUSB est défini sur une valeur non nulle, le navigateur effectue ensuite une requête GET_URL spécifique à WebUSB en émettant un transfert de contrôle avec bRequest défini sur la valeur bVendorCode du descripteur de capacités de la plate-forme et wValue défini sur la valeur iLandingPage. Le code de requête pour GET_URL (0x02) est placé dans wIndex:

bmRequestType bRequest wValue wIndex wLength Données (réponse)
0b11000000 0x01 0x0001 0x0002 * Descripteur d'URL

Là encore, cette requête peut être envoyée deux fois afin de vérifier d'abord la longueur du descripteur lu.

Considérations spécifiques à la plate-forme

Bien que l'API WebUSB tente de fournir une interface cohérente pour accéder aux appareils USB, les développeurs doivent tout de même connaître les exigences imposées aux applications, telles que les exigences des navigateurs Web pour accéder aux appareils.

macOS

Rien de particulier n’est nécessaire pour macOS. Un site Web utilisant WebUSB peut se connecter à l'appareil et revendiquer toutes les interfaces qui ne sont pas revendiquées par un pilote de kernel ou une autre application.

Linux

Linux est semblable à macOS, mais par défaut, la plupart des distributions ne configurent pas les comptes utilisateur autorisés à ouvrir les appareils USB. Un démon système appelé udev est chargé d'attribuer l'utilisateur et le groupe autorisés à accéder à un appareil. Une règle de ce type attribue la propriété d'un appareil correspondant au fournisseur et aux ID de produit donnés au groupe plugdev, qui est un groupe commun pour les utilisateurs ayant accès aux périphériques:

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

Remplacez XXXX par les ID de produit et de fournisseur hexadécimaux de votre appareil. Par exemple, ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e11" correspond à un téléphone Nexus One. Pour être correctement reconnues, elles doivent être écrites sans le préfixe "0x" habituel et en minuscules. Pour trouver les ID de votre appareil, exécutez l'outil de ligne de commande lsusb.

Cette règle doit être placée dans un fichier du répertoire /etc/udev/rules.d et prend effet dès que l'appareil est branché. Il n'est pas nécessaire de redémarrer udev.

Android

La plate-forme Android est basée sur Linux, mais ne nécessite aucune modification de la configuration système. Par défaut, tout appareil qui ne dispose pas de pilote intégré au système d'exploitation est disponible pour le navigateur. Les développeurs doivent toutefois savoir que les utilisateurs devront suivre une étape supplémentaire lorsqu'ils se connecteront à l'appareil. Une fois qu'un utilisateur a sélectionné un appareil en réponse à un appel à requestDevice(), Android affiche une invite lui demandant d'autoriser Chrome à y accéder. Cette invite réapparaît également si un utilisateur revient sur un site Web déjà autorisé à se connecter à un appareil et que le site Web appelle open().

De plus, plus d'appareils seront accessibles sur Android que sur Linux pour ordinateur, car moins de pilotes sont inclus par défaut. Par exemple, la classe USB CDC-ACM, couramment implémentée par les adaptateurs USB-série, est une omission notable, car il n'existe aucune API dans le SDK Android pour communiquer avec un appareil série.

ChromeOS

ChromeOS est également basé sur Linux et ne nécessite aucune modification de la configuration système. Le service permission_broker contrôle l'accès aux appareils USB et permettra au navigateur d'y accéder tant qu'il existe au moins une interface non revendiquée.

Windows

Le modèle de pilote Windows introduit une exigence supplémentaire. Contrairement aux plates-formes ci-dessus, la possibilité d'ouvrir un périphérique USB à partir d'une application utilisateur n'est pas la fonctionnalité par défaut, même si aucun pilote n'est chargé. À la place, un pilote spécial, WinUSB, doit être chargé pour fournir l'interface que les applications utilisent pour accéder à l'appareil. Pour ce faire, vous pouvez installer un fichier d'informations sur le conducteur (INF) personnalisé sur le système ou modifier le micrologiciel de l'appareil pour fournir les descripteurs de compatibilité Microsoft OS lors de l'énumération.

Fichier d'informations sur le pilote (INF)

Un fichier d'informations sur le pilote indique à Windows ce qu'il doit faire lorsqu'il rencontre un appareil pour la première fois. Étant donné que le système de l'utilisateur inclut déjà le pilote WinUSB, il suffit que le fichier INF associe votre fournisseur et votre ID produit à cette nouvelle règle d'installation. Le fichier ci-dessous est un exemple de base. Enregistrez-le dans un fichier avec l'extension .inf, modifiez les sections marquées par "X", puis effectuez un clic droit dessus et sélectionnez "Installer" dans le menu contextuel.

[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"

La section [Dev_AddReg] configure l'ensemble des DeviceInterfaceGUIDs pour l'appareil. Chaque interface d'appareil doit disposer d'un GUID pour qu'une application puisse la trouver et s'y connecter via l'API Windows. Utilisez la cmdlet PowerShell New-Guid ou un outil en ligne pour générer un GUID aléatoire.

À des fins de développement, l'outil Zadig fournit une interface simple pour remplacer le pilote chargé pour une interface USB par le pilote WinUSB.

Descripteurs de compatibilité des systèmes d'exploitation Microsoft

L'approche de fichier INF ci-dessus est fastidieuse, car elle nécessite de configurer à l'avance la machine de chaque utilisateur. Windows 8.1 et les versions ultérieures proposent une alternative à l'aide de descripteurs USB personnalisés. Ces descripteurs fournissent des informations au système d'exploitation Windows, qui sont normalement incluses dans le fichier INF, lorsque l'appareil est branché pour la première fois.

Une fois les descripteurs WebUSB configurés, il est facile d'ajouter également les descripteurs de compatibilité de l'OS de Microsoft. Commencez par étendre le descripteur BOS avec ce descripteur de capacité de plate-forme supplémentaire. Veillez à mettre à jour wTotalLength et bNumDeviceCaps pour tenir compte de ce changement.

Valeur Champ Description
Descripteur de capacité de la plate-forme Microsoft OS 2.0
0x1C bLength Taille de ce descripteur
0x10 bDescriptorType Descripteur de fonctionnalités de l'appareil
0x05 bDevCapabilityType Descripteur de capacité de la plate-forme
0x00 bReserved
{0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F} PlatformCapablityUUID GUID du descripteur de compatibilité de la plate-forme Microsoft OS 2.0 au format little-endian
0x06030000 dwWindowsVersion Version Windows minimale compatible (Windows 8.1)
0x00B2 wMSOSDescriptorSetTotalLength Longueur totale de l'ensemble de descripteurs
0x02 bMS_VendorCode Valeur de la requête pour récupérer d'autres descripteurs Microsoft
0x00 bAltEnumCode L'appareil n'est pas compatible avec l'énumération alternative

Comme pour les descripteurs WebUSB, vous devez choisir une valeur bRequest à utiliser par les transferts de contrôle associés à ces descripteurs. Dans cet exemple, j'ai choisi 0x02. 0x07, placé dans wIndex, est la commande permettant de récupérer l'ensemble de descripteurs Microsoft OS 2.0 de l'appareil.

bmRequestType bRequest wValue wIndex wLength Données (réponse)
0b11000000 0x02 0x0000 0x0007 * Ensemble de descripteurs MS OS 2.0

Un appareil USB peut avoir plusieurs fonctions. La première partie de l'ensemble de descripteurs décrit donc la fonction à laquelle les propriétés suivantes sont associées. L'exemple ci-dessous configure l'interface 1 d'un appareil composite. Le descripteur donne au système d'exploitation deux informations importantes sur cette interface. Le descripteur d'ID compatible indique à Windows que cet appareil est compatible avec le pilote WinUSB. Le descripteur de propriété de Registre fonctionne de manière similaire à la section [Dev_AddReg] de l'exemple INF ci-dessus, en définissant une propriété de Registre pour attribuer à cette fonction un GUID d'interface d'appareil.

Valeur Champ Description
En-tête d'ensemble de descripteurs Microsoft OS 2.0
0x000A wLength Taille de ce descripteur
0x0000 wDescriptorType Descripteur de l'en-tête de l'ensemble de descripteurs
0x06030000 dwWindowsVersion Version Windows minimale compatible (Windows 8.1)
0x00B2 wTotalLength Longueur totale de l'ensemble de descripteurs
En-tête de sous-ensemble de configuration Microsoft OS 2.0
0x0008 wLength Taille de ce descripteur
0x0001 wDescriptorType Description de l'en-tête du sous-ensemble de configuration
0x00 bConfigurationValue S'applique à la configuration 1 (indexée à partir de 0, malgré les configurations normalement indexées à partir de 1)
0x00 bReserved Doit être défini sur 0
0x00A8 wTotalLength Longueur totale du sous-ensemble incluant cet en-tête
En-tête de sous-ensemble de fonctions Microsoft OS 2.0
0x0008 wLength Taille de ce descripteur
0x0002 wDescriptorType Descripteur d'en-tête de sous-ensemble de fonctions
0x01 bFirstInterface Première interface de la fonction
0x00 bReserved Doit être défini sur 0
0x00A0 wSubsetLength Longueur totale du sous-ensemble, y compris cet en-tête
Descripteur d'ID compatible avec Microsoft OS 2.0
0x0014 wLength Taille de ce descripteur
0x0003 wDescriptorType Descripteur d'ID compatible
"WINUSB\0\0" CompatibileID Chaîne ASCII remplie à 8 octets
"\0\0\0\0\0\0\0\0" SubCompatibleID Chaîne ASCII remplie à 8 octets
Descripteur de propriété de registre Microsoft OS 2.0
0x0084 wLength Taille de ce descripteur
0x0004 wDescriptorType Descripteur de propriété de registre
0x0007 wPropertyDataType REG_MULTI_SZ
0x002A wPropertyNameLength Longueur du nom de l'établissement
"DeviceInterfaceGUIDs\0" PropertyName Nom de propriété avec séparateur nul encodé en UTF-16LE
0x0050 wPropertyDataLength Longueur de la valeur de la propriété
"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\0\0" PropertyData GUID plus deux terminateurs nuls encodés en UTF-16LE

Windows ne demande ces informations à l'appareil qu'une seule fois. Si l'appareil ne répond pas avec des descripteurs valides, il ne demandera plus la prochaine fois qu'il sera connecté. Microsoft a fourni une liste d'entrées de registre d'appareils USB décrivant les entrées de registre créées lors de l'énumération d'un appareil. Lors des tests, supprimez les entrées créées pour un appareil afin de forcer Windows à réessayer de lire les descripteurs.

Pour en savoir plus, consultez l'article de blog de Microsoft sur l'utilisation de ces descripteurs.

Exemples

Vous trouverez dans les projets suivants des exemples de code mettant en œuvre des appareils compatibles WebUSB qui incluent à la fois des descripteurs WebUSB et des descripteurs d'OS Microsoft: