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 对枚举设备。

您必须在 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);
参数(类型)说明
EnumerateDevicesOptions(对象)一个同时指定 vendorId(长整型)和 productId(长整型)的对象,用于在总线上查找正确的设备类型。您的清单必须声明 usbDevices 权限部分,列出应用要访问的所有 vendorIddeviceId 对。
callback(函数)在设备枚举完成时调用。系统将使用一个形参执行该回调,即包含以下三个属性的 Device 对象数组:devicevendorIdproductId。设备属性是已连接设备的稳定标识符。在拔下设备电源线之前,此模式不会更改。标识符的详情不透明,可能会发生变化。请勿依赖其当前类型。
如果找不到任何设备,该数组将为空。

示例:

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 应用的计算机)。对于入站(设备到主机)邮件,主机(发起的 )会发送标记为“入站”的消息,发送到设备如需详细了解 消息取决于设备,但通常能识别您请求的内容 。然后,设备会返回请求的数据。设备的响应由 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);

CONTROL 次换乘

控制传输通常用于将配置或命令参数发送到 USB 设备。controlTransfer 方法始终向端点 0 发送数据或从端点 0 读取数据,并且没有声明接口 必填字段。此方法很简单,可接收三个参数:

chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback)
参数(类型)说明
connectionHandleusb.openDevice 回调中收到了对象。
transferInfo包含下表中的值的参数对象。如需了解详情,请查看您的 USB 设备协议规范。
transferCallback()在转移完成后调用。

transferInfo 对象的值:

说明
requestType(字符串)“vendor”、“standard”、“class”即“已预留”。
收件人(字符串)“device”“interface”“endpoint”或“其他”
方向(字符串)“在”即“输出”“in”用于通知设备
应向主机发送信息。USB 总线上的所有通信都由主机发起,因此请使用“输入”
以允许设备
发回信息。
request(整数)由设备的协议定义。
值(整数)由设备的协议定义。
索引(整数)由设备的协议定义。
长度(整数)仅在方向为“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(字符串) :“in”或“out”。
endpoint(整数): 由您的设备定义。通常可通过查看 USB 检查工具找到,例如 lsusb -v
length (integer):仅在方向为“in”时使用。 通知设备这是主机预期响应的数据量。
应不小于 packets × packetLength
数据(数组缓冲区): 由设备的协议定义;仅在方向为“出”时使用。
数据包(整数)此传输作业中预期的数据包总数。
packageLength(整数)此次传输中每个数据包的预期长度。

示例:

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);
参数说明
connectionHandleusb.openDevice 回调中收到了对象。
transferInfo具有下表中的值的参数对象。
transferCallback在转移完成后调用。

transferInfo 对象的值:

说明
方向(字符串)“在”即“输出”
端点(整数)由设备的协议定义。
长度(整数)仅在方向为“in”时使用。通知设备这是主机预期响应的数据量。
数据 (ArrayBuffer)由设备的协议定义;仅在方向为“出”时使用。

示例:

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)由设备的协议定义;仅在方向为“出”时使用。

示例:

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

注意事项

并非所有设备都可以通过 USB API 进行访问。一般情况下,设备处于无法访问的状态 操作系统的内核或原生驱动程序会使其阻止用户空间代码。部分 例如在 OSX 系统上具有 HID 配置文件的设备,以及 U 盘。

在大多数 Linux 系统中,默认情况下,USB 设备具有只读权限。要打开 设备,则您的用户也需要拥有该设备的写入权限。一个简单的解决方法是 并设置一个 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 即可。权限代理会为您执行此操作。