התקני USB

במסמך הזה מוסבר איך להשתמש ב-USB API כדי לתקשר עם התקני USB. מכשירים מסוימים לא ניתן לגשת אליהן דרך ה-USB API (פרטים נוספים אפשר למצוא בסעיף האזהרות שבהמשך). אפליקציות Chrome אפשר גם להתחבר למכשירים טוריים ו-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);
פרמטר (סוג)תיאור
EnumerateמכשיריםOptions (אובייקט)אובייקט שמציין גם 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 באמצעות נקודות אחיזה לחיבור.

נכסתיאור
מכשיראובייקט שהתקבל בקריאה חוזרת (callback) של usb.getDevices.
data (מערךbuffer)מכילה את הנתונים שנשלחו מהמכשיר, אם ההעברה בוצעה.

דוגמה:

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 ונשלח באופן אסינכרוני לקריאה החוזרת שציינתם בשיטת ההעברה. יציאה ההודעה (ממארח למכשיר) דומה, אבל התגובה לא מכילה נתונים שמוחזרים מהמכשיר.

עבור כל הודעה מהמכשיר, הקריאה החוזרת שצוינה תקבל אובייקט אירוע עם המאפיינים הבאים:

נכסתיאור
resultCode (מספר שלם)0 הוא הצלחה; ערכים אחרים מצביעים על כשל. אפשר
לקרוא מחרוזת שגיאה החל מ-chrome.extension.lastError כשמצוין כשל
.
data (מערךbuffer)מכילה את הנתונים שנשלחו מהמכשיר, אם ההעברה בוצעה.

דוגמה:

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:

ערךתיאור
requestType (מחרוזת)'vendor', 'standard', 'class' או 'שמורים'.
נמען (מחרוזת)'device', 'interface', 'endpoint' או 'אחר'.
כיוון (מחרוזת)"in" או 'out'. "in" הכיוון משמש כדי להודיע למכשיר
שהוא צריך לשלוח מידע למארח. כל התקשורת באוטובוס
USB מופעלת ביוזמת המארח, לכן צריך להשתמש ב-"in" העברה כדי לאפשר למכשיר
לשלוח מידע בחזרה.
request (מספר שלם)מוגדר על-ידי הפרוטוקול של המכשיר.
ערך (שלם)מוגדר על-ידי הפרוטוקול של המכשיר.
אינדקס (שלם)מוגדר על-ידי הפרוטוקול של המכשיר.
length (מספר שלם)משמש רק כשהכיוון הוא 'in'. מודיעה למכשיר שזו כמות הנתונים שהמארח מצפה לקבל בתגובה.
data (מערךbuffer)מוגדר על ידי הפרוטוקול של המכשיר, נדרש כשהכיוון לא פועל.

דוגמה:

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:

ערךתיאור
TransferInfo (אובייקט)אובייקט עם המאפיינים הבאים:
כיוון (מחרוזת): "in" או 'out'.
נקודת הקצה (שלמה): הוגדרה על ידי המכשיר. בדרך כלל ניתן למצוא את הכלי לבדיקת ספקטרום של USB, כמו lsusb -v
length (מספר שלם): רק כשהכיוון הוא "in". מציין למכשיר שזו כמות הנתונים שהמארח מצפה לקבל בתגובה.
הנתונים צריכים להיות לפחות packets × packetLength.
נתונים (arraybuffer): מוגדרים על ידי הפרוטוקול של המכשיר. משמש רק כשהכיוון הוא 'out'.
מנות (שלמים)המספר הכולל של המנות הצפויות בהעברה הזו.
packageLength (מספר שלם)האורך הצפוי של כל חבילה בהעברה הזו.

דוגמה:

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);
פרמטרתיאור
connectionHandleאובייקט התקבל בקריאה חוזרת (callback) של usb.openDevice.
transferInfoאובייקט פרמטר עם הערכים בטבלה הבאה.
transferCallbackמופעל כשההעברה תסתיים.

ערכים של האובייקט transferInfo:

ערךתיאור
כיוון (מחרוזת)"in" או 'out'.
נקודת קצה (שלם)מוגדר על-ידי הפרוטוקול של המכשיר.
length (מספר שלם)משמש רק כשהכיוון הוא '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);
פרמטרתיאור
connectionHandleאובייקט התקבל בקריאה חוזרת (callback) של usb.openDevice.
transferInfoאובייקט פרמטר עם הערכים בטבלה הבאה.
transferCallbackמופעל כשההעברה תסתיים. שימו לב שהקריאה החוזרת (callback) הזו לא מכילה את התגובה של המכשיר. מטרת הקריאה החוזרת היא פשוט ליידע את הקוד שלכם שבקשות ההעברה האסינכרוניות עובדו.

ערכים של האובייקט transferInfo:

ערךתיאור
כיוון (מחרוזת)"in" או 'out'.
נקודת קצה (שלם)מוגדר על-ידי הפרוטוקול של המכשיר.
length (מספר שלם)משמש רק כשהכיוון הוא 'in'. מודיעה למכשיר שזו כמות הנתונים שהמארח מצפה לקבל בתגובה.
נתונים (ArrayBuffer)מוגדר על ידי הפרוטוקול של המכשיר; משמש רק כשהכיוון הוא 'out'.

דוגמה:

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

נקודות שצריך לשים לב אליהן:

יש מכשירים שלא ניתן לגשת אליהם באמצעות ממשק ה-API של USB. באופן כללי, אין גישה למכשירים כי ליבה (kernel) של מערכת ההפעלה או מנהל התקן נייטיב מעכבים את הגישה שלהם לקוד המרחב של המשתמש. במידה מסוימת לדוגמה: מכשירים עם פרופילי ממשק אנושי (HID) במערכות OSX וכונני עטים בחיבור USB.

ברוב מערכות Linux, התקני USB ממופים עם הרשאות קריאה בלבד כברירת מחדל. כדי לפתוח את המכשיר באמצעות ממשק ה-API הזה, וגם למשתמש שלך תהיה גישת כתיבה אליו. פתרון פשוט הוא להגדיר כלל udev. יצירת קובץ /etc/udev/rules.d/50-yourdevicename.rules עם ההגדרות הבאות content:

SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"

לאחר מכן, פשוט מפעילים מחדש את הדימון (daemon) של ה-udev: service udev restart. אפשר לבדוק אם הרשאות המכשיר מוגדר כראוי באמצעות השלבים הבאים:

  • מריצים את lsusb כדי למצוא את מספרי האוטובוס והמכשיר.
  • מריצים את ls -al /dev/bus/usb/[bus]/[device]. הקובץ הזה צריך להיות בבעלות הקבוצה Plugindev ויש להם הרשאות כתיבה קבוצתיות.

האפליקציה לא יכולה לעשות זאת באופן אוטומטי כי לתהליך הזה נדרשת גישה לרמה הבסיסית (root). ההמלצות שלנו שאתם מספקים הוראות למשתמשי הקצה ומקשרים לקטע אזהרות בדף הזה כדי לספק הסברנו.

ב-ChromeOS, פשוט קוראים ל-usb.requestAccess. סוכן ההרשאות מבצע את הפעולה הזו בשבילך.