本文档介绍了如何使用 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 权限部分,列出应用要访问的所有 vendorId 和 deviceId 对。 |
callback(函数) | 在设备枚举完成时调用。系统将使用一个形参执行该回调,即包含以下三个属性的 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 应用的计算机)。对于入站(设备到主机)邮件,主机(发起的 )会发送标记为“入站”的消息,发送到设备如需详细了解 消息取决于设备,但通常能识别您请求的内容 。然后,设备会返回请求的数据。设备的响应由 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)
参数(类型) | 说明 |
---|---|
connectionHandle | 在 usb.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)
参数 | 说明 |
---|---|
connectionHandle | 在 usb.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);
参数 | 说明 |
---|---|
connectionHandle | 在 usb.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);
参数 | 说明 |
---|---|
connectionHandle | 在 usb.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 即可。权限代理会为您执行此操作。