Como criar um dispositivo para WebUSB

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, veja Acessar dispositivos USB na Web.

Contexto

O USB (Universal Serial Bus) tornou-se a interface física mais comum para conectar periféricos a computadores e dispositivos móveis de computação. Além de definir as características elétricas do ônibus e um modelo geral para comunicando com um dispositivo, as especificações de USB incluem um conjunto de classes especificações. Esses são modelos gerais para classes específicas de dispositivos, como armazenamento, áudio, vídeo, rede etc. que os fabricantes de dispositivos 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 classe (um "driver de classe") e qualquer dispositivo que implemente essa classe será suporte. Essa foi uma grande melhoria em relação a todos os fabricantes que precisavam escrever os próprios drivers de dispositivo.

No entanto, alguns dispositivos não se encaixam nessas classes padronizadas. Um o fabricante pode optar por rotular seus dispositivos como em uma classe específica de fornecedor. Nesse caso, o sistema operacional escolhe o dispositivo o driver seja carregado com base nas informações fornecidas no pacote de drivers do fornecedor, normalmente um conjunto de IDs de produto e fornecedor que são conhecidos por implementar uma protocolo específico do fornecedor.

Outra característica do USB é que os dispositivos podem fornecer várias interfaces para ao host ao qual estão conectados. Cada interface pode implementar uma classe padronizada ou ser específico do fornecedor. Quando um sistema operacional escolhe os drivers certos para o dispositivo. Cada interface pode ser reivindicada por um motorista. Por exemplo, uma webcam USB normalmente tem duas interfaces, uma implementando a classe de vídeo USB (para a câmera) e outra implementando a classe de áudio (para o microfone). O sistema operacional não carrega um único "driver da webcam" mas carrega drivers independentes de classe de vídeo e áudio que assumem a responsabilidade pelas funções separadas do dispositivo. Isso 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, página pode capturar vídeo de um dispositivo de classe de vídeo usando getUserMedia() ou receber eventos de entrada de um dispositivo de classe de interface humana (HID) ouvindo para KeyboardEvents ou PointerEvents, ou ao usar o Gamepad ou o API WebHID. Assim como nem todos os dispositivos implementam uma definição de classe padronizada, nem todos dispositivos implementam recursos que correspondem às APIs de plataforma web existentes. Quando neste caso, a API WebUSB pode preencher essa lacuna oferecendo uma maneira reivindicar uma interface específica do fornecedor e implementar o suporte diretamente dentro de suas páginas.

Os requisitos específicos para que um dispositivo possa ser acessado via WebUSB variam um pouco de plataforma para plataforma devido a diferenças na forma como os sistemas operacionais gerenciam USB do Google, mas o requisito básico é que ele não tenha um que reivindica a interface que a página quer controlar. Pode ser um driver de classe genérica fornecido pelo fornecedor do SO ou um driver de dispositivo fornecido pelo com o fornecedor. Como os dispositivos USB podem fornecer várias interfaces, cada uma delas pode tem driver próprio, é possível criar um dispositivo para o qual algumas interfaces são reivindicados por um motorista, e os demais podem ser acessados pelo navegador.

Por exemplo, um teclado USB de última geração pode fornecer uma interface de classe HID que será reivindicado pelo subsistema de entrada do sistema operacional e por um nome de que continua disponível para o WebUSB para uso por uma ferramenta de configuração. Isso ferramenta pode ser exibida no site do fabricante, permitindo que o usuário altere aspectos do comportamento do dispositivo, como teclas macro e efeitos de iluminação sem sem instalar softwares específicos da plataforma. Esse descritor de configuração de dispositivo será 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 dessa 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 autônomo com ativação remota
0x32 bMaxPower A potência máxima é expressa em incrementos de 2 mA.
Descritor da interface
0x09 bLength Tamanho deste descritor
0x04 bDescriptorType Descritor da 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 (IN)
0b00000011 bmAttributes Interromper
0x0008 wMaxPacketSize Pacotes de 8 bytes
0x0A bInterval Intervalo de 10 ms
Descritor da interface
0x09 bLength Tamanho deste descritor
0x04 bDescriptorType Descritor da 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 (IN)
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 (SAINDO)
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 juntas. Cada um começa com os campos bLength e bDescriptorType para que possam ser identificados. A primeira interface é uma interface HID com uma interface Descritor HID e um único endpoint usado para entregar eventos de entrada para o operacional principal. A segunda interface é específica para fornecedores, com duas endpoints que podem ser usados para enviar comandos ao dispositivo e receber respostas em troca.

Descritores WebUSB

Embora a WebUSB funcione com muitos dispositivos sem modificações no firmware, Para ativar esse recurso, marque o dispositivo com informações descritores que indicam compatibilidade com WebUSB. Por exemplo, é possível especificar URL da página de destino para a qual o navegador pode direcionar o usuário quando o dispositivo está conectado.

Captura de tela da notificação WebUSB no Chrome
Notificação WebUSB.

O armazenamento de objetos binários (BOS, na sigla em inglês) de dispositivos binários é um conceito introduzido no USB 3.0, mas também passou por backport para dispositivos USB 2.0 como parte da versão 2.1. Declaração o suporte para WebUSB começa com a inclusão da seguinte capacidade da plataforma Descritor no BOS:

Valor Campo Descrição
Descritor de armazenamento de objetos de dispositivo binário
0x05 bLength Tamanho deste descritor
0x0F bDescriptorType Descritor de armazenamento de objetos de dispositivo binário
0x001D wTotalLength Comprimento total dessa 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 de capacidade do dispositivo
0x05 bDevCapabilityType Descritor de recursos da plataforma
0x00 bReserved
{0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} PlatformCapablityUUID Descritor de capacidade da plataforma WebUSB GUID em formato pouco-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 da capacidade da plataforma identifica esse recurso como uma Capacidade da plataforma WebUSB descritor, que fornece informações básicas sobre o dispositivo. Para o navegador para buscar mais informações sobre o dispositivo em que o valor bVendorCode é usado. emitir solicitações adicionais ao dispositivo. A única solicitação especificada atualmente é GET_URL, que retorna um descritor de URL. Elas são semelhantes às strings descritores, mas são projetados para codificar URLs com menos bytes. Um URL descritor de "https://google.com" ficaria assim:

Valor Campo Descrição
Descritor do URL
0x0D bLength Tamanho deste descritor
0x03 bDescriptorType Descritor do URL
0x01 bScheme https://
"google.com" URL Conteúdo de URL codificado em UTF-8

Quando seu dispositivo é conectado pela primeira vez, o navegador lê o descritor BOS emitindo esta transferência de controle do 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 vez com um wLength grande o suficiente para que o host descubra o valor do campo wTotalLength sem fazer uma transferência grande e novamente quando o comprimento completo do descritor for conhecidos.

Se o descritor de recursos da plataforma WebUSB tiver o campo iLandingPage definido como um valor diferente de zero, o navegador executa uma solicitação GET_URL específica de WebUSB emitindo uma transferência de controle com o bRequest definido como o valor bVendorCode do descritor de capacidade da plataforma e o wValue definido como iLandingPage . O código de solicitação para GET_URL (0x02) fica 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 a primeira sondagem do descritor que está sendo lido.

Considerações específicas da plataforma

Enquanto a API WebUSB tenta fornecer uma interface consistente para acessar Os desenvolvedores de dispositivos USB ainda devem estar cientes dos requisitos impostos aos aplicativos, como requisitos de navegadores da Web, para acessar os dispositivos.

macOS

Nada de especial é necessário para o macOS. Um site que usa WebUSB pode se conectar ao o dispositivo e reivindicar quaisquer interfaces que não sejam reivindicadas por um driver de kernel ou outro aplicativo.

Linux

O Linux é como o macOS, mas, por padrão, a maioria das distribuições não configura contas com permissão para abrir dispositivos USB. Um daemon do sistema chamado udev é responsável por atribuir o usuário e o grupo com permissão para acessar um dispositivo. Uma regra como isso atribui a propriedade de um dispositivo correspondente ao fornecedor e IDs de produtos ao grupo plugdev, que é um grupo comum de usuários com acesso a periféricos:

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

Substitua XXXX pelos IDs de fornecedor e produto hexadecimais do seu dispositivo. Por exemplo: ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e11" corresponderia a um Nexus One celular. Eles devem ser escritos sem o "0x" normal, prefixo e todos em minúsculas para ser reconhecido corretamente. Para encontrar os IDs do seu dispositivo, execute a linha de comando ferramenta lsusb.

Essa regra deve 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 udev.

Android

A plataforma Android é baseada em Linux, mas não requer nenhuma modificação para configuração do sistema. Por padrão, qualquer dispositivo que não tenha um driver integrado. no sistema operacional está disponível para o navegador. Os desenvolvedores devem ser mas que os usuários encontrarão uma etapa adicional ao se conectarem o dispositivo. Quando um usuário seleciona um dispositivo em resposta a uma chamada para requestDevice(), o Android vai mostrar uma solicitação perguntando se você quer permitir Chrome para acessá-lo. 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 há menos drivers incluídos por padrão. Uma omissão importante, por exemplo, é a classe USB CDC-ACM geralmente implementada por adaptadores USB para serial, não há uma API no SDK do Android para comunicação com um dispositivo serial.

ChromeOS

O ChromeOS também é baseado no Linux e também não requer nenhuma modificação à configuração do sistema. O serviço permission_broker controla o acesso ao USB dispositivos e permitirá que o navegador os acesse, desde que haja pelo menos uma interface sem reivindicação.

Windows

O modelo de driver do Windows apresenta um requisito extra. Ao contrário do plataformas acima da capacidade de abrir um dispositivo USB a partir de um aplicativo do usuário o padrão, mesmo que não haja nenhum driver carregado. Em vez disso, há um WinUSB, que precisa ser carregado para fornecer a interface usam para acessar o dispositivo. Isso pode ser feito com um modelo arquivo de informações do driver (INF) instalado no sistema ou modificando o dispositivo firmware para fornecer os descritores de compatibilidade do SO Microsoft durante enumeração.

Arquivo de informações do motorista (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 o fornecedor e o ID do produto com essa nova regra de instalação. O arquivo abaixo é um exemplo básico. Salvar em um com a extensão .inf, mude as seções marcadas com "X" e depois clique nele 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 de dispositivo deve ter um GUID para que um aplicativo encontrá-lo e se conectar a ele pela API do Windows. Usar o PowerShell New-Guid cmdlet ou uma ferramenta on-line para gerar um GUID aleatório.

Para fins de desenvolvimento, a ferramenta Zadig oferece uma interface fácil para para substituir o driver carregado de uma interface USB pelo WinUSB.

Descritores de compatibilidade do SO Microsoft

A abordagem de arquivo INF acima é trabalhosa porque exige a configuração de todos os na máquina do usuário com antecedência. O Windows 8.1 e as versões mais recentes com o uso de descritores USB personalizados. Esses descritores fornecem informações ao sistema operacional Windows quando o dispositivo é conectado pela primeira vez normalmente ser incluídas no arquivo INF.

Depois de configurar os descritores WebUSB, é fácil adicionar o sistema operacional da Microsoft descritores de compatibilidade. Primeiro, estenda o descritor BOS com este descritor de capacidade adicional da plataforma. Não se esqueça de atualizar o app wTotalLength e bNumDeviceCaps para considerar isso.

Valor Campo Descrição
Descritor de recursos da plataforma Microsoft OS 2.0
0x1C bLength Tamanho deste descritor
0x10 bDescriptorType Descritor de capacidade do dispositivo
0x05 bDevCapabilityType Descritor de recursos da plataforma
0x00 bReserved
{0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F} PlatformCapablityUUID Descritor GUID de compatibilidade da plataforma Microsoft OS 2.0 no formato small-endian
0x06030000 dwWindowsVersion Versão mínima do Windows compatível (Windows 8.1)
0x00B2 wMSOSDescriptorSetTotalLength Tamanho total do conjunto do descritor
0x02 bMS_VendorCode Valor de bRequest para recuperar outros descritores da Microsoft
0x00 bAltEnumCode O dispositivo não é compatível com enumeração alternativa

Assim como nos descritores WebUSB, você precisa escolher um valor bRequest para ser usado pelo e controla as transferências relacionadas a esses descritores. Neste exemplo, escolhi 0x02: 0x07, colocado em wIndex, é o comando para recuperar o SO Microsoft 2.0 Conjunto do descritor do dispositivo.

bmRequestType bRequest wValue wIndex wLength Dados (resposta)
0b11000000 0x02 0x0000 0x0007 * Conjunto de descritor do MS OS 2.0

Um dispositivo USB pode ter várias funções e, portanto, a primeira parte do descritor O conjunto descreve a qual função as propriedades a seguir estão associadas. A abaixo configura a interface 1 de um dispositivo composto. O descritor fornece duas informações importantes sobre essa interface. Os modelos O descritor de ID informa ao Windows que este dispositivo é compatível com a rede WinUSB motorista. O descritor de propriedade do registro funciona de forma semelhante à Seção [Dev_AddReg] do exemplo INF acima, definindo uma propriedade de registro como atribua um GUID da interface do dispositivo a essa função.

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 do conjunto de descritor
0x06030000 dwWindowsVersion Versão mínima do Windows compatível (Windows 8.1)
0x00B2 wTotalLength Tamanho 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 indexado a partir de 1)
0x00 bReserved Precisa ser definido como 0
0x00A8 wTotalLength Tamanho total do subconjunto que inclui 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 Tamanho total do subconjunto que inclui este cabeçalho
Descritor de ID compatível com Microsoft OS 2.0
0x0014 wLength Tamanho deste descritor
0x0003 wDescriptorType Descritor de ID compatível
"WINUSB\0\0" CompatibileID String ASCII preenchida com 8 bytes
"\0\0\0\0\0\0\0\0" SubCompatibleID String ASCII preenchida com 8 bytes
Descritor de propriedade de registro do Microsoft OS 2.0
0x0084 wLength Tamanho deste descritor
0x0004 wDescriptorType Descritor de propriedade de 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ó consulta o dispositivo uma vez para encontrar essas informações. Se o dispositivo não responder com descritores válidos, ele não perguntará novamente na próxima vez que o dispositivo está conectado. A Microsoft forneceu uma lista de registros de dispositivos USB Entradas que descrevem as entradas de registro criadas ao enumerar um dispositivo. Quando o teste exclui as entradas criadas para um dispositivo para forçar o Windows a tentar ler os descritores novamente.

Para mais informações, confira a postagem do blog da Microsoft sobre como usá-los descritores.

Exemplos

Exemplo de código que implementa dispositivos com reconhecimento de WebUSB que incluem WebUSB descritores do Microsoft OS e podem ser encontrados nestes projetos: