HID-Protokoll für Kopf-Tracker

Das Human Interface Device (HID)-Protokoll des Head Trackers, das für Geräte mit Android 13 und höher verfügbar ist, ermöglicht es, ein Kopf-Tracking-Gerät über USB oder Bluetooth mit einem Android-Gerät zu verbinden und das Android-Framework und die Apps über das Sensoren-Framework zu sehen. Mit diesem Protokoll wird ein Audio-Virtualizer-Effekt (3D-Audio) gesteuert. Auf dieser Seite werden die Begriffe Gerät und Host im Sinne von Bluetooth verwendet, wobei device für das Head-Tracking-Gerät und host für den Android-Host steht.

Gerätehersteller müssen ihre Android-Geräte so konfigurieren, dass das HID-Protokoll des Head-Trackers unterstützt wird. Weitere Informationen zur Konfiguration finden Sie in der README-Datei zu dynamischen Sensoren.

Auf dieser Seite wird davon ausgegangen, dass Sie mit den folgenden Ressourcen vertraut sind:

Struktur der obersten Ebene

Das Android-Framework identifiziert den Kopf-Tracker als HID-Gerät.

Ein vollständiges Beispiel für einen gültigen HID-Descriptor finden Sie unter Anhang 1: Beispiel für einen HID-Descriptor.

Auf der obersten Ebene ist das Head-Tracker-Gerät eine App-Sammlung mit der Sensors-Seite (0x20) und der Other: Custom-Nutzung (0xE1). Innerhalb dieser Sammlung befinden sich mehrere Datenfelder (Eingaben) und Eigenschaften (Funktionen).

Eigenschaften und Datenfelder

In diesem Abschnitt werden die Eigenschaften und Datenfelder in einer Anwendungssammlung eines Kopf-Trackers beschrieben.

Eigenschaft: Sensorbeschreibung (0x0308)

Das Attribut „Sensorbeschreibung“ (0x0308) ist ein schreibgeschütztes ASCII-Stringattribut (8-Bit), das die folgenden Werte enthalten muss:

Kopf-Tracker Version 1.0:

#AndroidHeadTracker#1.0

Kopf-Tracker-Version 2.0 (verfügbar unter Android 15 oder höher), einschließlich Unterstützung für LE-Audio:

#AndroidHeadTracker#2.0#x

x ist eine Ganzzahl (1, 2, 3), die den unterstützten Transport angibt:

  • 1: ACL
  • 2: ISO
  • 3: ACL + ISO

Es wird kein Nullterminator erwartet. Das bedeutet, dass die Gesamtgröße dieses Attributs für Version 1.0 23 8-Bit-Zeichen beträgt.

Diese Property dient als Unterscheidungsmerkmal, um Kollisionen mit anderen benutzerdefinierten Sensoren zu vermeiden.

Property: Persistent Unique ID (0x0302)

Die Eigenschaft „Persistent Unique ID“ (0x0302) ist ein schreibgeschütztes Array mit 16 Elementen mit jeweils 8 Bit (insgesamt 128 Bit). Es wird kein Nullabschlusszeichen erwartet. Dieses Attribut ist optional.

Mit dieser Property können Geräte mit integrierter Kopfverfolgung auf das Audiogerät verweisen, an das sie angeschlossen sind. Die folgenden Schemas werden unterstützt.

Eigenständiger Kopf-Tracker

Wenn die Eigenschaft „Persistent Unique ID“ (0x0302) nicht vorhanden ist oder auf „0“ gesetzt ist, ist der Kopf-Tracker nicht dauerhaft mit einem Audiogerät verbunden und kann separat verwendet werden. Der Nutzer kann den Kopf-Tracker beispielsweise manuell mit einem separaten Audiogerät verknüpfen.

Referenz mit Bluetooth-MAC-Adresse

Oktett 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Wert 0 0 0 0 0 0 0 0 B T Bluetooth-MAC

Bei diesem Schema müssen die ersten 8 Oktette 0 sein, die Oktette 8 und 9 müssen die ASCII-Werte B und T enthalten. Die folgenden 6 Oktette werden als Bluetooth-MAC-Adresse interpretiert, vorausgesetzt, das Kopftrackergerät gilt für jedes Audiogerät mit dieser MAC-Adresse. Diese Adresse muss die Identitätsadresse sein, auch wenn das Gerät eine zufällige MAC-Adresse zum Herstellen von Verbindungen verwendet. Geräte im Dual-Modus, die eine Verbindung über klassisches Bluetooth (HID-Format der Version 1.0) und Bluetooth LE (HID-Format der Version 2.0) herstellen, müssen zwei HID-Deskriptoren mit derselben Identitätsadresse verfügbar machen. Dual-Modus-Geräte mit getrennten linken und rechten Geräten müssen Bluetooth LE HID über das primäre Dual-Modus-Gerät anstelle des sekundären LE-Geräts bereitstellen.

Referenz mit UUID

Wenn das höchstwertige Bit (MSB) von Oktett 8 festgelegt ist (≥0x80), wird das Feld wie in RFC 4122 angegeben als UUID interpretiert. Das entsprechende Audiogerät bietet dieselbe UUID, die im Android-Framework über einen nicht angegebenen Mechanismus registriert wird, der für den verwendeten Transporttyp spezifisch ist.

Property: Meldestatus (0x0316)

Das Attribut „Reporting State“ (0x0316) ist ein Lese-/Schreibattribut mit der Standardsemantik, wie in der HID-Spezifikation definiert. Der Host verwendet dieses Attribut, um dem Gerät mitzuteilen, welche Ereignisse gemeldet werden sollen. Es werden nur die Werte „Keine Ereignisse“ (0x0840) und „Alle Ereignisse“ (0x0841) verwendet.

Der Anfangswert für dieses Feld muss „No Events“ lauten und darf niemals vom Gerät, nur vom Host geändert werden.

Property: Leistungszustand (0x0319)

Das Attribut „Power State“ (0x0319) ist ein Lese-/Schreibattribut mit der Standardsemantik, wie in der HID-Spezifikation definiert. Der Host verwendet diese Eigenschaft, um dem Gerät anzuzeigen, in welchem Energiestatus er sich befinden muss. Es werden nur die Werte „Vollständig ein“ (0x0851) und „Aus“ (0x0855) verwendet.

Der Ausgangswert für dieses Feld wird vom Gerät bestimmt und darf niemals vom Gerät, nur vom Host geändert werden.

Property: Berichtszeitraum (0x030E)

Die Eigenschaft „Berichtsintervall“ (0x030E) ist eine Lese-/Schreibeigenschaft mit der Standardsemantik, wie in der HID-Spezifikation definiert. Der Host verwendet diese Eigenschaft, um dem Gerät anzugeben, wie oft seine Datenlesungen gemeldet werden sollen. Die Einheit sind Sekunden. Der gültige Bereich für diesen Wert wird vom Gerät bestimmt und mit dem Mechanismus der physischen Min/Max-Werte beschrieben. Es muss eine Berichtsrate von mindestens 50 Hz unterstützt werden und die empfohlene maximale Berichtsrate beträgt 100 Hz. Daher muss das minimale Berichtsintervall 20 ms oder weniger betragen. Es wird empfohlen, es sollte größer oder gleich 10 ms sein.

Property: Vendor-reserved LE Transport (0xF410)

Das vom Anbieter reservierte LE-Transport-Attribut (0xF410) ist ein Lese-/Schreibattribut mit der in der HID-Spezifikation definierten Standardsemantik. Der Host verwendet diese Eigenschaft, um den ausgewählten Transport (ACL oder ISO) anzugeben. Es werden nur die Werte ACL (0xF800) und ISO (0xF801) verwendet und beide müssen in der logischen Sammlung enthalten sein.

Diese Eigenschaft wird vor den Status „Ein/Aus“ oder „Melden“ konfiguriert.

Datenfeld: Benutzerdefinierter Wert 1 (0x0544)

Das Feld „Benutzerdefinierter Wert 1“ (0x0544) ist ein Eingabefeld, in dem die tatsächlichen Informationen zur Kopfverfolgung erfasst werden. Es handelt sich um ein Array mit drei Elementen, das gemäß den normalen HID-Regeln für physikalische Werte gemäß Abschnitt 6.2.2.7 der HID-Spezifikation interpretiert wird. Der gültige Bereich für jedes Element ist [-±, ‐] rad. Die Einheiten sind immer Bogenmaß.

Die Elemente werden so interpretiert: [rx, ry, rz], wobei [rx, ry, rz] ein Rotationsvektor ist, der die Transformation vom Referenzframe zum Headframe darstellt. Die Amplitude muss im Bereich [0..π] liegen.

Der Referenzrahmen ist beliebig, aber in der Regel fixiert und muss rechtshänderisch sein. Eine geringe Abweichung ist akzeptabel. Die Kopfachsen sind:

  • X vom linken Ohr nach rechts
  • Y vom Kopf bis zur Nase (hinten nach vorne)
  • Z vom Hals bis zum Scheitel

Datenfeld: Benutzerdefinierter Wert 2 (0x0545)

Das Feld für den benutzerdefinierten Wert 2 (0x0545) ist ein Eingabefeld, das für die Erfassung der tatsächlichen Head-Tracking-Informationen verwendet wird. Es ist ein Fixpunkt-Array mit drei Elementen, das gemäß den normalen HID-Regeln für physikalische Werte interpretiert wird. Die Einheiten sind immer Radian/Sekunde.

Die Elemente werden als [vx, vy, vz] interpretiert, wobei [vx, vy, vz] ein Drehvektor ist, der die Winkelgeschwindigkeit des Kopf-Frames (relativ zu sich selbst) darstellt.

Datenfeld: Benutzerdefinierter Wert 3 (0x0546)

Das Feld für den benutzerdefinierten Wert 3 (0x0546) ist ein Eingabefeld zum Verfolgen von Abweichungen im Referenzframe. Es ist eine skalare Ganzzahl mit 8 Bit. Sie muss jedes Mal vom Gerät mit Wraparound erhöht werden, wenn der Referenzrahmen geändert wird, beispielsweise wenn ein Ausrichtungsfilteralgorithmus zur Bestimmung der Ausrichtung verwendet wurde, dessen Status zurückgesetzt wurde. Dieser Wert wird gemäß den normalen HID-Regeln für physikalische Werte interpretiert. Der physikalische Wert und die Einheiten spielen jedoch keine Rolle. Die einzige für den Host relevante Information ist ein geänderter Wert. Um numerische Probleme im Zusammenhang mit einem Genauigkeitsverlust bei der Konvertierung von logischen in physische Einheiten zu vermeiden, wird empfohlen, die Werte für den physikalischen Mindestwert, den physikalischen Maximalwert und den Einheitenexponenten für dieses Feld auf null zu setzen.

Berichtsstruktur

Die Gruppierung von Properties in Berichten (durch Zuweisung von Berichts-IDs) ist flexibel. Aus Effizienzgründen empfehlen wir, die schreibgeschützten Attribute von den nicht schreibgeschützten Attributen zu trennen.

Für die Datenfelder müssen sich die benutzerdefinierten Werte 1, 2 und 3 im selben Bericht und nur in einem Bericht für ein bestimmtes Gerät befinden (App-Sammlung).

Eingabeberichte senden

Das Gerät muss regelmäßig und asynchron (über HID-INPUT-Nachrichten) Eingabeberichte senden, wenn alle diese Bedingungen erfüllt sind:

  • Die Eigenschaft "Leistungsstatus" ist auf "Full Power" gesetzt.
  • Das Attribut „Berichtsstatus“ ist auf „Alle Ereignisse“ festgelegt.
  • Die Eigenschaft Reporting-Intervall ist ungleich null.

Mit der Eigenschaft "Berichtsintervall" wird festgelegt, wie oft die Berichte gesendet werden. Wenn eine der oben genannten Bedingungen nicht erfüllt ist, darf das Gerät keine Berichte senden.

Vorwärts- und Abwärtskompatibilität

Für das HID-Protokoll des Head Trackers wird ein Versionsverwaltungsschema verwendet, das Aktualisierungen ermöglicht und gleichzeitig die Interoperabilität zwischen einem Host und einem Gerät ermöglicht, die unterschiedliche Versionen des Protokolls verwenden. Versionen für das Protokoll werden durch zwei Zahlen (Haupt- und Nebennummer) identifiziert. Diese haben eine unterschiedliche Semantik, wie in den folgenden Abschnitten beschrieben.

Die von einem Gerät unterstützten Versionen können anhand der Eigenschaft der Sensorbeschreibung (0x0308) ermittelt werden.

Kompatibilität von Nebenversionen

Änderungen an der Nebenversion sind abwärtskompatibel mit früheren Nebenversionen, die auf derselben Hauptversion basieren. Bei Updates der Minorversion ignoriert der Host zusätzliche Datenfelder und -eigenschaften. Beispielsweise ist ein Gerät, das die Protokollversion 1.6 verwendet, mit einem Host kompatibel, der die Protokollversion 1.x einschließlich Version 1.5 unterstützt.

Kompatibilität mit Hauptversionen

Nicht abwärtskompatible Änderungen sind für Änderungen an Hauptversionen zulässig. Um mehrere Hauptversionen für die Interoperabilität mit alten und neuen Hosts zu unterstützen, können Geräte mehrere Anwendungssammlungen in ihren Berichtsdeskriptoren angeben. Beispiel:

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,
};

In diesem Fall kann der Host alle verschiedenen Anwendungssammlungen auflisten, die vom Gerät beworben werden. Dabei wird die Sensorbeschreibungseigenschaft untersucht, um die jeweils implementierten Protokollversionen zu ermitteln und dann die neueste Protokollversion auszuwählen, die vom Host unterstützt wird. In diesem Fall arbeitet der Host mit dem einzelnen Protokoll, das für die Lebensdauer der Geräteverbindung ausgewählt wurde.

Anhang: Beispiel eines HID-Deskriptors

Das folgende Beispiel zeigt einen typischen gültigen HID-Deskriptor. Es verwendet die gängigen C-Makros, die in HID-Sensornutzungen (Abschnitt 4.1) bereitgestellt werden.

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,
};

Anhang 2: Beispiel für einen HID-Beschreibungsblock der Version 2.0

Das folgende Beispiel zeigt einen HID-Beschreibungsblock der Version 2.0 für ein Gerät, das nur den Bluetooth LE ACL-Transport unterstützt.

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,
};