USB 裝置

本文說明如何使用 USB API 與 USB 裝置通訊。部分裝置 無法透過 USB API 存取 (詳情請參閱下方注意事項一節)。Chrome 應用程式 也可以連線至序列藍牙裝置。

如需 USB 的背景資訊,請參閱官方 USB 規格NutShell 中的 USB 連接埠是合理的速成課程中,或許對您有所幫助。

資訊清單規定

USB API 需要「usb」權限:

"permissions": [
  "usb"
]

此外,為避免指紋列印,您必須宣告 您要在資訊清單檔案中存取哪些項目每個類型的 USB 裝置都會對應到一個供應商 ID/產品 ID (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 權限部分,其中列出應用程式要存取的所有 vendorIddeviceId 組合。
回呼 (函式)裝置列舉完成時呼叫。系統會使用一個參數執行回呼,這是一種 Device 物件陣列,其中包含三個屬性:devicevendorIdproductId。裝置屬性是已連結裝置的穩定 ID。除非裝置接上電源,否則這個數值不會改變。ID 的詳細資料不透明,隨時可能變更。請勿依賴當前類型。
如果找不到裝置,陣列會是空白的。

範例:

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 應用程式的電腦)。如果是內送 (裝置對主機) 郵件,主機 (已啟動) ) 會傳送標記為「inbound」的訊息。詳細資料 訊息取決於裝置,但通常都能識別您提出要求的內容 從資料中學習接著,裝置會傳回要求的資料。裝置回應會由 Chrome 並以非同步的方式傳送至您在轉移方法中指定的回呼。出站 (主機對裝置) 訊息相似,但回應不包含裝置傳回的資料。

針對裝置的每則訊息,指定的回呼都會收到含有 屬性:

屬性說明
resultCode (整數)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 傳送至/讀取,且沒有宣告介面 這通常代表交易 不會十分要求關聯語意方法簡單,會收到三個參數:

chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback)
參數 (類型)說明
connectionHandleusb.openDevice 回呼中接收到的物件。
transferInfo包含下表值的參數物件。詳情請參閱 USB 裝置通訊協定規格。
transferCallback()轉移作業完成後叫用。

transferInfo 物件的值:

說明
requestType (字串)「vendor」、「standard」、「class」或「reserve」等值
收件者 (字串)「裝置」、「介面」、「端點」或「其他」
方向 (字串)「位於」或是「輸出」「進」方向是用來通知裝置,
應傳送資訊給主機。USB
匯流排上的所有通訊都是由主機發起,因此請使用「in」資料轉移,讓裝置
能夠傳回資訊。
要求 (整數)由裝置通訊協定定義。
值 (整數)由裝置通訊協定定義。
索引 (整數)由裝置通訊協定定義。
長度 (整數)僅適用於方向為「in」時。通知裝置,瞭解主機預期回應的資料量。
資料 (陣列緩衝區)由裝置通訊協定定義;方向為「離開」時必須要求。

範例:

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」。
端點 (整數): 由裝置定義。通常可透過 USB 掃描工具取得,例如 lsusb -v
長度 (整數): 只有在方向「以」表示時才能使用。通知裝置此伺服器預期回應的資料量。
應至少為 packets × packetLength
資料 (陣列緩衝區): 由裝置的通訊協定定義。只有在方向為「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」時。通知裝置,瞭解主機預期回應的資料量。
資料 (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」時。通知裝置,瞭解主機預期回應的資料量。
資料 (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 Daemon:service udev restart。你可以查看裝置權限是否 請按照下列步驟正確設定:

  • 執行 lsusb 找出公車和裝置號碼。
  • 執行 ls -al /dev/bus/usb/[bus]/[device]。 這個檔案應由「plugdev」群組擁有並擁有 群組寫入權限。

這個程序需要 Root 存取權,因此您的應用程式無法自動執行此操作。建議做法 為使用者提供操作說明,並參閱本頁的注意事項部分, 解釋。

在 ChromeOS 中,直接呼叫 usb.requestAccess 即可。權限代理程式會為您執行這項作業。