USB 기기

이 문서에서는 USB API를 사용하여 USB 기기와 통신하는 방법을 설명합니다. 일부 기기는 USB API를 통해 액세스할 수 없습니다 (자세한 내용은 아래 주의사항 섹션 참고). Chrome 앱은 serial블루투스 기기에도 연결할 수 있습니다.

USB에 대한 배경 정보는 공식 USB 사양을 참조하세요. NutShell의 USB는 도움이 될 만한 합리적인 단기 집중 과정입니다.

매니페스트 요구사항

USB API를 사용하려면 매니페스트 파일에 'usb' 권한이 필요합니다.

"permissions": [
  "usb"
]

또한 지문 인쇄를 방지하려면 액세스하려는 모든 기기 유형을 매니페스트 파일에서 선언해야 합니다. 각 USB 기기 유형은 공급업체 ID/제품 ID(VID/PID) 쌍에 상응합니다. usb.getDevices를 사용하여 VID/PID 쌍으로 기기를 열거할 수 있습니다.

아래 예와 같이 앱의 매니페스트 파일에서 usbDevices 권한으로 사용하려는 각 기기 유형의 VID/PID 쌍을 선언해야 합니다.

"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);
매개변수 (type)설명
EnumerateDevicesOptions (객체)버스에서 올바른 유형의 기기를 찾는 데 사용되는 vendorId (long)과 productId (long)을 모두 지정하는 객체입니다. 매니페스트에서 앱이 액세스하려는 모든 vendorIddeviceId 쌍을 나열하는 usbDevices 권한 섹션을 선언해야 합니다.
콜백 (함수)기기 열거가 완료될 때 호출됩니다. 콜백은 하나의 매개변수, 즉 device, vendorId, productId 세 가지 속성이 있는 Device 객체의 배열로 실행됩니다. 기기 속성은 연결된 기기의 안정적인 식별자입니다. 기기의 연결을 해제할 때까지는 변경되지 않습니다. 식별자 세부정보는 불투명하며 변경될 수 있습니다. 현재 유형에 의존하지 마세요.
기기를 찾을 수 없으면 배열이 비어 있게 됩니다.

예:

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 콜백에서 수신된 객체
data (arraybuffer)인바운드 전송인 경우 기기에서 전송한 데이터가 포함됩니다.

예:

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 프로토콜은 제어, 대량, 등시, 인터럽트 등 4가지 유형의 전송을 정의합니다. 이러한 전송은 아래에 설명되어 있습니다.

전송은 기기에서 호스트로 (인바운드) 및 호스트에서 기기로 (아웃바운드) 양방향으로 발생할 수 있습니다. USB 프로토콜의 특성으로 인해 수신 및 발신 메시지 모두 호스트(Chrome 앱을 실행하는 컴퓨터)에서 시작해야 합니다. 수신(기기에서 호스트로) 메시지의 경우 자바스크립트 코드로 시작된 호스트가 '수신'으로 표시된 메시지를 기기로 전송합니다. 메시지의 세부정보는 기기에 따라 다르지만, 일반적으로 기기에서 요청하는 항목의 식별이 있습니다. 그러면 기기는 요청된 데이터로 응답합니다. 기기 응답은 Chrome에서 처리되며 전송 메서드에 지정한 콜백에 비동기식으로 전달됩니다. 호스트에서 기기로 보내는 발신 메시지도 비슷하지만, 기기에서 반환된 데이터는 응답에 포함되지 않습니다.

기기의 각 메시지에 대해 지정된 콜백은 다음과 같은 속성이 있는 이벤트 객체를 수신합니다.

속성설명
resultCode (정수)0은 성공이고 다른 값은 실패를 나타냅니다. 실패가 표시되면
chrome.extension.lastError에서 오류 문자열을
읽을 수 있습니다.
data (arraybuffer)인바운드 전송인 경우 기기에서 전송한 데이터가 포함됩니다.

예:

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에서 데이터를 보내거나 0으로 넘기며, requiredInterface는 필요하지 않습니다. 이 메서드는 간단하며 다음과 같은 세 가지 매개변수를 수신합니다.

chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback)
매개변수 (types)설명
connectionHandleusb.openDevice 콜백에서 객체가 수신되었습니다.
transferInfo아래 표의 값이 있는 매개변수 객체입니다. 자세한 내용은 USB 기기 프로토콜 사양을 확인하세요.
transferCallback()전송이 완료되면 호출됩니다.

transferInfo 객체의 값:

설명
requestType (문자열)'vendor', 'standard', 'class' 또는 '예약'으로 지정할 수 있습니다.
수신자 (문자열)'device', 'interface', 'endpoint' 또는 'other'입니다.
방향 (문자열)'in' 또는 'out'을 입력하세요. 'in' 방향은 기기에 정보를 호스트에
전송해야 함을 알리는 데 사용됩니다. USB 버스의 모든 통신은
호스트에서 시작되므로 기기에서 정보를 다시 전송할 수 있도록 '인' 전송을 사용합니다.
요청 (정수)기기의 프로토콜에 의해 정의됩니다.
값 (정수)기기의 프로토콜에 의해 정의됩니다.
색인 (정수)기기의 프로토콜에 의해 정의됩니다.
길이 (정수)방향이 'in'인 경우에만 사용됩니다. 이 데이터가 호스트가 응답에서 기대하는 데이터 양임을 기기에 알립니다.
data (arraybuffer)기기의 프로토콜에 의해 정의되며 방향이 'out'인 경우 필요합니다.

예:

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

ISOCHRONOUS 전송

등시 전송은 가장 복잡한 유형의 USB 전송입니다. 일반적으로 동영상 및 사운드와 같은 데이터 스트림에 사용됩니다. 등시 전송 (인바운드 또는 아웃바운드)을 시작하려면 usb.isochronousTransfer 메서드를 사용해야 합니다.

chrome.usb.isochronousTransfer(connectionHandle, isochronousTransferInfo, transferCallback)
매개변수설명
connectionHandleusb.openDevice 콜백에서 객체가 수신되었습니다.
isochronousTransferInfo아래 표에 있는 값이 있는 매개변수 객체입니다.
transferCallback()전송이 완료되면 호출됩니다.

isochronousTransferInfo 객체의 값:

설명
TransferInfo (객체)다음 속성을 가진 개체:
direction (string): "in" 또는 "out".
엔드포인트 (정수): 기기에서 정의합니다. 일반적으로 lsusb -v
length (정수): 방향이 'in'인 경우에만 사용됩니다. 이 데이터가 호스트가 응답에서 예상하는 데이터 양임을 기기에 알립니다.
최소 packets × packetLength여야 합니다.
data (arraybuffer): 기기 프로토콜에서 정의하며 방향이 'out'인 경우에만 사용됩니다.
패킷 (정수)이 전송에서 예상되는 총 패킷 수입니다.
패킷 길이 (정수)이 전송에서 각 패킷의 예상 길이입니다.

예:

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

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

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

BULK 전송

일괄 전송은 일반적으로 시간에 민감하지 않은 대량의 데이터를 안정적으로 전송하는 데 사용됩니다. usb.bulkTransfer에는 다음 세 가지 매개변수가 있습니다.

chrome.usb.bulkTransfer(connectionHandle, transferInfo, transferCallback);
매개변수설명
connectionHandleusb.openDevice 콜백에서 객체가 수신되었습니다.
transferInfo아래 표에 있는 값이 있는 매개변수 객체입니다.
transferCallback전송이 완료되면 호출됩니다.

transferInfo 객체의 값:

설명
방향 (문자열)'in' 또는 'out'을 입력하세요.
엔드포인트 (정수)기기의 프로토콜에 의해 정의됩니다.
길이 (정수)방향이 'in'인 경우에만 사용됩니다. 이 데이터가 호스트가 응답에서 기대하는 데이터 양임을 기기에 알립니다.
data (ArrayBuffer)기기의 프로토콜에 의해 정의되며 방향이 'out'인 경우에만 사용됩니다.

예:

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

전송 INTERRUPT건

인터럽트 전송은 시간에 민감한 소량의 데이터에 사용됩니다. 모든 USB 통신은 호스트에 의해 시작되므로 호스트 코드는 일반적으로 기기를 주기적으로 폴링하여 인터럽트 대기열에 항목이 있으면(기기에서 유지하는) 인터럽트 대기열에 데이터를 다시 보내도록 하는 인터럽트 IN 전송을 전송합니다. usb.interruptTransfer에는 세 가지 매개변수가 있습니다.

chrome.usb.interruptTransfer(connectionHandle, transferInfo, transferCallback);
매개변수설명
connectionHandleusb.openDevice 콜백에서 객체가 수신되었습니다.
transferInfo아래 표에 있는 값이 있는 매개변수 객체입니다.
transferCallback전송이 완료되면 호출됩니다. 이 콜백에는 기기의 응답이 포함되어 있지 않습니다. 콜백의 목적은 단순히 비동기 이전 요청이 처리되었음을 코드에 알리는 것입니다.

transferInfo 객체의 값:

설명
방향 (문자열)'in' 또는 'out'을 입력하세요.
엔드포인트 (정수)기기의 프로토콜에 의해 정의됩니다.
길이 (정수)방향이 'in'인 경우에만 사용됩니다. 이 데이터가 호스트가 응답에서 기대하는 데이터 양임을 기기에 알립니다.
data (ArrayBuffer)기기의 프로토콜에 의해 정의되며 방향이 'out'인 경우에만 사용됩니다.

예:

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

주의사항

일부 기기는 USB API를 통해 액세스할 수 없습니다. 일반적으로 운영체제의 커널이나 네이티브 드라이버가 사용자 공간 코드에서 기기를 제한하기 때문에 기기에 액세스할 수 없습니다. 몇 가지 예로는 OSX 시스템의 HID 프로필과 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' 그룹이 소유해야 하고 그룹 쓰기 권한이 있어야 합니다.

이 절차에는 루트 액세스가 필요하므로 앱에서 자동으로 이 작업을 실행할 수 없습니다. 최종 사용자에게 안내를 제공하고 설명을 위해 이 페이지의 주의사항 섹션으로 연결하는 것이 좋습니다.

ChromeOS에서는 usb.requestAccess를 호출합니다. 권한 브로커가 이 작업을 수행합니다.