Dispositivos USB

Este documento descreve como usar a API USB para se comunicar com dispositivos USB. Alguns dispositivos não podem ser acessados pela API USB. Consulte a seção Observações abaixo para mais detalhes. Esses apps também podem se conectar a dispositivos serial e Bluetooth.

Para informações contextuais sobre o USB, consulte as especificações USB oficiais. USB em um NutShell é um curso intensivo razoável que pode ser útil.

Requisito do manifesto

A API USB requer a permissão "usb" no arquivo de manifesto:

"permissions": [
  "usb"
]

Além disso, para evitar a impressão digital, é necessário declarar todos os tipos de dispositivo que você quer acessar no arquivo de manifesto. Cada tipo de dispositivo USB corresponde a um par de ID de fornecedor/ID do produto (VID/PID, na sigla em inglês). Você pode usar usb.getDevices para enumerar dispositivos pelo par de VID/PID.

Declare os pares de VID/PID para cada tipo de dispositivo que você quer usar com a permissão usbDevices no arquivo de manifesto do app, conforme mostrado no exemplo abaixo:

"permissions": [
  {
    "usbDevices": [
      {
        "vendorId": 123,
        "productId": 456
      }
    ]
  }
]

Desde o Chrome 57, o requisito de declarar todos os tipos de dispositivos no manifesto do app foi relaxado para apps em execução como aplicativos de quiosque do ChromeOS. Para aplicativos de quiosque, use a propriedade de permissão interfaceClass para solicitar acesso a dispositivos USB que:

  • implementar uma interface USB de uma classe de interface específica
  • tiver uma classe específica de dispositivo USB;

Por exemplo, a permissão usbDevices abaixo concederia ao app acesso a todos os dispositivos USB que implementam uma interface de impressora (código de classe de interface 7) e a dispositivos de hub USB (código de classe de dispositivo 9):

"permissions": [
  {
    "usbDevices": [
      {"interfaceClass": 7},
      {"interfaceClass": 9}
    ]
  }
]

Para ver a lista de valores de interfaceClass aceitáveis, consulte Códigos de classe USB.

A propriedade interfaceClass pode ser combinada com a propriedade vendorId para ter acesso apenas a dispositivos USB de um fornecedor específico, conforme demonstrado no exemplo a seguir:

"permissions": [
  {
    "usbDevices": [
      {
        "vendorId": 123,
        "interfaceClass": 7
      }
    ]
  }
]

Como encontrar um dispositivo

Para determinar se um ou mais dispositivos específicos estão conectados ao sistema de um usuário, use o método usb.getDevices:

chrome.usb.getDevices(enumerateDevicesOptions, callback);
Parâmetro (tipo)Descrição
EnumerateDevicesOptions (objeto)Um objeto que especifica um vendorId (longo) e productId (longo) usado para encontrar o tipo correto de dispositivo no ônibus. Seu manifesto precisa declarar a seção de permissões usbDevices, que lista todos os pares vendorId e deviceId que o app quer acessar.
callback (função)Chamado quando a enumeração do dispositivo é concluída. O callback será executado com um parâmetro, uma matriz de objetos Device com três propriedades: device, vendorId, productId. A propriedade do dispositivo é um identificador estável para um dispositivo conectado. Ele não mudará até que o dispositivo seja desconectado. Os detalhes do identificador são opacos e estão sujeitos a mudanças. Não dependa do tipo atual.
Se nenhum dispositivo for encontrado, a matriz estará vazia.

Exemplos

function onDeviceFound(devices) {
  this.devices=devices;
  if (devices) {
    if (devices.length > 0) {
      console.log("Device(s) found: "+devices.length);
    } else {
      console.log("Device could not be found");
    }
  } else {
    console.log("Permission denied.");
  }
}

chrome.usb.getDevices({"vendorId": vendorId, "productId": productId}, onDeviceFound);

Abrindo um dispositivo

Depois que os objetos Device forem retornados, será possível abrir um dispositivo usando usb.openDevice para receber um identificador de conexão. Só é possível se comunicar com dispositivos USB usando alças de conexão.

PropriedadeDescrição
dispositivoObjeto recebido no callback usb.getDevices.
dados (arraybuffer)Contém os dados enviados pelo dispositivo se a transferência foi de entrada.

Exemplos

var usbConnection = null;
var onOpenCallback = function(connection) {
  if (connection) {
    usbConnection = connection;
    console.log("Device opened.");
  } else {
    console.log("Device failed to open.");
  }
};

chrome.usb.openDevice(device, onOpenCallback);

Para simplificar o processo de abertura, use o método usb.findDevices, que enumera, solicita acesso e abre dispositivos em uma única chamada:

chrome.usb.findDevices({"vendorId": vendorId, "productId": productId, "interfaceId": interfaceId}, callback);

o que é equivalente a:

chrome.usb.getDevices({"vendorId": vendorId, "productId": productId}, function (devices) {
  if (!devices) {
    console.log("Error enumerating devices.");
    callback();
    return;
  }
  var connections = [], pendingAccessRequests = devices.length;
  devices.forEach(function (device) {
    chrome.usb.requestAccess(interfaceId, function () {
      // No need to check for errors at this point.
      // Nothing can be done if an error occurs anyway. You should always try
      // to open the device.
      chrome.usb.openDevices(device, function (connection) {
        if (connection) connections.push(connection);
        pendingAccessRequests--;
        if (pendingAccessRequests == 0) {
          callback(connections);
        }
      });
    });
  })
});

Transferências e recebimento de dados por USB de um dispositivo

O protocolo USB define quatro tipos de transferências: controle, em massa, isocrono e interrupção. Essas baldeações são descritas abaixo.

As transferências podem ocorrer em ambas as direções: dispositivo para host (entrada) e host para dispositivo (saída). Devido à natureza do protocolo USB, as mensagens de entrada e saída precisam ser iniciadas pelo host (o computador que executa o app Chrome). Para mensagens recebidas (do dispositivo para o host), o host (iniciado pelo código JavaScript) envia uma mensagem sinalizada como "recebida" para o dispositivo. Os detalhes da mensagem dependem do dispositivo, mas geralmente trazem alguma identificação do que você está solicitando dele. Em seguida, o dispositivo responde com os dados solicitados. A resposta do dispositivo é processada pelo Chrome e entregue de forma assíncrona ao callback especificado no método de transferência. Uma mensagem de saída (do host para o dispositivo) é semelhante, mas a resposta não contém os dados retornados do dispositivo.

Para cada mensagem do dispositivo, o callback especificado receberá um objeto de evento com as seguintes propriedades:

PropriedadeDescrição
resultCode (número inteiro)O valor 0 indica sucesso. Outros valores indicam falha. Uma string de erro pode ser
lida em chrome.extension.lastError quando uma falha é
indicada.
dados (arraybuffer)Contém os dados enviados pelo dispositivo se a transferência foi de entrada.

Exemplos

var onTransferCallback = function(event) {
   if (event && event.resultCode === 0 && event.data) {
     console.log("got " + event.data.byteLength + " bytes");
   }
};

chrome.usb.bulkTransfer(connectionHandle, transferInfo, onTransferCallback);

CONTROLAR baldeações

As transferências de controle geralmente são usadas para enviar ou receber parâmetros de configuração ou comando para um dispositivo USB. O método controlTransfer sempre envia/faz leituras no endpoint 0, e nenhuma claimInterface é necessária. O método é simples e recebe três parâmetros:

chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback)
Parâmetro (tipos)Descrição
connectionHandleObjeto recebido no callback de usb.openDevice.
transferInfoObjeto de parâmetro com valores da tabela abaixo. Verifique a especificação do protocolo do dispositivo USB para mais detalhes.
transferCallback()Invocado quando a transferência é concluída.

Valores do objeto transferInfo:

ValorDescrição
requestType (string)"vendor", "standard", "class" ou "reservado".
destinatário (string)"device", "interface", "endpoint" ou "other".
direção (string)"entrar" ou "fora". A direção "de" é usada para notificar o dispositivo
de que ele precisa enviar informações para o host. Toda a comunicação em um barramento
USB é iniciada pelo host. Portanto, use uma transferência "em" para permitir que um dispositivo
envie informações de volta.
solicitação (número inteiro)Definida pelo protocolo do dispositivo.
valor (número inteiro)Definida pelo protocolo do dispositivo.
índice (número inteiro)Definida pelo protocolo do dispositivo.
tamanho (número inteiro)Usado somente quando a direção é "para". Notifica o dispositivo de que essa é a quantidade de dados que o host espera em resposta.
dados (arraybuffer)Definida pelo protocolo do dispositivo, obrigatória quando a direção é "fora".

Exemplos

var transferInfo = {
  "requestType": "vendor",
   "recipient": "device",
  "direction": "out",
  "request":  0x31,
  "value": 120,
  "index": 0,
  // Note that the ArrayBuffer, not the TypedArray itself is used.
  "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer
};
chrome.usb.controlTransfer(connectionHandle, transferInfo, optionalCallback);

Transferências ISONOS

As transferências isócronas são o tipo mais complexo de transferência USB. Eles são comumente usados para streams de dados, como vídeo e som. Para iniciar uma transferência isócrona (de entrada ou saída), use o método usb.isochronousTransfer:

chrome.usb.isochronousTransfer(connectionHandle, isochronousTransferInfo, transferCallback)
ParâmetroDescrição
connectionHandleObjeto recebido no callback de usb.openDevice.
isochronousTransferInfoObjeto de parâmetro com os valores da tabela abaixo.
transferCallback()Invocado quando a transferência é concluída.

Valores do objeto isochronousTransferInfo:

ValorDescrição
transferInfo (objeto)Um objeto com os seguintes atributos:
direction (string): "para dentro" ou "para fora".
endpoint (número inteiro): definido pelo dispositivo. Geralmente, pode ser encontrado em uma ferramenta de inspeção USB, como lsusb -v
comprimento (número inteiro): usado somente quando a direção é "in". Notifica o dispositivo de que essa é a quantidade de dados que o host está esperando em resposta.
Precisa ser PELO MENOS packets × packetLength.
dados (arraybuffer): definidos pelo protocolo do dispositivo. Usado somente quando a direção é "fora".
pacotes (número inteiro)Número total de pacotes esperados nesta transferência.
packageLength (número inteiro)Comprimento esperado de cada pacote nesta transferência.

Exemplos

var transferInfo = {
  "direction": "in",
  "endpoint": 1,
  "length": 2560
};

var isoTransferInfo = {
  "transferInfo": transferInfo,
  "packets": 20,
  "packetLength": 128
};

chrome.usb.isochronousTransfer(connectionHandle, isoTransferInfo, optionalCallback);

Transferências em MASSA

As transferências em massa são normalmente usadas para transferir uma grande quantidade de dados não sensíveis ao tempo de maneira confiável. usb.bulkTransfer tem três parâmetros:

chrome.usb.bulkTransfer(connectionHandle, transferInfo, transferCallback);
ParâmetroDescrição
connectionHandleObjeto recebido no callback de usb.openDevice.
transferInfoObjeto de parâmetro com os valores da tabela abaixo.
transferCallbackInvocado quando a transferência é concluída.

Valores do objeto transferInfo:

ValorDescrição
direção (string)"entrar" ou "fora".
endpoint (número inteiro)Definida pelo protocolo do dispositivo.
tamanho (número inteiro)Usado somente quando a direção é "para". Notifica o dispositivo de que essa é a quantidade de dados que o host espera em resposta.
dados (ArrayBuffer)Definida pelo protocolo do dispositivo, usada apenas quando a direção é "fora".

Exemplos

var transferInfo = {
  "direction": "out",
  "endpoint": 1,
  "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer
};

INTERRUPTAR baldeações

As transferências de interrupção são usadas para uma pequena quantidade de dados sensíveis. Como toda a comunicação USB é iniciada pelo host, o código do host geralmente pesquisa o dispositivo periodicamente, enviando transferências IN de interrupção que farão o dispositivo enviar dados de volta se houver algo na fila de interrupção (mantida pelo dispositivo). O usb.interruptTransfer tem três parâmetros:

chrome.usb.interruptTransfer(connectionHandle, transferInfo, transferCallback);
ParâmetroDescrição
connectionHandleObjeto recebido no callback de usb.openDevice.
transferInfoObjeto de parâmetro com os valores da tabela abaixo.
transferCallbackInvocado quando a transferência é concluída. Esse callback não contém a resposta do dispositivo. O propósito do callback é simplesmente notificar seu código de que as solicitações de transferência assíncronas foram processadas.

Valores do objeto transferInfo:

ValorDescrição
direção (string)"entrar" ou "fora".
endpoint (número inteiro)Definida pelo protocolo do dispositivo.
tamanho (número inteiro)Usado somente quando a direção é "para". Notifica o dispositivo de que essa é a quantidade de dados que o host espera em resposta.
dados (ArrayBuffer)Definida pelo protocolo do dispositivo, usada apenas quando a direção é "fora".

Exemplos

var transferInfo = {
  "direction": "in",
  "endpoint": 1,
  "length": 2
};
chrome.usb.interruptTransfer(connectionHandle, transferInfo, optionalCallback);

Avisos

Nem todos os dispositivos podem ser acessados pela API USB. Em geral, os dispositivos não podem ser acessados porque o kernel do sistema operacional ou um driver nativo os impede do código do espaço do usuário. Alguns exemplos são dispositivos com perfis HID em sistemas OSX e pen drives USB.

Na maioria dos sistemas Linux, os dispositivos USB são mapeados com permissões somente leitura por padrão. Para abrir um dispositivo usando essa API, o usuário também precisará ter acesso de gravação a ele. Uma solução simples é definir uma regra udev. Crie um arquivo /etc/udev/rules.d/50-yourdevicename.rules com o seguinte conteúdo:

SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"

Em seguida, reinicie o daemon udev: service udev restart. É possível verificar se as permissões do dispositivo foram definidas corretamente seguindo estas etapas:

  • Execute lsusb para encontrar os números de ônibus e dispositivo.
  • Execute ls -al /dev/bus/usb/[bus]/[device]. Esse arquivo precisa pertencer ao grupo "plugdev" e ter permissões de gravação em grupo.

O app não pode fazer isso automaticamente porque o procedimento exige acesso à raiz. Recomendamos que você forneça instruções aos usuários finais e inclua um link para a seção Ressalvas desta página para uma explicação.

No ChromeOS, basta chamar usb.requestAccess. O agente de permissões faz isso para você.