USB デバイス

このドキュメントでは、USB API を使用して USB デバイスと通信する方法について説明します。一部のデバイスには USB API 経由ではアクセスできません(詳しくは、下記の注意事項をご覧ください)。Chrome アプリは、serialBluetooth デバイスにも接続できます。

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
      }
    ]
  }
]

デバイスを探す

ユーザーのシステムに接続されているデバイスが 1 つ以上あるかどうかを判断するには、usb.getDevices メソッドを使用します。

chrome.usb.getDevices(enumerateDevicesOptions, callback);
パラメータ(型)説明
EnumerateDevicesOptions(オブジェクト)バス上の適切なデバイスの種類を見つけるために使用される vendorId(long)と productId(long)の両方を指定するオブジェクト。マニフェストで usbDevices 権限セクションを宣言し、アプリがアクセスする vendorIddeviceId のペアをすべてリストする必要があります。
コールバック(関数)デバイスの列挙が完了すると呼び出されます。コールバックは 1 つのパラメータ、つまり devicevendorIdproductId の 3 つのプロパティを持つ 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(配列バッファ)転送がインバウンドの場合にデバイスから送信されたデータが含まれます。

例:

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 メソッドを使用します。このメソッドは、1 回の呼び出しでデバイスの列挙、アクセスのリクエスト、オープンを行います。

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 アプリを実行するパソコン)が開始する必要があります。インバウンド(デバイスからホスト間)メッセージの場合、ホスト(JavaScript コードで開始)は「インバウンド」フラグが付いたメッセージをデバイスに送信します。メッセージの詳細はデバイスによって異なりますが、通常はデバイスから何をリクエストしているかが識別されます。デバイスはリクエストされたデータを返します。デバイスのレスポンスは Chrome によって処理され、転送メソッドで指定したコールバックと非同期に配信されます。送信(ホストからデバイス)メッセージも同様ですが、レスポンスにはデバイスから返されたデータは含まれません。

デバイスからのメッセージごとに、指定されたコールバックが、次のプロパティを持つイベント オブジェクトを受け取ります。

プロパティ説明
resultCode(整数)0 は成功、他の値は失敗を示します。失敗が示された場合、
chrome.extension.lastError からエラー文字列を読み取ることができます。
data(配列バッファ)転送がインバウンドの場合にデバイスから送信されたデータが含まれます。

例:

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

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

CONTROL の移管

コントロール転送は通常、USB デバイスに構成パラメータまたはコマンド パラメータを送受信するために使用されます。controlTransfer メソッドは常にエンドポイント 0 に対して送受信を行うため、claimInterface は必要ありません。このメソッドはシンプルで、次の 3 つのパラメータを受け取ります。

chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback)
パラメータ(タイプ)説明
connectionHandleusb.openDevice コールバックでオブジェクトを受信しました。
transferInfo以下の表の値を含むパラメータ オブジェクト。詳しくは、USB デバイスのプロトコル仕様をご確認ください。
transferCallback()転送の完了時に呼び出されます。

transferInfo オブジェクトの値:

説明
requestType(文字列)「vendor」、「standard」、「class」、「rereservation」。
受信者(文字列)「device」、「interface」、「endpoint」、「other」のいずれかです。
方向(文字列)「in」か「out」で表します。「in」方向は、ホストに情報を送信する必要があることを
デバイスに通知するために使用されます。USB バス上の
通信はすべてホストによって開始されるため、デバイスから情報を返信できるようにするには「イン」転送を使用します。
リクエスト(整数)デバイスのプロトコルによって定義されます。
値(整数)デバイスのプロトコルによって定義されます。
番号(整数)デバイスのプロトコルによって定義されます。
長さ(整数)方向が「in」の場合にのみ使用されます。これがホストが想定しているデータ量であることをデバイスに通知します。
data(配列バッファ)デバイスのプロトコルで定義されます。方向が「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(文字列): 「in」または「out」。
endpoint(整数): デバイスで定義されます。通常は USB インストロスペクション ツールで調べることができます。たとえば、lsusb -v
length(integer): 方向が「in」の場合にのみ使用します。これがホストが応答時に想定しているデータ量であることをデバイスに通知します。
少なくとも packets × packetLength にする必要があります。
data(配列バッファ): デバイスのプロトコルによって定義されます。方向が「out」の場合にのみ使用されます。
パケット(整数)この転送で想定されるパケットの合計数。
PacketLength(整数)この転送で予想される各パケットの長さ。

例:

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

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

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

一括送金

一括転送は通常、時間的制約のない大量のデータを信頼性の高い方法で転送するために使用されます。usb.bulkTransfer には次の 3 つのパラメータがあります。

chrome.usb.bulkTransfer(connectionHandle, transferInfo, transferCallback);
パラメータ説明
connectionHandleusb.openDevice コールバックでオブジェクトを受信しました。
transferInfo以下の表の値を含むパラメータ オブジェクト。
transferCallback転送の完了時に呼び出されます。

transferInfo オブジェクトの値:

説明
方向(文字列)「in」か「out」で表します。
エンドポイント(整数)デバイスのプロトコルによって定義されます。
長さ(整数)方向が「in」の場合にのみ使用されます。これがホストが想定しているデータ量であることをデバイスに通知します。
データ(ArrayBuffer)デバイスのプロトコルで定義されます。方向が「out」の場合にのみ使用されます。

例:

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

INTERRUPT 転送

割り込み転送は、時間のかかる少量のデータに使用されます。USB 通信はすべてホストによって開始されるため、ホストコードは通常、定期的にデバイスをポーリングして割り込み IN 転送を送信し、割り込みキューに(デバイスで管理される)何かがあればデバイスからデータを返送します。usb.interruptTransfer には次の 3 つのパラメータがあります。

chrome.usb.interruptTransfer(connectionHandle, transferInfo, transferCallback);
パラメータ説明
connectionHandleusb.openDevice コールバックでオブジェクトを受信しました。
transferInfo以下の表の値を含むパラメータ オブジェクト。
transferCallback転送の完了時に呼び出されます。このコールバックにはデバイスのレスポンスが含まれていません。このコールバックの目的は、非同期転送リクエストが処理されたことをコードに通知することです。

transferInfo オブジェクトの値:

説明
方向(文字列)「in」か「out」で表します。
エンドポイント(整数)デバイスのプロトコルによって定義されます。
長さ(整数)方向が「in」の場合にのみ使用されます。これがホストが想定しているデータ量であることをデバイスに通知します。
データ(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」によって所有され、グループ書き込み権限が付与されている必要があります。

この手順は root アクセス権を必要とするため、アプリで自動的に実行することはできません。エンドユーザーに説明を行うとともに、このページの注意事項へのリンクとともに説明しておくことをおすすめします。

ChromeOS では、usb.requestAccess を呼び出します。これは権限ブローカーによって行われます。