Crie um dispositivo para aproveitar ao máximo a API WebUSB.
Este artigo explica como criar um dispositivo para aproveitar ao máximo a API WebUSB. Para uma breve introdução à API, consulte Acessar dispositivos USB na Web.
Contexto
O USB (Universal Serial Bus) se tornou a interface física mais comum para conectar periféricos a dispositivos de computação móvel e desktop. Além de definir as características elétricas do barramento e um modelo geral para se comunicar com um dispositivo, as especificações de USB incluem um conjunto de especificações de classe de dispositivo. Esses são modelos gerais para classes específicas de dispositivos, como armazenamento, áudio, vídeo, rede etc. que os fabricantes podem implementar. A vantagem dessas especificações de classe de dispositivo é que um fornecedor de sistema operacional pode implementar um único driver com base na especificação da classe (um "driver de classe"), e qualquer dispositivo que implemente essa classe terá suporte. Essa foi uma grande melhoria em relação a cada fabricante que precisava criar os próprios drivers de dispositivo.
No entanto, alguns dispositivos não se encaixam em uma dessas classes de dispositivos padronizadas. Em vez disso, um fabricante pode optar por marcar o dispositivo como implementando a classe específica do fornecedor. Nesse caso, o sistema operacional escolhe qual driver de dispositivo carregar com base nas informações fornecidas no pacote de drivers do fornecedor, geralmente um conjunto de IDs de fornecedores e produtos, conhecidos por implementar um protocolo específico do fornecedor.
Outro recurso do USB é que os dispositivos podem fornecer várias interfaces para o host ao qual estão conectados. Cada interface pode implementar uma classe padronizada ou ser específica do fornecedor. Quando um sistema operacional escolhe os drivers certos para lidar com o dispositivo, cada interface pode ser reivindicada por um driver diferente. Por exemplo, uma webcam USB geralmente fornece duas interfaces, uma implementando a classe de vídeo USB (para a câmera) e outra implementando a classe de áudio USB (para o microfone). O sistema operacional não carrega um único "driver de webcam", mas sim drivers independentes de classe de vídeo e áudio que assumem a responsabilidade pelas funções separadas do dispositivo. Essa composição de classes de interface oferece maior flexibilidade.
Princípios básicos da API
Muitas das classes USB padrão têm APIs da Web correspondentes. Por exemplo, uma
página pode capturar vídeos de um dispositivo de classe de vídeo usando getUserMedia()
ou receber eventos de entrada de um dispositivo de classe de interface humana (HID, na sigla em inglês) detectando
KeyboardEvents ou PointerEvents ou usando a API Gamepad ou a
API WebHID.
Assim como nem todos os dispositivos implementam uma definição de classe padronizada, nem todos os dispositivos implementam recursos que correspondem às APIs da plataforma da Web existentes. Quando
esse é o caso, a API WebUSB pode preencher essa lacuna oferecendo aos sites uma maneira de os sites
reivindicarem uma interface específica do fornecedor e implementarem suporte a ela diretamente
na página.
Os requisitos específicos para que um dispositivo seja acessível via WebUSB variam um pouco de plataforma para plataforma devido a diferenças na forma como os sistemas operacionais gerenciam dispositivos USB, mas o requisito básico é que o dispositivo ainda não tenha um driver, alegando a interface que a página quer controlar. Pode ser um driver de classe genérico fornecido pelo fornecedor do SO ou um driver de dispositivo fornecido pelo fornecedor. Como os dispositivos USB podem fornecer várias interfaces, cada uma com o próprio driver, é possível criar um dispositivo em que algumas interfaces são reivindicadas por um driver e outras ficam acessíveis ao navegador.
Por exemplo, um teclado USB de última geração pode fornecer uma interface de classe HID que será reivindicada pelo subsistema de entrada do sistema operacional e uma interface específica do fornecedor que permanece disponível para o WebUSB para uso por uma ferramenta de configuração. Essa ferramenta pode ser disponibilizada no site do fabricante, permitindo que o usuário mude aspectos do comportamento do dispositivo, como teclas de macro e efeitos de iluminação, sem instalar nenhum software específico da plataforma. O descritor de configuração desse dispositivo seria mais ou menos assim:
Valor | Campo | Descrição |
---|---|---|
Descritor de configuração | ||
0x09 |
bLength | Tamanho deste descritor |
0x02 |
bDescriptorType | Descritor de configuração |
0x0039 |
wTotalLength | Comprimento total desta série de descritores |
0x02 |
bNumInterfaces | Número de interfaces |
0x01 |
bConfigurationValue | Configuração 1 |
0x00 |
iConfiguration | Nome da configuração (nenhum) |
0b1010000 |
bmAttributes | Dispositivo autoligado com ativação remota |
0x32 |
bMaxPower | A potência máxima é expressa em incrementos de 2 mA |
Descritor de interface | ||
0x09 |
bLength | Tamanho deste descritor |
0x04 |
bDescriptorType | Descritor de interface |
0x00 |
bInterfaceNumber | Interface 0 |
0x00 |
bAlternateSetting | Configuração alternativa 0 (padrão) |
0x01 |
bNumEndpoints | 1 endpoint |
0x03 |
bInterfaceClass | Classe de interface HID |
0x01 |
bInterfaceSubClass | Subclasse da interface de inicialização |
0x01 |
bInterfaceProtocol | Teclado |
0x00 |
iInterface | Nome da interface (nenhum) |
Descritor HID | ||
0x09 |
bLength | Tamanho deste descritor |
0x21 |
bDescriptorType | Descritor HID |
0x0101 |
bcdHID | HID versão 1.1 |
0x00 |
bCountryCode | País de destino do hardware |
0x01 |
bNumDescriptors | Número de descritores de classe HID a serem seguidos |
0x22 |
bDescriptorType | Tipo de descritor do relatório |
0x003F |
wDescriptorLength | Comprimento total do descritor do relatório |
Descritor de endpoint | ||
0x07 |
bLength | Tamanho deste descritor |
0x05 |
bDescriptorType | Descritor de endpoint |
0b10000001 |
bEndpointAddress | Endpoint 1 (Índia) |
0b00000011 |
bmAttributes | Interromper |
0x0008 |
wMaxPacketSize | Pacotes de 8 bytes |
0x0A |
bInterval | Intervalo de 10 ms |
Descritor de interface | ||
0x09 |
bLength | Tamanho deste descritor |
0x04 |
bDescriptorType | Descritor de interface |
0x01 |
bInterfaceNumber | Interface 1 |
0x00 |
bAlternateSetting | Configuração alternativa 0 (padrão) |
0x02 |
bNumEndpoints | 2 endpoints |
0xFF |
bInterfaceClass | Classe de interface específica do fornecedor |
0x00 |
bInterfaceSubClass | |
0x00 |
bInterfaceProtocol | |
0x00 |
iInterface | Nome da interface (nenhum) |
Descritor de endpoint | ||
0x07 |
bLength | Tamanho deste descritor |
0x05 |
bDescriptorType | Descritor de endpoint |
0b10000010 |
bEndpointAddress | Endpoint 1 (Índia) |
0b00000010 |
bmAttributes | Em massa |
0x0040 |
wMaxPacketSize | Pacotes de 64 bytes |
0x00 |
bInterval | N/A para endpoints em massa |
Descritor de endpoint | ||
0x07 |
bLength | Tamanho deste descritor |
0x05 |
bDescriptorType | Descritor de endpoint |
0b00000011 |
bEndpointAddress | Endpoint 3 (OUT) |
0b00000010 |
bmAttributes | Em massa |
0x0040 |
wMaxPacketSize | Pacotes de 64 bytes |
0x00 |
bInterval | N/A para endpoints em massa |
O descritor de configuração consiste em vários descritores concatenados. Cada um começa com os campos bLength
e bDescriptorType
para que possam ser identificados. A primeira interface é uma interface HID com um descritor
HID associado e um único endpoint usado para entregar eventos de entrada ao
sistema operacional. A segunda é uma interface específica do fornecedor com dois
endpoints que podem ser usados para enviar comandos ao dispositivo e receber respostas
em troca.
Descritores WebUSB
Embora o WebUSB possa funcionar com muitos dispositivos sem modificações de firmware, outra funcionalidade é ativada marcando o dispositivo com descritores específicos que indicam suporte a WebUSB. Por exemplo, é possível especificar um URL de página de destino para o qual o navegador poderá direcionar o usuário quando o dispositivo for conectado.
O Binary Device Object Store (BOS) é um conceito introduzido no USB 3.0, mas que também passou por backport para dispositivos USB 2.0 como parte da versão 2.1. A declaração de suporte ao WebUSB começa com a inclusão do seguinte descritor de recursos da plataforma no descritor do BOS:
Valor | Campo | Descrição |
---|---|---|
Descritor do armazenamento de objetos do dispositivo binário | ||
0x05 |
bLength | Tamanho deste descritor |
0x0F |
bDescriptorType | Descritor do armazenamento de objetos do dispositivo binário |
0x001D |
wTotalLength | Comprimento total desta série de descritores |
0x01 |
bNumDeviceCaps | Número de descritores de capacidade do dispositivo no BOS |
Descritor de capacidade da plataforma WebUSB | ||
0x18 |
bLength | Tamanho deste descritor |
0x10 |
bDescriptorType | Descritor da capacidade do dispositivo |
0x05 |
bDevCapabilityType | Descritor de capacidade da plataforma |
0x00 |
bReserved | |
{0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} |
PlatformCapablityUUID | Descritor do GUID da plataforma WebUSB no formato few-endian |
0x0100 |
bcdVersion | Descritor WebUSB versão 1.0 |
0x01 |
bVendorCode | Valor de bRequest para WebUSB |
0x01 |
iLandingPage | URL da página de destino |
O UUID de capacidade da plataforma o identifica como um descritor de capacidade da plataforma WebUSB (link em inglês), que fornece informações básicas sobre o dispositivo. Para que o navegador
busque mais informações sobre o dispositivo, ele usa o valor bVendorCode
para
emitir outras solicitações ao dispositivo. A única solicitação especificada no momento é
GET_URL
, que retorna um descritor de URL. Eles são semelhantes aos descritores
de string, mas são projetados para codificar URLs no menor número de bytes. Um descritor
de URL para "https://google.com"
seria assim:
Valor | Campo | Descrição |
---|---|---|
Descritor de URL | ||
0x0D |
bLength | Tamanho deste descritor |
0x03 |
bDescriptorType | Descritor de URL |
0x01 |
bScheme | https:// |
"google.com" |
URL | Conteúdo do URL codificado em UTF-8 |
Quando o dispositivo é conectado pela primeira vez, o navegador lê o descritor do BOS
emitindo essa transferência de controle GET_DESCRIPTOR
padrão:
bmRequestType | bRequest | wValue | wIndex | wLength | Dados (resposta) |
---|---|---|---|---|---|
0b10000000 |
0x06 |
0x0F00 |
0x0000 |
* | O descritor BOS |
Essa solicitação geralmente é feita duas vezes, a primeira com um wLength
grande o suficiente para que o host descubra o valor do campo wTotalLength
sem se comprometer com uma transferência grande e novamente quando o tamanho completo do descritor for conhecido.
Se o descritor de recursos da plataforma WebUSB tiver o campo iLandingPage
definido como
um valor diferente de zero, o navegador vai executar uma solicitação GET_URL
específica do WebUSB
emitindo uma transferência de controle com o bRequest
definido como o valor bVendorCode
do descritor de capacidade da plataforma e wValue
definido como o valor
iLandingPage
. O código de solicitação para GET_URL
(0x02
) é enviado em wIndex
:
bmRequestType | bRequest | wValue | wIndex | wLength | Dados (resposta) |
---|---|---|---|---|---|
0b11000000 |
0x01 |
0x0001 |
0x0002 |
* | O descritor de URL |
Novamente, essa solicitação pode ser emitida duas vezes para verificar primeiro o tamanho do descritor que está sendo lido.
Considerações específicas da plataforma
Embora a API WebUSB tente fornecer uma interface consistente para acessar os desenvolvedores de dispositivos USB, os desenvolvedores ainda precisam estar cientes dos requisitos impostos a aplicativos, como os requisitos de navegadores da Web, para acessar os dispositivos.
macOS
Nada especial é necessário para o macOS. Um site que usa WebUSB pode se conectar ao dispositivo e reivindicar qualquer interface que não seja reivindicada por um driver do kernel ou outro aplicativo.
Linux
O Linux é como o macOS, mas, por padrão, a maioria das distribuições não configura contas de
usuário com permissão para abrir dispositivos USB. Um daemon do sistema chamado udev é
responsável por atribuir ao usuário e ao grupo com permissão para acessar um dispositivo. Uma regra
como essa atribuirá a propriedade de um dispositivo correspondente ao fornecedor e
ID de produto fornecidos ao grupo plugdev
, que é um grupo comum para usuários com acesso
a periféricos:
SUBSYSTEM=="usb", ATTR{idVendor}=="XXXX", ATTR{idProduct}=="XXXX", GROUP="plugdev"
Substitua XXXX
pelos IDs de fornecedor e de produto hexadecimais do seu dispositivo.
Por exemplo, ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e11"
corresponderia a um smartphone Nexus One. Eles precisam ser escritos sem o prefixo normal "0x" e todas as letras minúsculas
para serem reconhecidos corretamente. Para encontrar os IDs do seu dispositivo, execute a ferramenta de
linha de comando lsusb
.
Essa regra precisa ser colocada em um arquivo no diretório /etc/udev/rules.d
e
entra em vigor assim que o dispositivo é conectado. Não é necessário reiniciar o udev.
Android
A plataforma Android é baseada em Linux, mas não requer nenhuma modificação na
configuração do sistema. Por padrão, qualquer dispositivo que não tenha um driver integrado ao sistema operacional fica disponível para o navegador. No entanto, os desenvolvedores precisam estar cientes de que os usuários vão encontrar uma etapa extra ao se conectar ao dispositivo. Quando um usuário seleciona um dispositivo em resposta a uma chamada para
requestDevice()
, o Android exibe uma solicitação perguntando se permite
que o Chrome o acesse. Essa solicitação também reaparece se um usuário retornar a um site
que já tem permissão para se conectar a um dispositivo e o site chamar
open()
.
Além disso, mais dispositivos estarão acessíveis no Android do que no Linux para computador, porque menos drivers são incluídos por padrão. Uma omissão importante, por exemplo, é a classe USB CDC-ACM geralmente implementada por adaptadores USB para serial, já que não há API no SDK do Android para se comunicar com um dispositivo serial.
ChromeOS
O ChromeOS também é baseado em Linux e não requer nenhuma modificação na configuração do sistema. O serviço allow_broker controla o acesso a dispositivos USB e permite que o navegador os acesse, desde que haja pelo menos uma interface não reivindicada.
Windows
O modelo de driver do Windows apresenta um requisito adicional. Ao contrário das plataformas acima da capacidade de abrir um dispositivo USB em um app do usuário, não é o padrão, mesmo que nenhum driver seja carregado. Em vez disso, há um driver especial, o WinUSB, que precisa ser carregado para fornecer a interface usada pelos aplicativos para acessar o dispositivo. Isso pode ser feito com um arquivo de informações de driver personalizado (INF) instalado no sistema ou modificando o firmware do dispositivo para fornecer os descritores de compatibilidade do SO Microsoft durante a enumeração.
Arquivo de informações do driver (INF)
Um arquivo de informações do driver informa ao Windows o que fazer ao encontrar um dispositivo
pela primeira vez. Como o sistema do usuário já inclui o driver WinUSB,
basta que o arquivo INF associe seu fornecedor e ID do produto
a essa nova regra de instalação. O arquivo abaixo é um exemplo básico. Salve-o em um
arquivo com a extensão .inf
, altere as seções marcadas com "X", clique com o botão direito
nessa página e escolha "Instalar" no menu de contexto.
[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"
A seção [Dev_AddReg]
configura o conjunto de DeviceInterfaceGUIDs para o
dispositivo. Cada interface do dispositivo precisa ter um GUID para que um aplicativo possa encontrá-lo e se conectar a ele usando a API do Windows. Use o cmdlet New-Guid
do PowerShell
ou uma ferramenta on-line para gerar um GUID aleatório.
Para fins de desenvolvimento, a ferramenta Zadig fornece uma interface fácil para substituir o driver carregado para uma interface USB pelo driver WinUSB.
Descritores de compatibilidade do SO Microsoft
A abordagem do arquivo INF acima é complicada, porque exige a configuração prévia da máquina de cada usuário. O Windows 8.1 e versões mais recentes oferecem uma alternativa pelo uso de descritores USB personalizados. Esses descritores fornecem informações ao sistema operacional Windows quando o dispositivo é conectado pela primeira vez, e essas informações normalmente são incluídas no arquivo INF.
Depois de configurar os descritores WebUSB, é fácil também adicionar os descritores
de compatibilidade do SO da Microsoft. Primeiro, estenda o descritor de BOS com esse
descritor de recurso de plataforma adicional. Atualize wTotalLength
e bNumDeviceCaps
para considerar isso.
Valor | Campo | Descrição |
---|---|---|
Descritor de capacidade da plataforma Microsoft OS 2.0 | ||
0x1C |
bLength | Tamanho deste descritor |
0x10 |
bDescriptorType | Descritor da capacidade do dispositivo |
0x05 |
bDevCapabilityType | Descritor de capacidade da plataforma |
0x00 |
bReserved | |
{0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F} |
PlatformCapablityUUID | GUID do descritor de compatibilidade da plataforma Microsoft OS 2.0 no formato few-endian |
0x06030000 |
dwWindowsVersion | Versão mínima compatível do Windows (Windows 8.1) |
0x00B2 |
wMSOSDescriptorSetTotalLength | Comprimento total do conjunto do descritor |
0x02 |
bMS_VendorCode | Valor bRequest para recuperar mais descritores da Microsoft |
0x00 |
bAltEnumCode | O dispositivo não oferece suporte à enumeração alternativa |
Assim como nos descritores WebUSB, você precisa escolher um valor bRequest
para ser usado pelas
transferências de controle relacionadas a esses descritores. Neste exemplo, escolhi 0x02
. 0x07
, colocado em wIndex
, é o comando para recuperar o conjunto de descritores do
Microsoft OS 2.0 do dispositivo.
bmRequestType | bRequest | wValue | wIndex | wLength | Dados (resposta) |
---|---|---|---|---|---|
0b11000000 |
0x02 |
0x0000 |
0x0007 |
* | Conjunto de descritores do MS OS 2.0 |
Um dispositivo USB pode ter várias funções. Portanto, a primeira parte do conjunto
de descritores descreve com qual função as propriedades a seguir estão associadas. O exemplo abaixo configura a interface 1 de um dispositivo composto. O descritor fornece ao SO duas informações importantes sobre essa interface. O descritor
de ID compatível informa ao Windows que esse dispositivo é compatível com o driver
WinUSB. O descritor de propriedade do registro funciona de maneira semelhante à
seção [Dev_AddReg]
do exemplo INF acima, configurando uma propriedade de registro para
atribuir a essa função um GUID da interface do dispositivo.
Valor | Campo | Descrição |
---|---|---|
Cabeçalho do conjunto de descritor do Microsoft OS 2.0 | ||
0x000A |
wLength | Tamanho deste descritor |
0x0000 |
wDescriptorType | Descritor do cabeçalho definido |
0x06030000 |
dwWindowsVersion | Versão mínima compatível do Windows (Windows 8.1) |
0x00B2 |
wTotalLength | Comprimento total do conjunto do descritor |
Cabeçalho do subconjunto de configuração do Microsoft OS 2.0 | ||
0x0008 |
wLength | Tamanho deste descritor |
0x0001 |
wDescriptorType | Descrição do cabeçalho do subconjunto de configuração |
0x00 |
bConfigurationValue | Aplica-se à configuração 1 (indexada de 0, apesar das configurações normalmente indexadas de 1) |
0x00 |
bReserved | Precisa ser definido como 0. |
0x00A8 |
wTotalLength | Comprimento total do subconjunto, incluindo este cabeçalho |
Cabeçalho do subconjunto de funções do Microsoft OS 2.0 | ||
0x0008 |
wLength | Tamanho deste descritor |
0x0002 |
wDescriptorType | Descritor do cabeçalho do subconjunto de função |
0x01 |
bFirstInterface | Primeira interface da função |
0x00 |
bReserved | Precisa ser definido como 0. |
0x00A0 |
wSubsetLength | Comprimento total do subconjunto, incluindo este cabeçalho |
Descritor de ID compatível com o Microsoft OS 2.0 | ||
0x0014 |
wLength | Tamanho deste descritor |
0x0003 |
wDescriptorType | Descritor de ID compatível |
"WINUSB\0\0" |
CompatibileID | String ASCII preenchida em 8 bytes |
"\0\0\0\0\0\0\0\0" |
SubCompatibleID | String ASCII preenchida em 8 bytes |
Descritor de propriedade do registro do Microsoft OS 2.0 | ||
0x0084 |
wLength | Tamanho deste descritor |
0x0004 |
wDescriptorType | Descritor de propriedade do registro |
0x0007 |
wPropertyDataType | REG_MULTI_SZ |
0x002A |
wPropertyNameLength | Tamanho do nome da propriedade |
"DeviceInterfaceGUIDs\0" |
PropertyName | Nome da propriedade com terminador nulo codificado em UTF-16LE. |
0x0050 |
wPropertyDataLength | Tamanho do valor da propriedade |
"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\0\0" |
PropertyData | GUID mais dois terminadores nulos codificados em UTF-16LE |
O Windows só consultará o dispositivo uma vez para conseguir essas informações. Se o dispositivo não responder com descritores válidos, ele não vai perguntar novamente na próxima vez que ele for conectado. A Microsoft forneceu uma lista de entradas de registro de dispositivos USB que descrevem as entradas de registro criadas ao enumerar um dispositivo. Ao teste, exclua as entradas criadas para um dispositivo para forçar o Windows a tentar ler os descritores novamente.
Para mais informações, consulte a postagem do blog da Microsoft sobre como usar esses descritores.
Exemplos
Os exemplos de código que implementam dispositivos compatíveis com WebUSB que incluem descritores WebUSB e descritores do Microsoft OS podem ser encontrados nestes projetos: