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 Advertências abaixo para mais detalhes). Apps do Chrome também pode se conectar a dispositivos serial e Bluetooth.

Para informações contextuais sobre USB, consulte as especificações de USB oficiais. USB no NutShell (link em inglês) é um curso intensivo que pode ser útil.

Requisito do manifesto

A API USB exige o módulo "usb" no arquivo de manifesto:

"permissions": [
  "usb"
]

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

É necessário declarar os pares de VID/PID para cada tipo de dispositivo que você quer usar de acordo com a usbDevices. no arquivo de manifesto do app, conforme mostrado no exemplo abaixo:

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

Desde o Chrome 57, o requisito para declarar todos os tipos de dispositivos no manifesto do app é para apps executados como aplicativos de quiosque do ChromeOS. Para aplicativos de quiosque, é possível usar o interfaceClass para solicitar permissão de acesso a dispositivos USB que:

  • implementar uma interface USB de uma classe de interface específica
  • têm uma classe de dispositivo USB específica;

Por exemplo, a seguinte permissão usbDevices concederia a um app acesso a todos os dispositivos USB que implementar uma interface de impressora (código de classe de interface 7) e a dispositivos 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 vendorId para ter acesso apenas ao USB. dispositivos de um fornecedor específico, conforme demonstrado pelo 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 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 barramento. O manifesto precisa declarar a seção de permissão usbDevices, listando todos os pares de 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 e productId. A propriedade do dispositivo é um identificador estável de 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 vai estar vazia.

Exemplo:

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);

Como abrir um dispositivo

Quando os objetos Device forem retornados, será possível abrir um dispositivo usando usb.openDevice para receber uma identificador de conexão. Você só pode se comunicar com dispositivos USB usando identificadores de conexão.

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

Exemplo:

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, você pode usar o método usb.findDevices, que enumera: solicita acesso e abre dispositivos em uma chamada:

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

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 por USB e recebimento de dados de um dispositivo

O protocolo USB define quatro tipos de transferências: controle, em massa, isocronas e interromper. Essas transferências são descritas abaixo.

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

Para cada mensagem do dispositivo, o retorno de chamada especificado receberá um objeto de evento com o propriedades a seguir:

PropriedadeDescrição
resultCode (número inteiro)0 é sucesso; e outros valores indicam falha. Uma string de erro pode ser
lida no chrome.extension.lastError quando uma falha é
indicada.
dados (arraybuffer)Contém os dados enviados pelo dispositivo se a transferência tiver sido recebida.

Exemplo:

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 transferências

As transferências de controle geralmente são usadas para enviar ou receber parâmetros de configuração ou comando para um USB dispositivo. O método controlTransfer sempre envia para/lê do endpoint 0, e nenhum claimInterface obrigatórios. 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 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)"fornecedor", "padrão", "classe" ou "reservado".
destinatário (string)"dispositivo", "interface", "endpoint" ou "outro".
direção (string)“em” ou "out". O "em" é usada para notificar o dispositivo
de que ele precisa enviar informações ao host. Toda a comunicação em um barramento
USB é iniciada pelo host, portanto, use transferência para permitir que um dispositivo
envie informações de volta.
solicitação (número inteiro)Ela é definida pelo protocolo do dispositivo.
valor (inteiro)Ela é definida pelo protocolo do dispositivo.
índice (número inteiro)Ela é definida pelo protocolo do dispositivo.
comprimento (número inteiro)Usado somente quando a direção é "para dentro". Notifica o dispositivo de que essa é a quantidade de dados que o host espera em resposta.
dados (arraybuffer)Ela é definida pelo protocolo do dispositivo e é obrigatória quando a direção sai.

Exemplo:

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 ISOCHRONOUS

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

chrome.usb.isochronousTransfer(connectionHandle, isochronousTransferInfo, transferCallback)
ParâmetroDescrição
connectionHandleObjeto recebido no callback usb.openDevice.
isochronousTransferInfoObjeto de parâmetro com os valores na 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): "in" ou "out".
endpoint (número inteiro): definido pelo dispositivo. Geralmente, ele pode ser encontrado em uma ferramenta de inspeção USB, como lsusb -v
comprimento (número inteiro): usado apenas quando a direção está "para dentro". Notifica o dispositivo de que essa é a quantidade de dados que o host espera em resposta.
Deve ser MENOS packets × packetLength.
dados (arraybuffer): definidos pelo protocolo do seu dispositivo. usado somente quando a direção está "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.

Exemplo:

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 costumam ser usadas para transferir uma grande quantidade de dados não urgentes de maneira confiável de um jeito fácil. usb.bulkTransfer tem três parâmetros:

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

Valores do objeto transferInfo:

ValorDescrição
direção (string)“em” ou "out".
endpoint (número inteiro)Ela é definida pelo protocolo do dispositivo.
comprimento (número inteiro)Usado somente quando a direção é "para dentro". Notifica o dispositivo de que essa é a quantidade de dados que o host espera em resposta.
dados (ArrayBuffer)são definidas pelo protocolo do dispositivo; usado somente quando a direção está "fora".

Exemplo:

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

INTERRUPTAR transferências

As transferências de interrupção são usadas para pequenas quantidades de dados sensíveis ao tempo. Como toda a comunicação USB é iniciado pelo host, o código do host normalmente consulta o dispositivo periodicamente, enviando uma interrupção IN transferências que farão o dispositivo enviar dados de volta se houver algo na fila de interrupção (mantido pelo dispositivo). usb.interruptTransfer tem três parâmetros:

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

Valores do objeto transferInfo:

ValorDescrição
direção (string)“em” ou "out".
endpoint (número inteiro)Ela é definida pelo protocolo do dispositivo.
comprimento (número inteiro)Usado somente quando a direção é "para dentro". Notifica o dispositivo de que essa é a quantidade de dados que o host espera em resposta.
dados (ArrayBuffer)são definidas pelo protocolo do dispositivo; usado somente quando a direção está "fora".

Exemplo:

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

Advertências

Nem todos os dispositivos podem ser acessados pela API USB. Em geral, os dispositivos não são acessíveis porque se o kernel do sistema operacional ou um driver nativo os afasta do código de espaço do usuário. Algumas Por exemplo, 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 por essa API, o usuário também precisará ter acesso de gravação. Uma solução simples é 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 estão definido corretamente seguindo estas etapas:

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

Seu aplicativo não pode fazer isso automaticamente, pois o procedimento requer acesso raiz. Recomendamos forneça instruções aos usuários finais e inclua um link para a seção Ressalvas nesta página para uma explicação.

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