أجهزة USB

يوضّح هذا المستند كيفية استخدام USB API للاتصال بأجهزة USB. بعض الأجهزة لا يمكن الوصول إليها من خلال واجهة برمجة تطبيقات USB (راجع قسم تنبيهات أدناه لمعرفة التفاصيل). تطبيقات Chrome يمكنهم أيضًا الاتصال بالأجهزة المتسلسلة والبلوتوث.

للحصول على معلومات أساسية حول USB، يمكنك الاطّلاع على مواصفات USB الرسمية. USB في NutShell هو مسار مكثف معقول قد يكون مفيدًا لك.

متطلبات ملف البيان

تتطلب واجهة برمجة تطبيقات USB استخدام "usb" إذن في ملف البيان:

"permissions": [
  "usb"
]

بالإضافة إلى ذلك، ولتجنّب بصمات الأصابع، عليك الإفصاح عن جميع أنواع الأجهزة التي تريد الوصول إليه في ملف البيان. يتوافق كل نوع من أجهزة USB مع معرِّف المورِّد/معرّف المنتج (VID/PID). يمكنك استخدام usb.getDevices لتعداد الأجهزة حسب زوج VID/PID.

يجب الإفصاح عن أزواج VID/PID لكل نوع من الأجهزة التي تريد استخدامها ضمن usbDevices. إذن في ملف البيان لتطبيقك، كما هو موضّح في المثال أدناه:

"permissions": [
  {
    "usbDevices": [
      {
        "vendorId": 123,
        "productId": 456
      }
    ]
  }
]

بدايةً من Chrome 57، فإن متطلب تعريف جميع أنواع الأجهزة في بيان التطبيق هو مريح للتطبيقات التي تعمل كـ تطبيقات Kiosk على ChromeOS. بالنسبة إلى تطبيقات Kiosk، يمكنك استخدام سمة إذن 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);
المَعلمة (type)الوصف
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 باستخدام مقابض الاتصال.

الموقعالوصف
جهازتم استلام عنصر في معاودة الاتصال 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). بالنسبة إلى الرسائل الواردة (من جهاز إلى مضيف)، يبدأ المضيف (بدءًا من الجهاز) بواسطة رمز JavaScript) رسالة تم وضع علامة عليها على أنها "الواردة" إلى الجهاز. توضح تفاصيل تعتمد الرسالة على الجهاز، ولكن عادة ما يكون لديها بعض المعلومات حول ما تطلبه منه. ويستجيب الجهاز بعد ذلك بالبيانات المطلوبة. يتم التعامل مع استجابة الجهاز بواسطة Chrome ويتم تسليمه بشكل غير متزامن إلى رد الاتصال الذي تحدده في طريقة النقل. رحلة ذهاب (من جهاز إلى آخر) مشابهة، غير أنّ الردّ لا يتضمّن بيانات يتم عرضها من الجهاز.

بالنسبة إلى كل رسالة من الجهاز، سيتلقّى رد الاتصال المحدّد كائنًا للحدث مع السمات التالية:

الموقعالوصف
كود النتيجة (عدد صحيح)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);

Ctrl من عمليات النقل

تُستخدم عمليات النقل للتحكم بشكل عام لإرسال أو استلام معلمات التهيئة أو الأوامر إلى USB الخاص بك. ترسل طريقة Controltransfer دائمًا إلى نقطة النهاية 0 أو تقرأها، ولا يتم تحديد واجهة مطالبتك مطلوبة. هذه الطريقة بسيطة وتتلقى ثلاث معلمات:

chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback)
المَعلمة (الأنواع)الوصف
connectionHandleتم استلام عنصر في معاودة الاتصال usb.openDevice.
transferInfoكائن المَعلمة باستخدام قيم من الجدول أدناه راجِع مواصفات بروتوكول جهاز USB للحصول على التفاصيل.
transferCallback()تم استدعاء عند اكتمال عملية النقل.

قيم العنصر transferInfo:

القيمةالوصف
requestType (سلسلة)"المورِّد"، "عادي"، "الفئة" أو "محجوزة".
مستلم (سلسلة)"الجهاز"، "الواجهة"، "نقطة النهاية" أو "غير ذلك".
الاتجاه (سلسلة)"في" أو "الخروج". "in" يُستخدم الاتجاه لإعلام الجهاز
بأنه يجب أن يرسل المعلومات إلى المضيف. يبدأ المضيف في جميع الاتصالات على ناقل
USB، لذا يُرجى استخدام حرف "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" أو "خارج".
نقطة النهاية (عدد صحيح): يحدّدها جهازك. يمكن العثور عليها عادةً من خلال الاطّلاع على أداة فحص USB، مثل lsusb -v
length (integer): تُستخدم فقط عندما يكون الاتجاه "في". لإعلام الجهاز بأنّ هذا هو مقدار البيانات التي يتوقعها المضيف في الردّ.
يجب أن تكون درجة الحرارة أقل من 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:

القيمةالوصف
الاتجاه (سلسلة)"في" أو "الخروج".
نقطة النهاية (عدد صحيح)مُحددة من خلال بروتوكول الجهاز.
الطول (عدد صحيح)يُستخدَم فقط عندما يكون الاتجاه "في". يتم إشعار الجهاز بأنّ هذا هو مقدار البيانات التي يتوقّعها المضيف عند الردّ.
data (ArrayBuffer)محددة من خلال بروتوكول الجهاز؛ تُستخدم فقط عندما يكون الاتجاه "خارجًا".

مثال:

var transferInfo = {
  "direction": "out",
  "endpoint": 1,
  "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer
};

عمليات النقل الفاصلة

يتم استخدام عمليات النقل الموقتة لتقليل البيانات الحساسة من حيث الوقت. نظرًا لأن جميع اتصالات USB يبدأه المضيف، فإن رمز المضيف عادةً ما يفحص الجهاز بشكل دوري، ويُرسل مقاطعة IN عمليات النقل التي ستجعل الجهاز يعيد إرسال البيانات إذا كان هناك أي شيء في قائمة انتظار المقاطعة (يتم الاحتفاظ به بواسطة الجهاز). يتضمّن usb.interruptTransfer ثلاث مَعلمات:

chrome.usb.interruptTransfer(connectionHandle, transferInfo, transferCallback);
المعلمةالوصف
connectionHandleتم استلام عنصر في معاودة الاتصال usb.openDevice.
transferInfoكائن المَعلمة بالقيم الواردة في الجدول أدناه
transferCallbackتم استدعاء عند اكتمال عملية النقل. لاحِظ أنّ معاودة الاتصال هذه لا تتضمّن استجابة الجهاز. الغرض من معاودة الاتصال هو إعلام الرمز بأنه تمت معالجة طلبات النقل غير المتزامنة.

قيم العنصر transferInfo:

القيمةالوصف
الاتجاه (سلسلة)"في" أو "الخروج".
نقطة النهاية (عدد صحيح)مُحددة من خلال بروتوكول الجهاز.
الطول (عدد صحيح)يُستخدَم فقط عندما يكون الاتجاه "في". يتم إشعار الجهاز بأنّ هذا هو مقدار البيانات التي يتوقّعها المضيف عند الردّ.
data (ArrayBuffer)محددة من خلال بروتوكول الجهاز؛ تُستخدم فقط عندما يكون الاتجاه "خارجًا".

مثال:

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

المحاذير

لا يمكن الوصول إلى بعض الأجهزة من خلال واجهة برمجة تطبيقات USB. بشكل عام، لا يمكن الوصول إلى الأجهزة للأسباب التالية: تعمل نواة نظام التشغيل أو برنامج التشغيل الأصلي على إبعادها عن رمز مساحة المستخدم. بعض الإشعارات ومن الأمثلة على ذلك الأجهزة التي تحتوي على ملفات تعريف HID على أنظمة OSX، ومحركات أقراص USB.

في معظم أنظمة 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" ويمتلكون أذونات الكتابة للمجموعة.

لا يمكن لتطبيقك تنفيذ هذا الإجراء تلقائيًا لأنّ هذا الإجراء يتطلّب الوصول إلى الجذر. ننصحك بما يلي: تقديم تعليمات للمستخدمين النهائيين وإدراج رابط يؤدي إلى قسم تنبيهات في هذه الصفحة للحصول على شرح.

في نظام التشغيل ChromeOS، ما عليك سوى طلب usb.requestAccess. يتولى وسيط الأذونات هذا الإجراء نيابةً عنك.