מסמך זה מתאר איך להשתמש ב-USB API כדי לתקשר עם התקני USB. יש מכשירים שאי אפשר לגשת אליהם דרך USB API (פרטים נוספים מופיעים בקטע אזהרות שבהמשך). אפליקציות Chrome יכולות גם להתחבר למכשירים serial ו-Bluetooth.
למידע רקע על USB, ניתן לעיין במפרט הרשמי של USB. USB ב-NutShell הוא קורס קריסה סביר שיכול להועיל לך.
דרישה למניפסט
ל-USB API נדרשת ההרשאה 'usb' בקובץ המניפסט:
"permissions": [
"usb"
]
בנוסף, כדי למנוע הדפסה של טביעות אצבע, צריך להצהיר על כל סוגי המכשירים שרוצים לגשת אליהם בקובץ המניפסט. כל סוג של התקן USB מתאים לצמד של מזהה ספק/מזהה מוצר (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 שכולל את כל הזוגות vendorId ו-deviceId שהאפליקציה מבקשת גישה אליהם. |
קריאה חוזרת (פונקציה) | מתבצעת קריאה בסיום ספירת המכשיר. הקריאה החוזרת תתבצע עם פרמטר אחד, מערך של אובייקטים מסוג 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 רק באמצעות נקודות אחיזה לחיבור.
נכס | התיאור |
---|---|
device | האובייקט התקבל בקריאה חוזרת (callback) usb.getDevices. |
נתונים (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 מגדיר ארבעה סוגים של העברות: בקרה, כמות גדולה, איזוכרוני והפרעה. העברות אלה מתוארות בהמשך.
העברות יכולות להתבצע בשני הכיוונים: ממכשיר למארח (נכנס) וממארח למכשיר (יוצא). בשל אופיו של פרוטוקול ה-USB, המארח צריך להפעיל את ההודעות הנכנסות והיוצאות (המחשב שבו פועלת אפליקציית Chrome). בהודעות נכנסות (ממכשיר למארח), המארח (מופעל על ידי קוד ה-JavaScript שלכם) שולח הודעה שמסומנת כ'נכנסת' למכשיר. פרטי ההודעה תלויים במכשיר, אבל בדרך כלל יש צורך בזיהוי מסוים של הבקשה שביקשתם ממנו. לאחר מכן המכשיר מגיב בצירוף הנתונים המבוקשים. תגובת המכשיר מטופלת על ידי Chrome ונשלחת באופן אסינכרוני אל הקריאה החוזרת (callback) שציינתם בשיטת ההעברה. הודעה יוצאת (ממארח למכשיר) דומה, אבל התגובה לא מכילה נתונים שהוחזרו מהמכשיר.
לכל הודעה מהמכשיר, הקריאה החוזרת שצוינה תקבל אובייקט אירוע עם המאפיינים הבאים:
נכס | התיאור |
---|---|
resultCode (מספר שלם) | הערך 0 הוא הצלחה; ערכים אחרים מציינים כישלון. ניתן לקרוא מחרוזת שגיאה מ- chrome.extension.lastError אם מצויןכשל. |
נתונים (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);
העברות של Control
בדרך כלל, העברות בקרה משמשות לשליחה או לקבלה של פרמטרים של הגדרות או של פקודות להתקן USB. שיטת ControlTransfer תמיד שולחת או קוראת מנקודת הקצה 0, ולא צריך להשתמש ב-ClaimInterface. השיטה פשוטה וכוללת שלושה פרמטרים:
chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback)
פרמטר (סוגים) | התיאור |
---|---|
connectionHandle | האובייקט התקבל בקריאה חוזרת (callback) של usb.openDevice. |
transferInfo | אובייקט פרמטר עם ערכים מהטבלה שלמטה. לפרטים, יש לעיין במפרט הפרוטוקול של התקן ה-USB. |
transferCallback() | מופעלת כשההעברה הושלמה. |
ערכים עבור אובייקט transferInfo
:
Value | התיאור |
---|---|
requestType (מחרוזת) | "vendor", "standard", "class" או "reserve". |
נמען (מחרוזת) | 'device', 'interface', 'endpoint' או 'אחר'. |
כיוון (מחרוזת) | 'ב' או 'החוצה'. ההנחיה 'כניסה' משמשת כדי לשלוח למכשיר התראה שעליו לשלוח מידע למארח. כל התקשורת באוטובוס USB מתבצעת ביוזמת המארח, לכן יש להשתמש בהעברה בנקאית (in) כדי לאפשר למכשיר לשלוח מידע בחזרה. |
request (מספר שלם) | מוגדר על ידי הפרוטוקול של המכשיר שלך. |
ערך (מספר שלם) | מוגדר על ידי הפרוטוקול של המכשיר שלך. |
אינדקס (מספר שלם) | מוגדר על ידי הפרוטוקול של המכשיר שלך. |
אורך (מספר שלם) | בשימוש רק כשהכיוון הוא 'ב-'. הצגת התראה למכשיר שזו כמות הנתונים שהמארח מצפה לקבל בתגובה. |
נתונים (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)
פרמטר | התיאור |
---|---|
connectionHandle | האובייקט התקבל בקריאה חוזרת (callback) של usb.openDevice. |
isochronousTransferInfo | אובייקט פרמטר עם הערכים בטבלה שלמטה. |
transferCallback() | מופעלת כשההעברה הושלמה. |
ערכים עבור אובייקט isochronousTransferInfo
:
Value | התיאור |
---|---|
TransferInfo (אובייקט) | אובייקט עם המאפיינים הבאים: כיוון (מחרוזת): בפנים או בחוץ. נקודת קצה (מספר שלם): מוגדר על ידי המכשיר. בדרך כלל אפשר לאתר את המכשיר באמצעות כלי לבדיקת USB, כמו lsusb -v אורך (מספר שלם): בשימוש רק כשהכיוון הוא 'in'. התראה למכשיר שזו כמות הנתונים שהמארח מצפה לקבל בתגובה. הגודל צריך להיות לפחות packets × packetLength .נתונים (arraybuffer): מוגדר על ידי הפרוטוקול של המכשיר, בשימוש רק כשהכיוון הוא 'out'. |
חבילות (מספר שלם) | המספר הכולל של החבילות הצפויות בהעברה הזו. |
אורך המנה (מספר שלם) | האורך הצפוי של כל חבילה בהעברה הזו. |
דוגמה:
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 | האובייקט התקבל בקריאה חוזרת (callback) של usb.openDevice. |
transferInfo | אובייקט פרמטר עם הערכים בטבלה שלמטה. |
transferCallback | מופעלת כשההעברה הושלמה. |
ערכים עבור אובייקט transferInfo
:
Value | התיאור |
---|---|
כיוון (מחרוזת) | 'ב' או 'החוצה'. |
נקודת קצה (מספר שלם) | מוגדר על ידי הפרוטוקול של המכשיר שלך. |
אורך (מספר שלם) | בשימוש רק כשהכיוון הוא 'ב-'. הצגת התראה למכשיר שזו כמות הנתונים שהמארח מצפה לקבל בתגובה. |
נתונים (ArrayBuffer) | מוגדר על ידי הפרוטוקול של המכשיר שלך, בשימוש רק כאשר הכיוון הוא 'out'. |
דוגמה:
var transferInfo = {
"direction": "out",
"endpoint": 1,
"data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer
};
העברות ביניים
העברות מושהות משמשות לזמן קטן של מידע אישי רגיש. מכיוון שכל תקשורת USB מופעלת על ידי המארח, קוד המארח בדרך כלל מבצע סקר שוק מדי פעם, ושולח הפרעות בהעברות, כך שהמכשיר שולח נתונים בחזרה אם יש משהו בתור ההפרעות (תחזוקה של המכשיר). לפרמטר usb.interruptTransfer יש שלושה פרמטרים:
chrome.usb.interruptTransfer(connectionHandle, transferInfo, transferCallback);
פרמטר | התיאור |
---|---|
connectionHandle | האובייקט התקבל בקריאה חוזרת (callback) של usb.openDevice. |
transferInfo | אובייקט פרמטר עם הערכים בטבלה שלמטה. |
transferCallback | מופעלת כשההעברה הושלמה. לתשומת ליבך, הקריאה החוזרת הזו לא כוללת את תגובת המכשיר. מטרת הקריאה החוזרת היא פשוט ליידע את הקוד שלך שבקשות ההעברה האסינכרוניות עובדו. |
ערכים עבור אובייקט transferInfo
:
Value | התיאור |
---|---|
כיוון (מחרוזת) | 'ב' או 'החוצה'. |
נקודת קצה (מספר שלם) | מוגדר על ידי הפרוטוקול של המכשיר שלך. |
אורך (מספר שלם) | בשימוש רק כשהכיוון הוא 'ב-'. הצגת התראה למכשיר שזו כמות הנתונים שהמארח מצפה לקבל בתגובה. |
נתונים (ArrayBuffer) | מוגדר על ידי הפרוטוקול של המכשיר שלך, בשימוש רק כאשר הכיוון הוא 'out'. |
דוגמה:
var transferInfo = {
"direction": "in",
"endpoint": 1,
"length": 2
};
chrome.usb.interruptTransfer(connectionHandle, transferInfo, optionalCallback);
נקודות שצריך לשים לב אליהן:
לא ניתן לגשת לכל המכשירים באמצעות ממשק ה-API של USB. באופן כללי, אי אפשר לגשת למכשירים כי ליבת מערכת ההפעלה או מנהל התקן מקומי מונעים אותם מקוד המרחב של המשתמש. חלק מהדוגמאות הן מכשירים עם פרופיל HID במערכות OSX וכונני עט בחיבור 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" ולהיות בעל הרשאות כתיבה קבוצתיות.
האפליקציה לא יכולה לעשות זאת באופן אוטומטי כי כדי לבצע את התהליך הזה נדרשת גישה לרמה הבסיסית. אנחנו ממליצים לספק הוראות למשתמשי קצה ולקשר לקטע אזהרות בדף הזה כדי לקבל הסבר.
ב-ChromeOS, פשוט קוראים ל-usb.requestAccess. מתווך ההרשאות עושה זאת עבורכם.