Das Human Interface Device (HID)-Protokoll für den Head Tracker, das für Geräte verfügbar ist mit Android 13 und höher, ermöglicht zur Verbindung mit einem Android-Gerät über USB oder Bluetooth und das Android-Framework und die Apps können über die sensors-Framework. Dieses Protokoll wird zum Steuern eines Audio-Virtualisierungseffekts (3D-Audio) verwendet. Auf dieser Seite werden die Begriffe Gerät und host im Sinne von Bluetooth, wobei device für das Head-Tracker-Gerät steht und host steht für den Android-Host.
Gerätehersteller müssen ihre Android-Geräte so konfigurieren, dass sie die Unterstützung für das HID-Protokoll des Kopf-Trackers. Nähere Informationen über finden Sie in der README-Datei für dynamische 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 Kopf-Tracker-Gerät eine App-Sammlung mit dem
Sensors
Seite (0x20
) und die Other: Custom
-Nutzung (0xE1
). In diesem
Sammlung besteht aus mehreren Datenfeldern (Eingaben) und Eigenschaften (Features).
Properties und Datenfelder
In diesem Abschnitt werden die Eigenschaften und Datenfelder in einer Anwendungssammlung eines Kopf-Trackers beschrieben.
Property: Sensor Description (0x0308
)
Das Attribut „Sensorbeschreibung“ (0x0308
) ist ein schreibgeschützter ASCII-String (8-Bit)
die die folgenden Werte enthalten muss:
Kopf-Tracker-Version 1.0:
#AndroidHeadTracker#1.0
Kopf-Tracker-Version 2.0 (verfügbar mit Android 15 oder )), wobei auch LE Audio unterstützt wird:
#AndroidHeadTracker#2.0#x
x
ist eine Ganzzahl (1
, 2
, 3
), die den Supporttransport angibt:
- 1: ACL
- 2: ISO
- 3: ACL + ISO
Es wird kein Nullterminator erwartet. Die Gesamtgröße dieses Attributs beträgt also 23 8‑Bit-Zeichen für Version 1.0.
Diese Property dient als Unterscheidungsmerkmal, um Kollisionen mit anderen benutzerdefinierten Sensoren zu vermeiden.
Property: Persistente eindeutige ID (0x0302
)
Das Attribut der persistenten eindeutigen ID (0x0302
) ist ein schreibgeschütztes Array aus 16
-Elemente mit jeweils 8 Bit (insgesamt 128 Bit). Es wird kein Nullterminator 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.
Verweis 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, Oktett 8 und 9 müssen die ASCII-Werte B
bzw. T
enthalten und die folgenden 6 Oktette werden als Bluetooth-MAC-Adresse interpretiert, vorausgesetzt, der Head-Tracker gilt für jedes Audiogerät mit dieser MAC-Adresse. Diese Adresse muss die
Identitätsadresse, auch wenn das Gerät eine zufällige MAC-Adresse verwendet, um
Verbindungen. Dual-Mode-Geräte, die über Bluetooth Classic (HID-Format v1.0) und Bluetooth LE (HID-Format v2.0) verbunden werden, müssen zwei HID-Beschreibungen mit derselben Identitätsadresse bereitstellen. 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.
Verweis mit UUID
Wenn das höchstwertige Bit (MSB) von Oktett 8 (≥0x80
) festgelegt wird, wird das Feld
als UUID interpretiert, wie in
RFC-4122. Das entsprechende Audiogerät stellt dieselbe UUID bereit, die über einen nicht näher beschriebenen Mechanismus, der für den verwendeten Transporttyp spezifisch ist, im Android-Framework registriert wird.
Property: Berichtsstatus (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. Nur die Werte „Nein“
Es werden die Ereignisse "0x0840
" und "Alle Ereignisse" (0x0841
) verwendet.
Der Anfangswert dieses Feldes darf „No Events“ (Keine Ereignisse) sein. vom Gerät, nur vom Host verändert.
Property: Leistungszustand (0x0319
)
Das Attribut „Leistungsstatus“ (0x0319
) ist ein Lese-/Schreibattribut mit
Standardsemantik, wie in der HID-Spezifikation definiert. Der Host verwendet diese
, um dem Gerät anzugeben, in welchem Zustand das Gerät sich befinden muss. Nur die
werden die Werte „Volle Power“ (0x0851
) und „Ausschalten“ (0x0855
) verwendet.
Der Anfangswert für dieses Feld wird vom Gerät bestimmt und darf niemals vom Gerät, sondern nur vom Host geändert werden.
Property: Berichtszeitraum (0x030E
)
Das Attribut „Berichtsintervall“ (0x030E
) ist eine Lese-/Schreibeigenschaft mit folgendem Wert:
Standardsemantik, wie in der HID-Spezifikation definiert. Der Host gibt mit dieser Property an, wie oft das Gerät seine Datenwerte melden soll.
Die Einheit sind Sekunden. Der gültige Bereich für diesen Wert wird vom Gerät bestimmt und mit dem Mechanismus „Physikalisches Minimum/Maximum“ beschrieben. Es muss eine Berichtsrate von mindestens 50 Hz unterstützt werden. Die empfohlene maximale Berichtsrate beträgt 100 Hz. Das Mindestberichtsintervall muss daher maximal 20 ms betragen. Wir empfehlen jedoch, einen Wert von mindestens 10 ms zu verwenden.
Property: Vom Anbieter reservierter LE Transport (0xF410
)
Das Attribut für den vom Anbieter reservierten LE-Transport (0xF410
) ist ein Lese-/Schreibattribut
die die in der HID-Spezifikation definierte Standardsemantik hat. Host
verwendet diese Eigenschaft, um den ausgewählten Transport anzugeben (ACL oder ISO). Es werden nur die Werte „ACL“ (0xF800
) und „ISO“ (0xF801
) verwendet. Beide müssen in der logischen Sammlung enthalten sein.
Diese Eigenschaft wird vor dem Ein/Aus- oder Berichterstellungsstatus konfiguriert.
Datenfeld: Benutzerdefinierter Wert 1 (0x0544
)
Das Feld Benutzerdefinierter Wert 1 (0x0544
) ist ein Eingabefeld für die
Informationen zur Erfassung von Kopfbewegungen. Es ist ein Array mit drei Elementen,
die normalen HID-Regeln für physikalische Werte, wie in Abschnitt 6.2.2.7
die HID-Spezifikation. Der gültige Bereich für jedes Element ist [-hol, pi] rad. Einheiten
sind immer Radianten.
Die Elemente werden so interpretiert: [rx, ry, rz]
, wobei [rx, ry, rz]
ein
Rotationsvektor,
die die Transformation vom Referenzframe zum Headframe darstellt.
Die Magnitude 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 für die
Informationen zur Erfassung von Kopfbewegungen. Ein Festpunktarray mit 3 Elementen,
wird gemäß den normalen HID-Regeln für physikalische Werte interpretiert.
Die Einheiten sind immer Radian/Sekunde.
Die Elemente werden so interpretiert: [vx, vy, vz]
, wobei [vx, vy, vz]
ein
Rotationsvektor,
die die Winkelgeschwindigkeit des Headframes (relativ zu sich selbst) darstellt.
Datenfeld: Benutzerdefinierter Wert 3 (0x0546
)
Das Feld für den benutzerdefinierten Wert 3 (0x0546
) ist ein Eingabefeld für das Tracking
Diskontinuitäten im Bezugsrahmen. Es ist eine skalare Ganzzahl 8 Bit
Größe. Er muss jedes Mal vom Gerät inkrementiert (mit Überlauf) werden, wenn sich der Referenzrahmen ändert, z. B. wenn der Status eines Algorithmus für den Ausrichtungsfilter zurückgesetzt wurde, der zur Bestimmung der Ausrichtung verwendet wird. Dieser Wert ist
wird gemäß den normalen HID-Regeln für physikalische Werte interpretiert. Sie können jedoch
der physikalische Wert
und die Einheiten spielen keine Rolle. Die einzige für den Host relevante Information ist ein geänderter Wert. Um Probleme mit der Genauigkeit bei der Umwandlung von logischen in physikalische Einheiten zu vermeiden, sollten Sie die Werte für „Physikalisches Minimum“, „Physikalisches Maximum“ und „Einheitsexponent“ für dieses Feld auf null setzen.
Berichtsstruktur
Die Gruppierung von Properties in Berichten (durch Zuweisung von Berichts-IDs) ist flexibel. Aus Effizienzgründen empfehlen wir, die schreibgeschützten Eigenschaften zu trennen. aus den Lese-/Schreib-Attributen.
Die benutzerdefinierten Werte 1, 2 und 3 für die Datenfelder müssen sich in den Feldern „Benutzerdefinierter Wert 1“, „2“ und „3“ befinden. und sich für ein bestimmtes Gerät (App-Sammlung) nur in einem Bericht befinden.
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.
- Die Eigenschaft 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 Kopf-Trackers wird ein Versionierungsschema verwendet, und gleichzeitig die Interoperabilität zwischen einem Host und einem Gerät, Versionen des Protokolls. Versionen des Protokolls werden durch zwei Zahlen identifiziert, Haupt- und Nebenversion, die unterschiedliche Bedeutungen haben, wie in den folgenden Abschnitten beschrieben.
Die von einem Gerät unterstützten Versionen können anhand des Attributs „Sensorbeschreibung“ (0x0308
) ermittelt werden.
Kompatibilität mit 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. Ein Gerät mit der Protokollversion 1.6 ist beispielsweise mit einem Host kompatibel, der die Protokollversion 1.x unterstützt, einschließlich Version 1.5.
Kompatibilität mit Hauptversionen
Nicht abwärtskompatible Änderungen sind für Änderungen an Hauptversionen zulässig. Bis mehrere Hauptversionen für die Interoperabilität mit alten und neuen Hosts, Geräte können in ihrem Bericht mehrere App-Sammlungen angeben. Beschreibungen. 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 App-Sammlungen aufzählen, die vom Gerät beworben werden, untersuchen der Sensorbeschreibung ermitteln die Protokollversionen, die sie jeweils implementieren, und wählen dann neueste Protokollversion, die vom Host unterstützt wird. Bei der Auswahl funktioniert der Organisator mit dem einzelnen Protokoll, das für die Lebensdauer des Geräts ausgewählt wurde
Anhang: Beispiel für einen HID-Beschreibungsblock
Das folgende Beispiel zeigt einen typischen gültigen HID-Deskriptor. Dabei werden die häufig verwendeten C-Makros, die in Verwendung des HID-Sensors (Abschnitt 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,
};
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,
};