بروتوكول HID لجهاز تتبُّع حركة الرأس

إنّ بروتوكول الواجهة البشرية لجهاز تتبُّع الرأس (HID) المتاح للأجهزة التي تعمل بالإصدار 13 من نظام التشغيل Android والإصدارات الأحدث يسمح بتوصيل جهاز تتبُّع حركة الرأس بجهاز Android عبر USB أو البلوتوث وأن يتعرّض لإطار عمل Android وتطبيقاته من خلال إطار عمل أجهزة الاستشعار. يُستخدَم هذا البروتوكول للتحكّم في تأثير الصوت الافتراضي (الصوت الثلاثي الأبعاد). تستخدِم هذه الصفحة المصطلحين الجهاز و المضيف وفقًا لتعريفهما في البلوتوث، حيث يشير الجهاز إلى جهاز تتبُّع الرأس ويشير المضيف إلى مضيف Android.

على الشركات المصنّعة للأجهزة ضبط إعدادات أجهزة Android لتفعيل ميزة استخدام بروتوكول HID لجهاز تتبُّع الرأس. للحصول على معلومات أكثر تفصيلاً حول عملية الإعداد، يمكنك الاطّلاع على دليل أجهزة الاستشعار الديناميكية.

تفترض هذه الصفحة معرفة المَراجع التالية:

بنية المستوى الأعلى

يتعرّف إطار عمل Android على جهاز تتبُّع الرأس كجهاز واجهة بشرية (HID).

للحصول على مثال كامل لموصّف HID صالح، راجِع الملحق 1: مثال لموصّف HID.

في المستوى الأعلى، يكون جهاز تتبُّع الرأس عبارة عن مجموعة تطبيقات تتضمّن صفحة Sensors (0x20) واستخدام Other: Custom (0xE1). وتتضمّن هذه المجموعة العديد من حقول البيانات (الإدخالات) والخصائص (الميزات).

السمات وحقول البيانات

يصف هذا القسم السمات وحقول البيانات في مجموعة تطبيق لجهاز تتبُّع حركة الرأس.

السمة: وصف أداة الاستشعار (0x0308)

سمة وصف أداة الاستشعار (0x0308) هي خاصية سلسلة ASCII (8 بت) للقراءة فقط ويجب أن تحتوي على القيم التالية:

الإصدار 1.0 من أداة تتبُّع الرأس:

#AndroidHeadTracker#1.0

الإصدار 2.0 من أداة تتبُّع حركة الرأس (متاحة في الإصدار 15 من Android أو إصدار أحدث)، والذي يتضمّن تقنية LE Audio:

#AndroidHeadTracker#2.0#x

x هو عدد صحيح (1 أو 2 أو 3) يشير إلى طريقة النقل المتوافقة:

  • 1: قائمة التحكّم بالوصول
  • 2: ISO
  • 3: ACL + ISO

لا يُتوقّع وجود عنصر إنهاء فارغ، ما يعني أنّ الحجم الإجمالي لهذه السمة هو 23 حرفًا من 8 بتات للإصدار 1.0.

تُستخدَم هذه السمة كعنصر تمييز لتجنُّب حدوث تداخل مع الأجهزة الاستشعارية المخصّصة الأخرى.

الموقع: المعرّف الفريد الدائم (0x0302)

سمة المعرّف الفريد الدائم (0x0302) هي صفيف للقراءة فقط يتألف من 16 عنصرًا، بسعة 8 بت لكل عنصر (إجمالي 128 بت). ولا يُتوقَّع وجود علامة إنهاء فارغة. هذا السمة اختيارية.

تسمح هذه السمة لأجهزة تتبُّع حركة الرأس والمدمجة في أجهزة الصوت بالإشارة إلى جهاز الصوت المرتبط بها. المخططات التالية متاحة.

جهاز تتبُّع مستقل للرأس

إذا لم تتوفّر سمة المعرّف الفريد الدائم (0x0302) أو تم ضبطها على جميع القيم الصفرية، يعني هذا أنّ جهاز تتبُّع الرأس غير متصل دائمًا بجهاز صوتي ويمكن استخدامه بشكل منفصل مثلاً من خلال السماح للمستخدم بربط جهاز تتبُّع الرأس يدويًا بجهاز سماعي منفصل.

الإشارة إلى عنوان MAC الخاص بالبلوتوث

ثُمانِي واحد 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
القيمة 0 0 0 0 0 0 0 0 B T عنوان MAC الخاص بالبلوتوث

في هذا المخطط، يجب أن تكون الأوكتات الثمانية الأولى هي 0، ويجب أن تحتوي الأوكتات 8 و9 على قيم ASCII B وT على التوالي، ويتم تفسير الأوكتات الستة التالية على أنّها عنوان MAC لتقنية Bluetooth، بافتراض أنّ جهاز تتبُّع حركة الرأس ينطبق على أي جهاز صوتي يتضمّن عنوان MAC هذا. يجب أن يكون هذا العنوان عنوان الهوية، حتى إذا كان الجهاز يستخدم عنوان MAC عشوائيًا لإنشاء الاتصالات. يجب أن تعرض الأجهزة المزوّدة بوضعَين وتتم ربطها عبر البلوتوث الكلاسيكي (بتنسيق HID 1.0) وBluetooth LE (بتنسيق HID 2.0) وصفَين HID بعنوان الهوية نفسه. يجب أن تعرض الأجهزة المزودة بوضعَين والتي تتضمّن جهازَين منفصلَين، أحدهما على يمين الجهاز والآخر على يساره، واجهة HID لتقنية Bluetooth LE باستخدام الجهاز الأساسي المزوّد بوضعَين بدلاً من الجهاز الثانوي المزوّد بتقنية LE فقط.

مرجع يستخدم المعرّف الفريد العالمي (UUID)

عند ضبط أهم وحدة بت (MSB) من الثمانيت 8 (≥0x80)، يتم تفسير الحقل على أنّه معرّف UUID، كما هو محدّد في RFC-4122. يوفّر الجهاز السماعي المقابل المعرّف الفريد العالمي نفسه، الذي يتم تسجيله على إطار عمل Android، من خلال آلية غير محدّدة خاصة بنوع النقل المستخدَم.

الموقع: حالة إعداد التقارير (0x0316)

سمة حالة إعداد التقارير (0x0316) هي سمة للقراءة/الكتابة تحتوي على الدلالات العادية كما هو موضّح في مواصفات HID. يستخدم المضيف هذه السمة للإشارة إلى الجهاز بالأحداث التي يجب الإبلاغ عنها. ولا يتم استخدام سوى القيمتين "لا توجد أحداث" (0x0840) و"جميع الأحداث" (0x0841).

يجب أن تكون القيمة الأولية لهذا الحقل "بلا أحداث" ويجب ألا يتم تعديله مطلقًا بواسطة الجهاز، بل المضيف فقط.

السمة: حالة الطاقة (0x0319)

سمة Power State (0x0319) هي خاصية للقراءة/الكتابة تحتوي على دلالات قياسية على النحو المحدَّد في مواصفات HID. يستخدم المضيف هذه الخاصية لإخبار الجهاز بحالة الطاقة التي يجب أن يكون فيها. لا يتم استخدام سوى القيمتَين "تشغيل كامل" (0x0851) و"إيقاف" (0x0855).

يتم تحديد القيمة الأولية لهذا الحقل من خلال الجهاز ويجب ألا يعدّلها الجهاز مطلقًا، ولكن يجب أن يعدّله المضيف فقط.

السمة: فاصل إعداد التقارير (0x030E)

خاصية فاصل إعداد التقارير (0x030E) هي خاصية للقراءة/الكتابة تحتوي على الدلالات العادية كما هو محدّد في مواصفات HID. يستخدم المضيف هذه السمة للإشارة إلى الجهاز بعدد المرات التي يجب فيها الإبلاغ عن قراءات البيانات. تكون الوحدات بالثواني. يتم تحديد النطاق الصالح لهذه القيمة من خلال الجهاز ويتم وصفه باستخدام آلية الحد الأدنى/الحد الأقصى المادي. يجب توفّر ما لا يقل عن 50 هرتز لمعدّل إعداد التقارير، وأن يكون الحدّ الأقصى المقترَح لمعدّل إعداد التقارير هو 100 هرتز. وبالتالي، يجب أن يكون الحدّ الأدنى للفاصل الزمني للتقرير أقل من أو يساوي 20 ملّي ثانية، ويُنصَح بأن يكون أكبر من أو يساوي 10 ملّي ثانية.

الموقع: شركة LE Transport محجوزة من قِبل المورِّدين (0xF410)

سمة LE Transport المحفوظة من قِبل المورِّدين (0xF410) هي سمة للقراءة/الكتابة تحتوي على الدلالات العادية كما هو موضَّح في مواصفات HID. يستخدم المضيف هذه السمة للإشارة إلى وسيط النقل المحدّد (ACL أو ISO). لن يتم استخدام سوى القيمتين ACL (0xF800) وISO (0xF801)، ويجب تضمينهما في المجموعة المنطقية.

يتم ضبط هذه السمة قبل حالات الطاقة أو إعداد التقارير.

حقل البيانات: القيمة المخصّصة 1 (0x0544)

حقل "القيمة المخصّصة 1" (0x0544) هو حقل إدخال يُستخدَم لتسجيل معلومات تتبُّع الرأس الفعلية. إنه مصفوفة مكونة من 3 عناصر، ويتم تفسيرها وفقًا لقواعد HID العادية للقيم الفيزيائية كما هو محدّد في الفقرة 6.2.2.7 من مواصفات HID. النطاق الصالح لكل عنصر هو [- تبيّن، ارتداء] راديو. تكون الوحدات دائمًا راديان.

ويتم تفسير العناصر على النحو التالي: [rx, ry, rz]، بحيث يكون [rx, ry, rz] متّجه تدوير، يمثّل التحويل من الإطار المرجعي إلى إطار الرأس. يجب أن يكون حجم الإشعاع في النطاق [0..π].

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

  • X من الأذن اليسرى إلى اليمين
  • الحرف اللاتيني y من الجزء الخلفي للرأس إلى الأنف (من الخلف إلى الأمام)
  • الحرف اللاتيني z من الرقبة إلى أعلى الرأس

حقل البيانات: القيمة المخصّصة 2 (0x0545)

حقل "القيمة المخصّصة 2" (0x0545) هو حقل إدخال يُستخدَم لإعداد تقارير عن معلومات تتبُّع حركة الرأس الفعلية. إنها صفيفة مكونة من 3 عناصر ثابتة ويتم تفسيرها وفقًا لقواعد HID العادية للقيم الفيزيائية. تكون الوحدات دائمًا راديان/ثانية.

يتم تفسير العناصر على النحو التالي: [vx, vy, vz]، بحيث يكون [vx, vy, vz] هو ناقل دوران، يمثّل السرعة الزاويّة لإطار الرأس (بالنسبة إلى نفسه).

حقل البيانات: القيمة المخصّصة 3 (0x0546)

إنّ حقل القيمة المخصّصة 3 (0x0546) هو حقل إدخال يُستخدم لتتبّع انقطاعات الخدمة في الإطار المرجعي. إنه عدد صحيح عددي يبلغ 8 بت. يجب أن يضيفه الجهاز (مع التفاف) كل مرة يتم فيها تغيير الإطار المرجعي، على سبيل المثال، إذا تمت إعادة ضبط حالته لخوارزمية فلتر الاتجاه المستخدَمة لتحديد الاتجاه. يتم تفسير هذه القيمة وفقًا لقواعد HID العادية للقيم الفيزيائية. ومع ذلك، لا يهم القيمة المادية والوحدات. إنّ المعلومات الوحيدة ذات الصلة بالمضيف هي القيمة التي تم تغييرها. ولتجنب المشكلات الرقمية المتعلقة بفقدان الدقة عند التحويل من الوحدات المنطقية إلى الوحدات المادية، يوصى بتعيين قيم الحد الأدنى والحد الأقصى المادي وأس الوحدة إلى صفر في هذا الحقل.

بنية التقرير

تتميّز عملية تجميع المواقع في تقارير (حسب تخصيص أرقام تعريف التقارير) بمرونة. لتحقيق الكفاءة، ننصحك بفصل المواقع التي تتيح القراءة فقط عن المواقع التي تتيح القراءة والكتابة.

بالنسبة إلى حقول البيانات، يجب أن تكون حقول "القيمة المخصّصة 1" و"القيمة المخصّصة 2" و"القيمة المخصّصة 3" في التقرير نفسه وأن تكون في تقرير واحد فقط لجهاز معيّن (مجموعة تطبيقات).

إرسال تقارير الإدخال

يجب أن يرسل الجهاز تقارير الإدخال بشكل دوري وغير متزامن (من خلال رسائل HID INPUT) عند استيفاء جميع هذه الشروط:

  • تم ضبط سمة Power State (حالة الطاقة) على Full Power (طاقة كاملة).
  • تم ضبط موقع "حالة إعداد التقارير" على "جميع الأحداث".
  • تكون قيمة سمة "فترة إعداد التقارير" غير صفرية.

يحدد الموقع "الفاصل الزمني لإعداد التقارير" عدد مرات إرسال التقارير. في حال عدم استيفاء أي من الشروط المذكورة أعلاه، يجب ألا يرسل الجهاز أي تقارير.

التوافق الأمامي والخلفي

يستخدم بروتوكول HID لتتبُّع حركة الرأس مخطّط إصدارات يتيح إجراء التحديثات، مع السماح بإمكانية التشغيل التفاعلي بين مضيف وجهاز يستخدمان إصدارات مختلفة من البروتوكول. ويتم تحديد إصدارات البروتوكول برقمين، كبير وثانوي، ولهما دلالات مميزة كما هو موضّح في الأقسام التالية.

يمكن تحديد الإصدارات المتوافقة مع الجهاز من خلال فحص خاصية وصف أداة الاستشعار (0x0308).

توافق بسيط مع الإصدار

تكون التغييرات في الإصدار الثانوي متوافقة مع الإصدارات الثانوية السابقة التي تستند إلى الإصدار الرئيسي نفسه. في تحديثات الإصدار الثانوي، يتجاهل المضيف حقول وخصائص البيانات الإضافية. على سبيل المثال، يكون الجهاز الذي يستخدم الإصدار 1.6 من البروتوكول متوافقًا مع مضيف يتوافق مع الإصدار 1.x من البروتوكول، بما في ذلك الإصدار 1.5.

التوافق مع الإصدارات الرئيسية

يُسمح بتغييرات غير متوافقة مع الأنظمة القديمة لإجراء التغييرات على الإصدارات الرئيسية. لإتاحة إمكانية التشغيل التفاعلي مع العديد من الإصدارات الرئيسية مع المضيفين القديم والجديد، يمكن للأجهزة تحديد مجموعات تطبيقات متعددة في واصفات التقارير. مثلاً:

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,

    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#1.5"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

      ...

    HID_END_COLLECTION,

    HID_COLLECTION(HID_APPLICATION),
        // Feature report 12 (read-only).
        HID_REPORT_ID(12),

        // Magic value: "#AndroidHeadTracker#2.4"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

      ...

    HID_END_COLLECTION,
};

في هذه الحالة، يمكن للمضيف سرد جميع مجموعات التطبيقات المختلفة التي يُعلِن عنها الجهاز، وفحص سمة "وصف أداة الاستشعار" لتحديد إصدارات البروتوكول التي ينفّذها كلّ منها، ثم اختيار أحدث إصدار من البروتوكول يتيح للمضيف استخدامه. عند اختياره، يعمل المضيف مع البروتوكول الفردي الذي تم اختياره طوال فترة اتصال الجهاز.

الملحق: مثال على أداة وصف HID

يوضّح المثال التالي واصف HID نموذجيًا صالحًا. ويستخدم رمز برمجي مختصر شائعًا في لغة C، وهو متوفر في استخدامات أدوات استشعار HID (القسم 4.1).

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#1.0"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // UUID.
        HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(16),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // Feature report 1 (read/write).
        HID_REPORT_ID(1),

        // 1-bit on/off reporting state.
        HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 1-bit on/off power state.
        HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
        HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
        HID_LOGICAL_MIN_8(0x00),
        HID_LOGICAL_MAX_8(0x3F),
        HID_PHYSICAL_MIN_8(10),
        HID_PHYSICAL_MAX_8(100),
        HID_REPORT_SIZE(6),
        HID_REPORT_COUNT(1),
        HID_USAGE_SENSOR_UNITS_SECOND,
        HID_UNIT_EXPONENT(0xD),  // 10^-3
        HID_FEATURE(HID_DATA_VAR_ABS),

        // Input report 1

        // Orientation as rotation vector (scaled to [-pi..pi] rad).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED),  // -314159265
        HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12),  // 314159265
        HID_UNIT_EXPONENT(0x08),  // 10^-8
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_8(0xE0),
        HID_PHYSICAL_MAX_8(0x20),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Reference frame reset counter.
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
        HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
        HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
        HID_PHYSICAL_MIN_8(0x00),
        HID_PHYSICAL_MAX_8(0x00),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(1),
        HID_INPUT(HID_DATA_VAR_ABS),

    HID_END_COLLECTION,
};

الملحق 2: مثال على وصف HID بالإصدار 2.0

يوضّح المثال التالي وصف HID بالإصدار 2.0 لجهاز يتوافق مع نقل ACL في Bluetooth LE فقط.

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#2.0#1"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(25),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // UUID.
        HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(16),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // Feature report 1 (read/write).
        HID_REPORT_ID(1),

        // 1-bit on/off reporting state.
        HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 1-bit on/off power state.
        HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
        HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
        HID_LOGICAL_MIN_8(0x00),
        HID_LOGICAL_MAX_8(0x3F),
        HID_PHYSICAL_MIN_8(10),
        HID_PHYSICAL_MAX_8(100),
        HID_REPORT_SIZE(6),
        HID_REPORT_COUNT(1),
        HID_USAGE_SENSOR_UNITS_SECOND,
        HID_UNIT_EXPONENT(0xD),  // 10^-3
        HID_FEATURE(HID_DATA_VAR_ABS),

        // 1-bit transport selection
        HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ACL,
            HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ISO,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // Input report 1

        // Orientation as rotation vector (scaled to [-pi..pi] rad).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED),  // -314159265
        HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12),  // 314159265
        HID_UNIT_EXPONENT(0x08),  // 10^-8
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_8(0xE0),
        HID_PHYSICAL_MAX_8(0x20),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Reference frame reset counter.
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
        HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
        HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
        HID_PHYSICAL_MIN_8(0x00),
        HID_PHYSICAL_MAX_8(0x00),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(1),
        HID_INPUT(HID_DATA_VAR_ABS),

    HID_END_COLLECTION,
};