Creazione di un dispositivo per WebUSB

Crea un dispositivo per sfruttare appieno l'API WebUSB.

Questo articolo spiega come creare un dispositivo per sfruttare appieno l'API WebUSB. Per una breve introduzione all'API stessa, vedi Accedere ai dispositivi USB sul web.

Sfondo

La Universal Serial Bus (USB) è diventata l'interfaccia fisica più comune per collegare le periferiche ai computer desktop e mobili. Oltre a definire le caratteristiche elettriche del bus e un modello generale per la comunicazione con un dispositivo, le specifiche USB includono un insieme di specifiche per le classi di dispositivi. Si tratta di modelli generali per particolari classi di dispositivi, come archiviazione, audio, video, reti e così via, che i produttori di dispositivi possono implementare. Il vantaggio di queste specifiche di classe del dispositivo è che un fornitore di sistemi operativi può implementare un singolo driver in base alla specifica della classe ("driver di classe") e qualsiasi dispositivo che implementa quella classe sarà supportato. Si tratta di un grande miglioramento rispetto alla necessità di ogni produttore di scrivere i propri driver di dispositivo.

Tuttavia, alcuni dispositivi non rientrano in una di queste classi di dispositivi standardizzate. Un produttore può invece scegliere di etichettare il proprio dispositivo come implementante la classe specifica del fornitore. In questo caso, il sistema operativo sceglie quale driver del dispositivo caricare in base alle informazioni fornite nel pacchetto del driver del fornitore, in genere un insieme di ID fornitore e prodotto che implementano un determinato protocollo specifico del fornitore.

Un'altra funzionalità dell'USB è che i dispositivi possono fornire più interfacce all'host a cui sono collegati. Ogni interfaccia può implementare una classe standardizzata o essere specifica del fornitore. Quando un sistema operativo sceglie i driver giusti per gestire il dispositivo, ogni interfaccia può essere rivendicata da un driver diverso. Ad esempio, una webcam USB in genere fornisce due interfacce, una che implementa la classe video USB (per la videocamera) e una che implementa la classe audio USB (per il microfono). Il sistema operativo non carica un singolo "driver della webcam", ma carica driver di classi video e audio indipendenti che si occupano delle funzioni separate del dispositivo. Questa composizione di classi di interfaccia offre una maggiore flessibilità.

Nozioni di base sulle API

Molte delle classi USB standard hanno API web corrispondenti. Ad esempio, una pagina può acquisire video da un dispositivo di classe video utilizzando getUserMedia() o ricevere eventi di input da un dispositivo di classe di interfaccia umana (HID) ascoltando KeyboardEvents o PointerEvents oppure utilizzando l'API Gamepad o WebHID. Così come non tutti i dispositivi implementano una definizione di classe standardizzata, non tutti i dispositivi implementano funzionalità corrispondenti alle API della piattaforma web esistenti. In questi casi, l'API WebUSB può colmare questa lacuna offrendo ai siti un modo per rivendicare un'interfaccia specifica del fornitore e implementarne il supporto direttamente dalla loro pagina.

I requisiti specifici per l'accesso a un dispositivo tramite WebUSB variano leggermente da una piattaforma all'altra a causa delle differenze nel modo in cui i sistemi operativi gestiscono i dispositivi USB, ma il requisito di base è che un dispositivo non debba già avere un driver che rivendichi l'interfaccia che la pagina vuole controllare. Può trattarsi di un driver di classe generico fornito dal fornitore del sistema operativo o di un driver di dispositivo fornito dal fornitore. Poiché i dispositivi USB possono fornire più interfacce, ciascuna delle quali può avere il proprio driver, è possibile creare un dispositivo per cui alcune interfacce sono rivendicate da un driver e altre sono lasciate accessibili al browser.

Ad esempio, una tastiera USB di fascia alta può fornire un'interfaccia di classe HID che verrà rivendicata dal sottosistema di input del sistema operativo e un'interfaccia specifica del fornitore che rimane disponibile per WebUSB per essere utilizzata da uno strumento di configurazione. Questo strumento può essere pubblicato sul sito web del produttore e consente all'utente di modificare aspetti del comportamento del dispositivo, come i tasti macro e gli effetti di illuminazione, senza installare alcun software specifico per la piattaforma. Il descrittore di configurazione di un dispositivo di questo tipo sarà simile al seguente:

Valore Campo Descrizione
Descrittore della configurazione
0x09 bLength Dimensioni di questo descrittore
0x02 bDescriptorType Descrittore della configurazione
0x0039 wTotalLength Lunghezza totale di questa serie di descrittori
0x02 bNumInterfaces Numero di interfacce
0x01 bConfigurationValue Configurazione 1
0x00 iConfiguration Nome configurazione (nessuno)
0b1010000 bmAttributes Dispositivo autoalimentato con attivazione da remoto
0x32 bMaxPower La potenza massima è espressa in incrementi di 2 mA
Descrittore dell'interfaccia
0x09 bLength Dimensioni di questo descrittore
0x04 bDescriptorType Descrittore dell'interfaccia
0x00 bInterfaceNumber Interfaccia 0
0x00 bAlternateSetting Impostazione alternativa 0 (valore predefinito)
0x01 bNumEndpoints 1 endpoint
0x03 bInterfaceClass Classe di interfaccia HID
0x01 bInterfaceSubClass Sottoclasse dell'interfaccia di avvio
0x01 bInterfaceProtocol Tastiera
0x00 iInterface Nome interfaccia (nessuna)
Descrittore HID
0x09 bLength Dimensioni di questo descrittore
0x21 bDescriptorType Descrittore HID
0x0101 bcdHID HID versione 1.1
0x00 bCountryCode Paese di destinazione dell'hardware
0x01 bNumDescriptors Numero di descrittori di classe HID da seguire
0x22 bDescriptorType Tipo di descrittore report
0x003F wDescriptorLength Lunghezza totale del descrittore del report
Descrittore dell'endpoint
0x07 bLength Dimensioni di questo descrittore
0x05 bDescriptorType Descrittore dell'endpoint
0b10000001 bEndpointAddress Endpoint 1 (IN)
0b00000011 bmAttributes Interrompi
0x0008 wMaxPacketSize Pacchetti da 8 byte
0x0A bInterval Intervallo di 10 ms
Descrittore dell'interfaccia
0x09 bLength Dimensioni di questo descrittore
0x04 bDescriptorType Descrittore dell'interfaccia
0x01 bInterfaceNumber Interfaccia 1
0x00 bAlternateSetting Impostazione alternativa 0 (valore predefinito)
0x02 bNumEndpoints 2 endpoint
0xFF bInterfaceClass Classe di interfaccia specifica del fornitore
0x00 bInterfaceSubClass
0x00 bInterfaceProtocol
0x00 iInterface Nome interfaccia (nessuna)
Descrittore dell'endpoint
0x07 bLength Dimensioni di questo descrittore
0x05 bDescriptorType Descrittore dell'endpoint
0b10000010 bEndpointAddress Endpoint 1 (IN)
0b00000010 bmAttributes Collettivo
0x0040 wMaxPacketSize Pacchetti da 64 byte
0x00 bInterval N/D per gli endpoint collettivi
Descrittore dell'endpoint
0x07 bLength Dimensioni di questo descrittore
0x05 bDescriptorType Descrittore dell'endpoint
0b00000011 bEndpointAddress Endpoint 3 (OUT)
0b00000010 bmAttributes Collettivo
0x0040 wMaxPacketSize Pacchetti da 64 byte
0x00 bInterval N/D per gli endpoint collettivi

Il descrittore di configurazione è costituito da più descrittori concatenati tra loro. Ognuno inizia con i campi bLength e bDescriptorType per poter essere identificato. La prima interfaccia è un'interfaccia HID con un descrittore HID associato e un singolo endpoint utilizzato per inviare eventi di input al sistema operativo. La seconda interfaccia è un'interfaccia specifica del fornitore con due endpoint che possono essere utilizzati per inviare comandi al dispositivo e ricevere risposte in cambio.

Descrittori WebUSB

Sebbene WebUSB possa funzionare con molti dispositivi senza modifiche al firmware, la funzionalità aggiuntiva viene attivata contrassegnando il dispositivo con descrittori specifici che indicano il supporto di WebUSB. Ad esempio, puoi specificare un URL della pagina di destinazione a cui il browser può indirizzare l'utente quando il dispositivo è collegato alla corrente.

Screenshot della notifica WebUSB in Chrome
Notifica WebUSB
.

L'Object Store del dispositivo binario (BOS) è un concetto introdotto in USB 3.0, ma è stato anche sottoposto a backporting ai dispositivi USB 2.0 nell'ambito della versione 2.1. La dichiarazione del supporto per WebUSB inizia con l'inclusione del seguente descrittore della funzionalità della piattaforma nel descrittore BOS:

Valore Campo Descrizione
Descrittore dell'Object Store del dispositivo binario
0x05 bLength Dimensioni di questo descrittore
0x0F bDescriptorType Descrittore dell'Object Store del dispositivo binario
0x001D wTotalLength Lunghezza totale di questa serie di descrittori
0x01 bNumDeviceCaps Numero di descrittori delle funzionalità del dispositivo nel BOS
Descrittore delle funzionalità della piattaforma WebUSB
0x18 bLength Dimensioni di questo descrittore
0x10 bDescriptorType Descrittore delle funzionalità del dispositivo
0x05 bDevCapabilityType Descrittore delle funzionalità della piattaforma
0x00 bReserved
{0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} PlatformCapablityUUID GUID del descrittore delle funzionalità della piattaforma WebUSB in formato little-endian
0x0100 bcdVersion Descrittore WebUSB versione 1.0
0x01 bVendorCode Valore bRequest per WebUSB
0x01 iLandingPage URL pagina di destinazione

L'UUID della funzionalità della piattaforma lo identifica come un descrittore della funzionalità della piattaforma WebUSB, che fornisce informazioni di base sul dispositivo. Affinché il browser possa recuperare ulteriori informazioni sul dispositivo, utilizza il valore bVendorCode per inviare richieste aggiuntive al dispositivo. L'unica richiesta attualmente specificata è GET_URL che restituisce un descrittore URL. Sono simili ai descrittori di stringa, ma sono progettati per codificare gli URL nel minor numero di byte. Un descrittore URL per "https://google.com" avrà il seguente aspetto:

Valore Campo Descrizione
Descrittore URL
0x0D bLength Dimensioni di questo descrittore
0x03 bDescriptorType Descrittore URL
0x01 bScheme https://
"google.com" URL Contenuti dell'URL codificati in UTF-8

Quando il dispositivo viene collegato per la prima volta, il browser legge il descrittore BOS emettendo questo trasferimento di controllo GET_DESCRIPTOR standard:

bmRequestType bRequest wValue wIndex wLength Dati (risposta)
0b10000000 0x06 0x0F00 0x0000 * Il descrittore BOS

Questa richiesta viene solitamente effettuata due volte, la prima volta con un valore wLength sufficientemente grande da consentire all'host di scoprire il valore del campo wTotalLength senza impegnarsi in un trasferimento di grandi dimensioni e poi di nuovo quando è nota la lunghezza completa del descrittore.

Se il descrittore delle funzionalità della piattaforma WebUSB ha il campo iLandingPage impostato su un valore diverso da zero, il browser esegue una richiesta GET_URL specifica per WebUSB emettendo un trasferimento di controllo con bRequest impostato sul valore bVendorCode del descrittore delle funzionalità della piattaforma e wValue impostato sul valore iLandingPage. Il codice richiesta per GET_URL (0x02) va inserito in wIndex:

bmRequestType bRequest wValue wIndex wLength Dati (risposta)
0b11000000 0x01 0x0001 0x0002 * Il descrittore dell'URL

Anche questa richiesta può essere inviata due volte per verificare prima la lunghezza del descrittore in fase di lettura.

Considerazioni specifiche della piattaforma

Sebbene l'API WebUSB cerchi di fornire un'interfaccia coerente per accedere ai dispositivi USB, gli sviluppatori devono comunque essere a conoscenza dei requisiti imposti sulle applicazioni, ad esempio i requisiti dei browser web, per accedere ai dispositivi.

macOS

Non è necessario alcun requisito speciale per macOS. Un sito web che utilizza WebUSB può connettersi al dispositivo e rivendicare le interfacce non rivendicate da un driver del kernel o da un'altra applicazione.

Linux

Linux è simile a macOS, ma per impostazione predefinita la maggior parte delle distribuzioni non configura account utente con l'autorizzazione per aprire dispositivi USB. Un demone di sistema chiamato udev è responsabile dell'assegnazione dell'utente e del gruppo autorizzati ad accedere a un dispositivo. Una regola come questa assegna la proprietà di un dispositivo corrispondente agli ID fornitore e prodotto specificati al gruppo plugdev, un gruppo comune per gli utenti con accesso alle periferiche:

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

Sostituisci XXXX con gli ID prodotto e fornitore esadecimali del tuo dispositivo, ad esempio ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e11" corrisponde a uno smartphone Nexus One. Per essere riconosciuti correttamente, devono essere scritti senza il consueto prefisso "0x" e in minuscolo. Per trovare gli ID del tuo dispositivo, esegui lo strumento lsusbriga di comando.

Questa regola deve essere inserita in un file nella directory /etc/udev/rules.d e viene applicata non appena il dispositivo viene collegato all'alimentazione. Non è necessario riavviare udev.

Android

La piattaforma Android è basata su Linux, ma non richiede alcuna modifica alla configurazione del sistema. Per impostazione predefinita, qualsiasi dispositivo che non dispone di un driver integrato nel sistema operativo è disponibile per il browser. Gli sviluppatori devono tuttavia essere consapevoli che gli utenti dovranno eseguire un passaggio aggiuntivo per connettersi al dispositivo. Quando un utente seleziona un dispositivo in risposta a una chiamata a requestDevice(), Android mostra una richiesta di conferma per consentire a Chrome di accedervi. Questa richiesta viene visualizzata di nuovo anche se un utente torna su un sito web che ha già l'autorizzazione a connettersi a un dispositivo e il sito web chiama open().

Inoltre, su Android saranno accessibili più dispositivi rispetto a Linux per computer perché per impostazione predefinita sono inclusi meno driver. Un'omissione notevole, ad esempio, è la classe USB CDC-ACM comunemente implementata dagli adattatori USB-seriale, in quanto nell'SDK Android non è presente un'API per la comunicazione con un dispositivo seriale.

ChromeOS

Anche ChromeOS è basato su Linux e non richiede alcuna modifica alla configurazione di sistema. Il servizio permission_broker controlla l'accesso ai dispositivi USB e consente al browser di accedervi a condizione che sia presente almeno un'interfaccia non rivendicata.

Windows

Il modello di driver di Windows introduce un requisito aggiuntivo. A differenza delle piattaforme sopra indicate, la possibilità di aprire un dispositivo USB da un'applicazione utente non è predefinita, anche se non è caricato alcun driver. Esiste invece un driver speciale, WinUSB, che deve essere caricato per fornire l'interfaccia utilizzata dalle applicazioni per accedere al dispositivo. Questo può essere fatto con un file INF (driver information file) personalizzato installato sul sistema o modificando il firmware del dispositivo per fornire i descrittori di compatibilità del sistema operativo Microsoft durante l'enumerazione.

File INF (Driver Information File)

Un file di informazioni sul driver indica a Windows cosa fare quando rileva un dispositivo per la prima volta. Poiché il sistema dell'utente include già il driver WinUSB, è sufficiente che il file INF associ il fornitore e l'ID prodotto a questa nuova regola di installazione. Il file riportato di seguito è un esempio di base. Salvalo in un file con estensione .inf, modifica le sezioni contrassegnate con "X", quindi fai clic con il pulsante destro del mouse e scegli "Installa" dal menu contestuale.

[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 sezione [Dev_AddReg] configura l'insieme di DeviceInterfaceGUID per il dispositivo. Ogni interfaccia del dispositivo deve avere un GUID affinché un'applicazione possa trovarla e connettersi tramite l'API Windows. Utilizza il cmdlet New-Guid PowerShell o uno strumento online per generare un GUID casuale.

Per scopi di sviluppo, lo strumento Zadig fornisce un'interfaccia semplice per sostituire il driver caricato per un'interfaccia USB con il driver WinUSB.

Descrittori di compatibilità del sistema operativo Microsoft

L'approccio del file INF riportato sopra è complicato perché richiede la configurazione anticipata del computer di ogni utente. Windows 8.1 e versioni successive offrono un'alternativa tramite l'utilizzo di descrittori USB personalizzati. Questi descrittori forniscono al sistema operativo Windows informazioni che normalmente verrebbero incluse nel file INF quando il dispositivo viene collegato per la prima volta.

Una volta configurati i descrittori WebUSB, è facile aggiungere anche i descrittori di compatibilità del sistema operativo di Microsoft. Innanzitutto, espandi il descrittore BOS con questo descrittore aggiuntivo delle funzionalità della piattaforma. Assicurati di aggiornare wTotalLength e bNumDeviceCaps in base a questo.

Valore Campo Descrizione
Descrittore delle funzionalità della piattaforma Microsoft OS 2.0
0x1C bLength Dimensioni di questo descrittore
0x10 bDescriptorType Descrittore delle funzionalità del dispositivo
0x05 bDevCapabilityType Descrittore delle funzionalità della piattaforma
0x00 bReserved
{0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F} PlatformCapablityUUID GUID del descrittore di compatibilità della piattaforma Microsoft OS 2.0 in formato little-endian
0x06030000 dwWindowsVersion Versione minima di Windows compatibile (Windows 8.1)
0x00B2 wMSOSDescriptorSetTotalLength Lunghezza totale dell'insieme di descrittori
0x02 bMS_VendorCode Valore bRequest per il recupero di ulteriori descrittori Microsoft
0x00 bAltEnumCode Il dispositivo non supporta l'enumerazione alternativa

Come per i descrittori WebUSB, devi scegliere un valore bRequest da utilizzare per i trasferimenti di controllo relativi a questi descrittori. In questo esempio ho scelto 0x02. 0x07, inserito in wIndex, è il comando per recuperare il set di descrittori del sistema operativo Microsoft 2.0 dal dispositivo.

bmRequestType bRequest wValue wIndex wLength Dati (risposta)
0b11000000 0x02 0x0000 0x0007 * Set di descrittori MS OS 2.0

Un dispositivo USB può avere più funzioni, pertanto la prima parte dell'insieme di descrittori descrive a quale funzione sono associate le proprietà che seguono. L'esempio riportato di seguito configura l'interfaccia 1 di un dispositivo composito. Il descrittore fornisce al sistema operativo due informazioni importanti su questa interfaccia. Il descrittore ID compatibile indica a Windows che questo dispositivo è compatibile con il driver WinUSB. Il descrittore della proprietà del registro funziona in modo simile alla sezione [Dev_AddReg] dell'esempio INF riportato sopra, impostando una proprietà del registro per assegnare a questa funzione un GUID dell'interfaccia del dispositivo.

Valore Campo Descrizione
Intestazione del set di descrittori del sistema operativo Microsoft 2.0
0x000A wLength Dimensioni di questo descrittore
0x0000 wDescriptorType Descrittore dell'intestazione del set di descrittori
0x06030000 dwWindowsVersion Versione minima di Windows compatibile (Windows 8.1)
0x00B2 wTotalLength Lunghezza totale dell'insieme di descrittori
Intestazione del sottoinsieme di configurazione del sistema operativo Microsoft 2.0
0x0008 wLength Dimensioni di questo descrittore
0x0001 wDescriptorType Descrizione dell'intestazione del sottoinsieme di configurazione.
0x00 bConfigurationValue Si applica alla configurazione 1 (indicizzata da 0 nonostante le configurazioni siano normalmente indicizzate da 1)
0x00 bReserved Deve essere impostato su 0
0x00A8 wTotalLength Lunghezza totale del sottoinsieme inclusa questa intestazione
Intestazione del sottoinsieme di funzioni del sistema operativo Microsoft 2.0
0x0008 wLength Dimensioni di questo descrittore
0x0002 wDescriptorType Descrittore dell'intestazione del sottoinsieme di funzioni
0x01 bFirstInterface Prima interfaccia della funzione
0x00 bReserved Deve essere impostato su 0
0x00A0 wSubsetLength Lunghezza totale del sottoinsieme inclusa questa intestazione
Descrittore ID compatibile con il sistema operativo Microsoft 2.0
0x0014 wLength Dimensioni di questo descrittore
0x0003 wDescriptorType Descrittore ID compatibile
"WINUSB\0\0" CompatibileID Stringa ASCII riempita fino a 8 byte
"\0\0\0\0\0\0\0\0" SubCompatibleID Stringa ASCII riempita fino a 8 byte
Descrittore della proprietà del registro Microsoft OS 2.0
0x0084 wLength Dimensioni di questo descrittore
0x0004 wDescriptorType Descrittore della proprietà del registry
0x0007 wPropertyDataType REG_MULTI_SZ
0x002A wPropertyNameLength Lunghezza del nome della proprietà
"DeviceInterfaceGUIDs\0" PropertyName Nome della proprietà con terminatore nullo codificato in UTF-16LE
0x0050 wPropertyDataLength Lunghezza del valore della proprietà
"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\0\0" PropertyData GUID più due terminatori nullo codificati in UTF-16LE

Windows eseguirà una query sul dispositivo per queste informazioni una sola volta. Se il dispositivo non risponde con descrittori validi, la richiesta non verrà ripetuta alla successiva connessione del dispositivo. Microsoft ha fornito un elenco di voci del Registro di dispositivi USB che descrivono le voci del Registro create durante l'enumerazione di un dispositivo. Durante il test, elimina le voci create per un dispositivo per forzare Windows a tentare di leggere di nuovo i descrittori.

Per ulteriori informazioni, consulta il post del blog di Microsoft su come utilizzare questi descrittori.

Esempi

Il codice di esempio che implementa i dispositivi compatibili con WebUSB che includono sia i descrittori WebUSB sia i descrittori del sistema operativo Microsoft è disponibile in questi progetti: