USB-устройства

В этом документе описывается, как использовать USB API для связи с USB-устройствами. Некоторые устройства недоступны через USB API (подробную информацию см. в разделе «Предостережения» ниже). Приложения Chrome также могут подключаться к устройствам с последовательным интерфейсом и Bluetooth .

Дополнительную информацию о USB см. в официальных спецификациях USB . USB в двух словах — это разумный ускоренный курс, который может оказаться вам полезным.

Требование манифеста

USB API требует разрешения «usb» в файле манифеста:

"permissions": [
  "usb"
]

Кроме того, чтобы предотвратить отпечатки пальцев , вы должны объявить все типы устройств, к которым вы хотите получить доступ, в файле манифеста. Каждому типу USB-устройства соответствует пара идентификатор поставщика/идентификатор продукта (VID/PID). Вы можете использовать usb.getDevices для перечисления устройств по их паре VID/PID.

Вы должны объявить пары VID/PID для каждого типа устройства, которое вы хотите использовать, с разрешением usbDevices в файле манифеста вашего приложения, как показано в примере ниже:

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

Начиная с Chrome 57 , требование объявления всех типов устройств в манифесте приложения смягчено для приложений, работающих как киоск-приложения ChromeOS. Для киоск-приложений вы можете использовать свойство разрешения interfaceClass , чтобы запросить разрешение на доступ к USB-устройствам, которые:

  • реализовать интерфейс USB определенного класса интерфейса
  • иметь определенный класс USB-устройства

Например, следующее разрешение usbDevices предоставит приложению доступ ко всем USB-устройствам, реализующим интерфейс принтера (код класса интерфейса 7), а также к устройствам USB-концентратора (код класса устройства 9):

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

Список допустимых значений interfaceClass см. в разделе Коды классов USB .

Свойство interfaceClass можно объединить со vendorId , чтобы получить доступ только к USB-устройствам от определенного поставщика, как показано в следующем примере:

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

Поиск устройства

Чтобы определить, подключены ли к системе пользователя одно или несколько конкретных устройств, используйте метод usb.getDevices :

chrome.usb.getDevices(enumerateDevicesOptions, callback);
Параметр (тип) Описание
EnumerateDevicesOptions (объект) Объект, указывающий как vendorId (длинный), так и productId (длинный), используемый для поиска правильного типа устройства на шине. В вашем манифесте должен быть объявлен раздел разрешений usbDevices , в котором перечислены все vendorId deviceId , к которым ваше приложение хочет получить доступ.
обратный вызов (функция) Вызывается, когда перечисление устройств завершено. Обратный вызов будет выполнен с одним параметром — массивом объектов Device с тремя свойствами: device , vendorId , productId . Свойство устройства — это стабильный идентификатор подключенного устройства. Оно не изменится, пока устройство не будет отключено от сети. Детали идентификатора непрозрачны и могут быть изменены. Не полагайтесь на его текущий тип.
Если устройства не найдены, массив будет пуст.

Пример:

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

Открытие устройства

Как только объекты Device будут возвращены, вы можете открыть устройство, используя usb.openDevice, чтобы получить дескриптор соединения. Общаться с USB-устройствами можно только с помощью маркеров подключения.

Свойство Описание
устройство Объект, полученный в обратном вызове usb.getDevices .
данные (буфер массива) Содержит данные, отправленные устройством, если передача была входящей.

Пример:

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

Чтобы упростить процесс открытия, вы можете использовать метод usb.findDevices , который перечисляет, запрашивает доступ и открывает устройства за один вызов:

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

что эквивалентно:

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

USB-передача и получение данных с устройства

Протокол USB определяет четыре типа передачи: контрольную , групповую , изохронную и прерывания . Эти передачи описаны ниже.

Передача может происходить в обоих направлениях: от устройства к хосту (входящая) и от хоста к устройству (исходящая). Из-за особенностей протокола USB как входящие, так и исходящие сообщения должны инициироваться хостом (компьютером, на котором запущено приложение Chrome). Для входящих сообщений (от устройства к хосту) хост (инициированный вашим кодом JavaScript) отправляет на устройство сообщение, помеченное как «входящее». Детали сообщения зависят от устройства, но обычно содержат некоторую идентификацию того, что вы от него запрашиваете. Затем устройство отвечает запрошенными данными. Ответ устройства обрабатывается Chrome и доставляется асинхронно обратному вызову, указанному вами в методе передачи. Исходящее сообщение (от хоста к устройству) аналогично, но ответ не содержит данных, возвращаемых с устройства.

Для каждого сообщения от устройства указанный обратный вызов получит объект события со следующими свойствами:

Свойство Описание
код результата (целое число) 0 – успех; другие значения указывают на неудачу. Строка ошибки может быть
читать из chrome.extension.lastError при сбое
указано.
данные (буфер массива) Содержит данные, отправленные устройством, если передача была входящей.

Пример:

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

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

КОНТРОЛЬ переводов

Передача управления обычно используется для отправки или получения параметров конфигурации или команд на USB-устройство. Метод controlTransfer всегда отправляет данные в конечную точку 0 и считывает их из нее, и ClaimInterface не требуется. Метод прост и получает три параметра:

chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback)
Параметр (типы) Описание
дескриптор соединения Объект, полученный в обратном вызове usb.openDevice .
TransferInfo Объект параметра со значениями из таблицы ниже. Подробную информацию см. в спецификации протокола вашего USB-устройства.
передачаОбратный вызов() Вызывается после завершения передачи.

Значения для объекта transferInfo :

Ценить Описание
requestType (строка) «поставщик», «стандарт», «класс» или «зарезервировано».
получатель (строка) «устройство», «интерфейс», «конечная точка» или «другое».
направление (строка) «вход» или «выход». Направление «вход» используется для уведомления устройства о том, что
он должен отправить информацию хосту. Вся связь через USB
шина инициируется хостом, поэтому используйте передачу «вход», чтобы позволить устройству
отправить информацию обратно.
запрос (целое число) Определяется протоколом вашего устройства.
значение (целое) Определяется протоколом вашего устройства.
индекс (целое число) Определяется протоколом вашего устройства.
длина (целое число) Используется только в том случае, если направление «внутри». Уведомляет устройство о том, что именно этот объем данных хост ожидает в ответ.
данные (буфер массива) Определяется протоколом вашего устройства и требуется, когда направление «аут».

Пример:

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

ИЗОХРОННЫЕ передачи

Изохронная передача — самый сложный тип передачи через USB. Они обычно используются для потоков данных, таких как видео и звук. Чтобы инициировать изохронную передачу (входящую или исходящую), вы должны использовать метод usb.isochronousTransfer :

chrome.usb.isochronousTransfer(connectionHandle, isochronousTransferInfo, transferCallback)
Параметр Описание
дескриптор соединения Объект, полученный в обратном вызове usb.openDevice .
изохронныйTransferInfo Объект параметра со значениями, указанными в таблице ниже.
передачаОбратный вызов() Вызывается после завершения передачи.

Значения для объекта isochronousTransferInfo :

Ценить Описание
TransferInfo (объект) Объект со следующими атрибутами:
направление (строка): «внутрь» или «наружу».
конечная точка (целое число): определяется вашим устройством. Обычно его можно найти с помощью инструмента проверки USB, например lsusb -v
длина (целое число): используется только в том случае, если направление «внутри». Уведомляет устройство о том, что именно этот объем данных хост ожидает в ответ.
Должно быть МИНИМАЛЬНО packets × packetLength .
данные (arraybuffer): определяются протоколом вашего устройства; используется только тогда, когда направление «наружу».
пакеты (целое число) Общее количество пакетов, ожидаемых в этой передаче.
длина пакета (целое число) Ожидаемая длина каждого пакета в этой передаче.

Пример:

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

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

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

МАССОВЫЕ переводы

Массовые передачи обычно используются для надежной передачи большого количества независящих от времени данных. usb.bulkTransfer имеет три параметра:

chrome.usb.bulkTransfer(connectionHandle, transferInfo, transferCallback);
Параметр Описание
дескриптор соединения Объект, полученный в обратном вызове usb.openDevice .
TransferInfo Объект параметра со значениями, указанными в таблице ниже.
переводОбратный звонок Вызывается после завершения передачи.

Значения для объекта transferInfo :

Ценить Описание
направление (строка) «вход» или «выход».
конечная точка (целое число) Определяется протоколом вашего устройства.
длина (целое число) Используется только в том случае, если направление «внутри». Уведомляет устройство о том, что именно этот объем данных хост ожидает в ответ.
данные (ArrayBuffer) Определяется протоколом вашего устройства; используется только тогда, когда направление «наружу».

Пример:

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

ПРЕРЫВАНИЕ передачи

Передача прерываний используется для небольших объемов данных, чувствительных ко времени. Поскольку вся связь USB инициируется хостом, код хоста обычно периодически опрашивает устройство, отправляя передачи прерываний IN, которые заставят устройство отправлять данные обратно, если что-то есть в очереди прерываний (поддерживаемой устройством). usb.interruptTransfer имеет три параметра:

chrome.usb.interruptTransfer(connectionHandle, transferInfo, transferCallback);
Параметр Описание
дескриптор соединения Объект, полученный в обратном вызове usb.openDevice .
TransferInfo Объект параметра со значениями, указанными в таблице ниже.
переводОбратный звонок Вызывается после завершения передачи. Обратите внимание, что этот обратный вызов не содержит ответа устройства. Цель обратного вызова — просто уведомить ваш код о том, что запросы асинхронной передачи обработаны.

Значения для объекта transferInfo :

Ценить Описание
направление (строка) «вход» или «выход».
конечная точка (целое число) Определяется протоколом вашего устройства.
длина (целое число) Используется только в том случае, если направление «внутри». Уведомляет устройство о том, что именно этот объем данных хост ожидает в ответ.
данные (ArrayBuffer) Определяется протоколом вашего устройства; используется только тогда, когда направление «наружу».

Пример:

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

Предостережения

Не ко всем устройствам можно получить доступ через USB API. Как правило, устройства недоступны, потому что либо ядро ​​операционной системы, либо собственный драйвер удерживают их от кода пользовательского пространства. Некоторыми примерами являются устройства с профилями HID в системах OSX и USB-накопители.

В большинстве систем Linux USB-устройства по умолчанию имеют разрешения только на чтение. Чтобы открыть устройство через этот API, вашему пользователю также потребуется доступ на запись к нему. Простое решение — установить правило udev. Создайте файл /etc/udev/rules.d/50-yourdevicename.rules со следующим содержимым:

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

Затем просто перезапустите демон udev: service udev restart . Вы можете проверить, правильно ли установлены разрешения устройства, выполнив следующие действия:

  • Запустите lsusb , чтобы найти номера шин и устройств.
  • Запустите ls -al /dev/bus/usb/[bus]/[device] . Этот файл должен принадлежать группе «plugdev» и иметь групповые права на запись.

Ваше приложение не может сделать это автоматически, поскольку для этой процедуры требуется root-доступ. Мы рекомендуем вам предоставить инструкции конечным пользователям и дать ссылку на раздел «Предостережения» на этой странице для объяснения.

В ChromeOS просто вызовите usb.requestAccess . Брокер разрешений сделает это за вас.