Protokół HID urządzenia śledzącego głowy

Protokół urządzenia HID (Human Interface Device) śledzącego ruch głowy, dostępny na urządzeniach z Androidem 13 lub nowszym, umożliwia podłączenie urządzenia śledzącego ruch głowy do urządzenia z Androidem przez USB lub Bluetooth i ujawnienie go w ramach frameworku Androida i aplikacji za pomocą frameworku sensorów. Protokół ten jest używany do sterowanie efekt wirtualizacji dźwięku (dźwięk 3D). Na tej stronie używamy terminów urządzeniehost w rozumieniu Bluetooth, gdzie urządzenie oznacza urządzenie do śledzenia ruchów głowy, a host to host Androida.

Producenci urządzeń muszą skonfigurować swoje urządzenia z Androidem, aby umożliwić obsługę protokołu HID lokalizatora głowy. Więcej informacji o konfiguracji znajdziesz w pliku README Dynamic Sensors (w języku angielskim).

Ta strona zakłada, że znasz te zasoby:

Struktura najwyższego poziomu

Platforma Android rozpoznaje urządzenie śledzące ruch głowy jako urządzenie HID.

Pełny przykład prawidłowego deskryptora HID znajdziesz w załączniku 1: Przykład deskryptora HID.

Na najwyższym poziomie urządzenie śledzące ruch głowy jest kolekcją aplikacji z poziomami Sensors (0x20) i Other: Custom (0xE1). W tej kolekcji znajduje się kilka pól danych (wejść) i właściwości (funkcji).

Właściwości i pola danych

W tej sekcji opisano właściwości i pola danych w zbiorze aplikacji urządzenia śledzącego ruch głowy.

Właściwość: Opis czujnika (0x0308)

Właściwość Opis czujnika (0x0308) to tylko do odczytu ciąg znaków ASCII (8-bitowy), który musi zawierać te wartości:

Śledzik w wersji 1.0:

#AndroidHeadTracker#1.0

śledzenie ruchów głowy w wersji 2.0 (dostępne w Androidzie 15 lub nowszym), które obejmuje obsługę dźwięku LE:

#AndroidHeadTracker#2.0#x

Wartość x jest liczbą całkowitą (1, 2, 3) wskazującą transport wspierający:

  • 1: Lista kontroli dostępu (ACL)
  • 2: ISO
  • 3: ACL + ISO

Nie jest wymagany żaden terminator null, co oznacza, że łączny rozmiar tej właściwości w wersji 1.0 wynosi 23 8-bitowe znaki.

Ta właściwość służy do rozróżniania czujników, aby uniknąć kolizji z innymi czujnikami niestandardowymi.

Właściwość: trwały identyfikator unikalny (0x0302)

Właściwość trwały identyfikator unikalny (0x0302) to tablica tylko do odczytu zawierająca 16 elementów, z których każdy ma 8 bitów (razem 128 bitów). Nie oczekuje się zakończenia o wartości null. Ta właściwość jest opcjonalna.

Ta właściwość umożliwia urządzeniom śledzącym ruch głowy, które są zintegrowane z urządzeniami audio, odwoływanie się do urządzenia audio, do którego są podłączone. Obsługiwane są poniższe schematy.

Samodzielny lokalizator ruchów głowy

Jeśli właściwość Persistent Unique ID (0x0302) nie istnieje lub została ustawiona na wszystkie oznacza to, że tracker nie jest na stałe podłączony do urządzenia audio i można jej używać oddzielnie, np. umożliwiając ręcznie powiązać go z osobnym urządzeniem audio.

Odwoływanie się do adresu MAC Bluetooth

Oktet 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Wartość 0 0 0 0 0 0 0 0 B T Adres MAC Bluetooth

W tym schemacie pierwsze 8 oktetów musi zawierać ciąg 0, oktety 8 i 9 muszą zawierać wartości ASCII odpowiednio B i T, a następujących 6 oktetów jest interpretowane jako adres MAC Bluetooth przy założeniu, że urządzenie śledzące dotyczy wszystkich urządzeń audio z tym adresem MAC. Ten adres musi być adresu tożsamości, nawet jeśli urządzenie używa losowego adresu MAC do ustalania połączeń. Urządzenia z 2 trybami łączące się przez klasyczny Bluetooth (format HID v1.0) i Bluetooth LE (format HID v2.0) muszą udostępniać dwa HID deskryptory z tym samym adresem tożsamości. Urządzenia z 2 trybami oddzielone urządzenia lewe i prawe muszą udostępniać Bluetooth LE HID za pomocą podstawowego urządzenia w trybie online, a nie dodatkowego urządzenia obsługującego tylko LE.

Odwołanie z użyciem identyfikatora UUID

Gdy ustawiony jest najbardziej znaczący bit (MSB) oktetu 8 (≥0x80), pole jest interpretowany jako identyfikator UUID, jak określono w RFC-4122. Odpowiadające urządzenie audio udostępnia ten sam identyfikator UUID, który jest zarejestrowany w ramach Androida za pomocą nieokreślonego mechanizmu odpowiedniego do typu używanego transportu.

Usługa: stan raportowania (0x0316)

Właściwość Stan raportowania (0x0316) to właściwość tylko do odczytu lub do odczytu i zapisu, która ma standardową semantykę zgodnie ze specyfikacją HID. Używany przez gospodarza aby wskazać urządzeniu, które zdarzenia mają być zgłaszane. Używane są tylko wartości Brak zdarzeń (0x0840) i Wszystkie zdarzenia (0x0841).

Wartość początkowa tego pola musi wynosić Brak zdarzeń i nie może być modyfikowana przez urządzenie, tylko przez hosta.

Właściwość: stan zasilania (0x0319)

Właściwość Stan zasilania (0x0319) to właściwość odczytu/zapisu o standardowej semantyce zgodnie ze specyfikacją HID. Używany przez gospodarza jest właściwy, by wskazać urządzeniu, w jakim musi być jego stanie zasilania. Tylko używane są wartości Pełne zasilanie (0x0851) i Wyłącz (0x0855).

Początkowa wartość tego pola jest określana przez urządzenie i nigdy nie może być modyfikowana przez urządzenie, tylko przez hosta.

Usługa: interwał raportów (0x030E)

Właściwość Interwał raportów (0x030E) to właściwość do odczytu/zapisu, w której parametr standardowej semantyki zdefiniowanej w specyfikacji HID. Używany przez gospodarza właściwość wskazującą urządzenie, jak często ma ono raportować odczyty danych. Jednostką są sekundy. Prawidłowy zakres tej wartości jest określany przez urządzenie i opisane z użyciem mechanizmu minimalne/maksymalnej wartości fizycznej. Musi być obsługiwana częstotliwość raportowania co najmniej 50 Hz, a zalecana maksymalna częstotliwość raportowania to 100 Hz. Dlatego minimalny interwał raportowania musi być krótszy lub równy 20 ms, a zalecany interwał powinien być dłuższy lub równy 10 ms.

Usługa: zarezerwowane przez dostawcę LE Transport (0xF410)

Właściwość LE Transport (0xF410) zarezerwowana przez dostawcę to właściwość do odczytu/zapisu. o standardowej semantyce zdefiniowanej w specyfikacji HID. Gospodarz używa tej właściwości do wskazania wybranego transportu (ACL lub ISO). Tylko używane są wartości ACL (0xF800) i ISO (0xF801), które trzeba uwzględnić w zbiorze logicznym.

Ta usługa jest skonfigurowana przed stanem zasilania lub raportowania.

Pole danych: niestandardowa wartość 1 (0x0544)

Pole Wartość niestandardowa 1 (0x0544) to pole danych służące do raportowania rzeczywistych informacji o śledzeniu ruchem głowy. Jest to 3-elementowa tablica interpretowana jako do normalnych reguł HID dla wartości fizycznych, zgodnie z sekcją 6.2.2.7 ze specyfikacją HID. Prawidłowy zakres dla każdego elementu to [-π, π] rad. Jednostki zawsze są w radianach.

Elementy są interpretowane w taki sposób: [rx, ry, rz], więc [rx, ry, rz] to wektor obrotu, reprezentujący przekształcenie z ramki referencyjnej w ramkę nagłówka. Wielkość musi mieścić się w zakresie [0..π].

Ćwiczenie jest dowolne, ale jest zazwyczaj ustalone i musi być prawostronne. Dopuszczalne jest niewielkie odchylenie. Osie głowy:

  • X od lewego ucha do prawego
  • Y od tyłu głowy do nosa (z tyłu do przodu)
  • Z od szyi do góry głowy

Pole danych: wartość niestandardowa 2 (0x0545)

Pole Wartość niestandardowa 2 (0x0545) to pole do wprowadzania danych służące do raportowania rzeczywistych informacji o śledzeniu ruchem głowy. Jest to 3-elementowy tablica o stałym punkcie, interpretowana zgodnie ze zwykłymi regułami HID dla wartości fizycznych. Jednostkami są zawsze radiany na sekundę.

Elementy są interpretowane w taki sposób: [vx, vy, vz], więc [vx, vy, vz] to wektor obrotu, reprezentujący prędkość kątową ramy głowy (względem siebie).

Pole danych: niestandardowa wartość 3 (0x0546)

Pole Wartość niestandardowa 3 (0x0546) to pole wejściowe służące do śledzenia przerw w ramce odniesienia. Jest to liczba całkowita skalarna o długości 8 bitów. Musi zostać zwiększona (z zawijaniem) za każdym razem, gdy obszar odniesienia ulegnie zmianie, np. gdy algorytm filtra orientacji zostanie zmieniony. służy do określenia, że stan orientacji został zresetowany. Ta wartość jest interpretowana zgodnie ze zwykłymi regułami HID dotyczącymi wartości fizycznych. Wartość fizyczna i jednostki nie mają jednak znaczenia. Jedynymi informacjami istotnymi dla hosta jest zmieniona wartość. Aby uniknąć problemów liczbowych związanych z utratą dokładności przy konwertowaniu jednostek logicznych na fizyczne zalecamy ustawienie dla fizycznego minimum, fizycznego maksimum i wykładnika jednostki do zera dla tego pola.

Struktura raportu

Grupowanie usług w raporty (przez przypisanie identyfikatorów raportów) to i elastyczności. Aby zwiększyć wydajność, najlepiej oddzielić właściwości tylko do odczytu z właściwości do odczytu/zapisu.

W przypadku pól danych pola Wartość własna 1, 2 i 3 muszą znajdować się w tym samym raporcie i w jednym raporcie dotyczącym danego urządzenia (zbioru aplikacji).

Wysyłanie raportów o błędach

Urządzenie musi okresowo i asymetrycznie (za pomocą komunikatów HID INPUT) przesyłać raporty o wprowadzaniu danych, gdy są spełnione wszystkie te warunki:

  • Właściwość stanu zasilania ma wartość Pełne zasilanie.
  • Właściwość Stan raportowania ma wartość Wszystkie zdarzenia.
  • Właściwość Interwał raportowania ma wartość różną od zera.

Właściwość Interval Reporting określa częstotliwość wysyłania raportów. Kiedy żaden z powyższych warunków nie jest spełniony, urządzenie nie może wysyłać żadnych raportów.

Zgodność do przodu i do tyłu

Protokół HID śledzącego ruch głowy używa schematu wersji, który umożliwia aktualizacje, a także interoperacyjność między hostem a urządzeniem, które używają różnych wersji protokołu. Zidentyfikowano wersje protokołu dzielone na dwie liczby, większe i mniejsze. Mają one odmienne semantyki: opisane w dalszej części tego artykułu.

Wersje obsługiwane przez urządzenie można określić, analizując jego właściwość Opis czujnika (0x0308).

Zgodność wersji podrzędnych

Zmiany w wersji podrzędnej są zgodne z wcześniejszymi wersjami podrzędnymi, które są oparte na tej samej wersji nadrzędnej. W aktualizacjach do wersji podrzędnej host ignoruje dodatkowe pola danych i właściwości. Na przykład urządzenie korzystające z wersji 1.6 protokołu jest zgodne z hostem obsługującym wersję 1.x protokołu, w tym wersję 1.5.

Zgodność z wersją główną

Zmiany niekompatybilne wstecznie są dozwolone w przypadku zmian w wersjach głównych. Aby obsługiwać wiele głównych wersji w celu zapewnienia interoperacyjności ze starymi i nowymi hostami, urządzenia mogą określać w swoich deskryptorach raportów wiele kolekcji aplikacji. Na przykład:

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

W tym przypadku host może wyliczyć wszystkie różne kolekcje aplikacji które są reklamowane przez urządzenie, sprawdzając właściwość „Opis czujnika”, aby określić wersje protokołów wdrażane przez każdy z nich, a następnie wybrać Najnowsza wersja protokołu obsługiwana przez hosta. Po jej wybraniu host będzie działać korzystając z pojedynczego protokołu wybranego przez cały okres użytkowania urządzenia połączenia.

Załącznik: przykład opisu HID

Przykład poniżej pokazuje typowy prawidłowy deskryptor HID. Używa ona powszechnie stosowanych makr C, które są opisane w Zastosowaniach czujników HID (sekcja 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,
};

Załącznik 2. Przykład deskryptora HID w wersji 2.0

Ten przykład pokazuje deskryptor HID w wersji 2.0 dla urządzenia obsługującego tylko transport 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,
};