Målgruppe: Indlejrede firmwareingeniører, OS-driverintegratorer og OEM-produktchefer, der har brug for at forstå, hvad hver byte i læsepenneens HID-rapportdeskriptor reelt betyder, hvorfor brugssiden er opdelt i en generisk desktop-side plus en vendordefineret side, og hvordan man udvider kortet uden at ødelægge de 230+ distributørkvalitetsdrivere, der allerede er i felten.
HID-rapportdeskriptoren er de mest afgørende 80 til 200 bytes firmware i en læsepen. Den er kontrakten mellem pennens MCU og hvert værtsoperativsystem, den nogensinde vil tale med: Windows 10/11, macOS 12/13/14, ChromeOS, Android 9 til 14, iPadOS 15 til 17 og Linux-kernen 5.15+. Får man deskriptoren forkert, skriver pennen forkerte tegn, ignorerer OID-trykket eller – værre – omdirigerer lyden lydløst til den forkerte sink. På tværs af vores 47.000+ leverede enheder har deskriptor-regressioner kostet flere debugging-timer end ethvert andet firmware-undersystem undtagen OCR-motoren.
Denne artikel gennemgår den kanoniske deskriptor fra TC-057 byte for byte, forklarer hvorfor hvert felt er sat som det er, og viser hvor en ODM-mærke sikkert kan udvide kortet, og hvor de ikke kan.
1. De tre lag i HID-kontrakten
En HID-rapportdeskriptor parses i tre logiske lag:
| Lag | Granularitet | Formål | Længde |
|---|---|---|---|
| Element | Kort (1 byte) eller Lang (3 byte) | Tag + størrelse + data | Variabel |
| Lokal | Defineret pr. element | Usage, Usage Minimum/Maximum, Designator | Variabel |
| Hoved | Ombryder en række input/output/feature | Collection, End Collection, Input, Output, Feature | Variabel |
Parseren er tilstandsbevidst. En Collection åbner et scope, og Input/Output/Feature-elementerne inden for dette scope beskriver en rapport. Deskriptoren behandles fra venstre mod højre, og hver byte skal være velformet. En enkelt misdannet byte får værten til at afvise hele deskriptoren.
Vores Q6/Q8-deskriptor er 92 bytes. F8-deskriptoren er 76 bytes. T2/Y6 (med fuld consumer-control-understøttelse til hovedtelefonafspilning) er 168 bytes. Deskriptorlængden er indkodet i HID-deskriptoren ved enumerationstidspunktet, og værten cacher den. Hvis du ændrer deskriptoren i en fremtidig firmware-revision, kan værten fortsætte med at bruge den cachelagrede version, hvilket er grunden til, at vi altid tvinger en USB-busnulstilling ved firmwareopdatering (se TC-075).
2. Element-tags som læsepennen bruger
HID-specifikationen definerer en tag-tabel; pennen bruger kun 12 af dem:
| Tag | Mnemoteknik | Rolle | Brugt i Q6/Q8 |
|---|---|---|---|
| 0x05 | Usage Page | Skifter side | Ja (3 gange) |
| 0x09 | Usage | Vælger én usage | Ja (4 gange) |
| 0x15 | Logical Minimum | Afgrænser et input/output | Ja (3 gange) |
| 0x25 | Logical Maximum | Afgrænser et input/output | Ja (3 gange) |
| 0x75 | Report Size | Bits pr. element | Ja (3 gange) |
| 0x95 | Report Count | Antal elementer | Ja (3 gange) |
| 0xA1 | Collection | Åbner scope | Ja (2 gange) |
| 0x81 | Input | Definerer et input-rapportfelt | Ja (3 gange) |
| 0x91 | Output | Definerer et output-rapportfelt | Ja (1 gang) |
| 0x19 | Usage Minimum | Åbn rækkevidde | Ja (1 gang) |
| 0x29 | Usage Maximum | Luk rækkevidde | Ja (1 gang) |
| 0xC0 | Afslut samling | Lukker scope | Ja (2 gange) |
Vi holder os bevidst inden for dette undersæt af 12 tags. HID-specifikationen tillader mange flere, men hvert ekstra tag øger risikoen for en OS-specifik mærkværdighed. For eksempel, den Padding (0x5A)-tagget Linux-kernens hid-core.c i visse Padding-sekvenser under suspend/resume.
3. De tre Usage Pages
Q6/Q8-deskriptoren bruger tre usage pages:
| Usage Page | Hex | Formål |
|---|---|---|
| Generic Desktop | 0x01 | Tastatur, mus, systemkontroller |
| Consumer | 0x0C | Lydstyrke, play/pause, skift af nummer |
| Vendor Defined | 0xFF00 | OID-hændelsespayload (brugerdefineret brandudvidelse) |
Generic Desktop-siden er den tastatursamling, vi leverer. Den erklærer en 1-byte modifier (8 bit, 1 byte rapportstørrelse, 8 rapportantal) og en 1-byte reserveret konstant (8 bit, 1 rapportstørrelse, 1 antal). Tilsammen udgør de boot-tastaturrapportens hoved.
Consumer-siden er den volumen op / volumen ned / play-pause-kontrol, vi leverer til hovedtelefonafspilning. Den er i alt 3 bit.
Vendor Defined-siden 0xFF00 er OID-hændelsespayloaden: 16 bytes pr. rapport, uden Report ID, i en Output-rapport, så værten også kan sende 16 bytes kontekst til pennen efter behov.
4. Bytekort for Q6/Q8-firmware 3.3.1
Nedenfor er den kanoniske deskriptor for Q6/Q8-platformen, byte-indekseret:
| Byte | Hex | Betydning | Hvorfor |
|---|---|---|---|
| 0 | 0x05 | Usage Page-tag (Global) | Fortæller parseren, at vi skifter side |
| 1 | 0x01 | Side 0x01 (Generic Desktop) | Åbner tastatur-scope |
| 2 | 0x09 | Usage-tag (Lokal) | Vælger én usage |
| 3 | 0x06 | Tastatur | Påkrævet til boot-protokol |
| 4 | 0xA1 | Collection-tag (Main) | Åbner Application-collection |
| 5 | 0x01 | Application | Application er standardindpakningen |
| 6 | 0x05 | Usage Page-tag | |
| 7 | 0x07 | Keyboard-side | Modifier-taster hører til her |
| 8 | 0x19 | Usage Minimum | |
| 9 | 0xE0 | Venstre Control = 224 | Modifier-område 224–231 |
| 10 | 0x29 | Usage Maximum | |
| 11 | 0xE7 | Højre GUI = 231 | Afslutter modifier-området |
| 12 | 0x15 | Logical Minimum | |
| 13 | 0x00 | 0 | Boolesk false |
| 14 | 0x25 | Logical Maximum | |
| 15 | 0x01 | 1 | Boolesk true |
| 16 | 0x75 | Report Size | |
| 17 | 0x01 | 1 bit pr. element | Modifier er et bitfield |
| 18 | 0x95 | Report Count | |
| 19 | 0x08 | 8 elementer | Én bit pr. modifier |
| 20 | 0x81 | Input-tag (Main) | |
| 21 | 0x02 | Data, Variabel, Absolut | Standard boot-tastatur-modifikator |
| 22 | 0x95 | Rapportantal | |
| 23 | 0x01 | 1 element | Reserveret byte |
| 24 | 0x75 | Rapportstørrelse | |
| 25 | 0x08 | 8 bit | Én byte reserveret |
| 26 | 0x81 | Input-tag | |
| 27 | 0x03 | Konstant, Variabel, Absolut | Styresystemet forventer denne reserverede byte |
| 28 | 0x05 | Usage Page-tag | |
| 29 | 0x0C | Forbrugerside | Til medietaster |
| 30 | 0x09 | Anvendelse | |
| 31 | 0xE9 | Lydstyrke op | |
| 32 | 0x09 | Anvendelse | |
| 33 | 0xEA | Lydstyrke ned | |
| 34 | 0x09 | Anvendelse | |
| 35 | 0xCD | Afspil/Pause | |
| 36 | 0x15 | Logisk minimum | |
| 37 | 0x00 | 0 | |
| 38 | 0x25 | Logisk maksimum | |
| 39 | 0x01 | 1 | |
| 40 | 0x75 | Reportstørrelse | |
| 41 | 0x01 | 1 bit | |
| 42 | 0x95 | Rapportantal | |
| 43 | 0x03 | 3 elementer | 3 mediebits |
| 44 | 0x81 | Input-tag | |
| 45 | 0x02 | Data, Variabel, Absolut | |
| 46 | 0xC0 | End Collection | Lukker tastatur-app |
| 47 | 0x06 | Usage Page-tag (udvidet) | 2-byte usage page |
| 48 | 0xFF, 0x00 | Side 0xFF00 | Leverandørdefineret |
| 49 | 0x09 | Brug | |
| 50 | 0x01 | OID-hændelse | Brugerdefineret brug |
| 51 | 0xA1 | Samling | |
| 52 | 0x01 | Applikation | |
| 53 | 0x15 | Logisk minimum | |
| 54 | 0x00 | 0 | |
| 55 | 0x26 | Logisk maksimum (udvidet) | 2-byte logisk max |
| 56 | 0xFF, 0x00 | 255 | |
| 57 | 0x75 | Report Size | |
| 58 | 0x08 | 8 bits | |
| 59 | 0x95 | Report Count | |
| 60 | 0x10 | 16 elementer | 16-byte OID-nyttelast |
| 61 | 0x09 | Usage | |
| 62 | 0x02 | OID-nyttelast | |
| 63 | 0x91 | Output-tag | |
| 64 | 0x02 | Data, Variable, Absolute | |
| 65 | 0xC0 | End Collection | Lukker vendor-samlingen |
De resterende bytes (66 til 91) på Q6/Q8 er polstring for bytejustering samt en Feature-rapport for brand-ID. På F8 fjernes bytes fra 47 og frem, og deskriptoren stopper ved byte 46.
5. Boot-protokol vs. Report-protokol
Læsepennen eksponerer en Protocol Mode-karakteristik (UUID 0x2A4E på BLE, bInterfaceProtocol-byte på USB). To protokoller er gyldige:
| Protokol | Rapportlayout | Bruges når |
|---|---|---|
| Boot | 8-byte fast (modifikator + reserveret + 6 keycodes) | BIOS, gendannelse, tidlig boot |
| Report | Deskriptordefineret | Normal OS-brug |
På Q6/Q8-firmware 3.3.1 er standarden Report Protocol. Værten kan anmode om Boot Protocol ved at skrive 0x00 til Protocol Mode-karakteristikken, hvilket vores firmware honorerer ved at kollapse den 16-byte OID-rapport til 8-byte boot-formatet. Vi mister ingen data; OID-side-ID'et og koden foldes blot ind i keycode-bytes.
For et skoleprogram på 12.000 enheder anbefalede vi, at Boot Protocol aktiveres som standard, fordi skolens bærbare computere kører et låst Windows-image, hvor den standard HID-driverstak indlæses på 1,4 s mod 4,8 s for den fulde Report Protocol-parser. Latensbesparelsen er meningsfuld, når 30 børn koldstarter systemet ved begyndelsen af timen.
6. Vendor-defineret side 0xFF00: Hvorfor vi reserverede den
Vi reserverede 0xFF00 i USB-IF's vendor-liste tilbage i 2022. Reservationen giver os juridisk autoritet til at definere alle usage-ID'er fra 0x01 til 0xFFFF på den side, og enhver tredjepart, der vilinteropere med vores penne, skal forhandle et usage-ID-område med os. ODM-kunden får et delområde på 32 usages allokeret til deres brand-ID. De første 8 af disse 32 er reserveret til vores interne brug, hvilket efterlader 24 usages til brandet.
Sådan kan den samme Q6/Q8-firmware leveres til 230+ distributører uden at deres app ved et uheld taler med en anden distributørs pen.
7. Layoutet for OID-rapporten
Den 16-byte OID-nyttelastrapport er opbygget som følger:
| Byte | Felt | Kodning | Område |
|---|---|---|---|
| 0–3 | Side-ID | uint32 LE | 0 til 4.294.967.295 |
| 4–7 | OID-kode | uint32 LE | 0 til 4.294.967.295 |
| 8 | Sprog | uint8 | 0–255 (se sprogpakke) |
| 9–10 | Sekvens | uint16 LE | 0–65.535 |
| 11–12 | Opholdstid (ms) | uint16 LE | 0–65.535 |
| 13 | Flag | uint8 bitmaske | bit 0 = gentag, bit 1 = lyd optaget |
| 14–15 | Reserveret | uint16 LE | 0 |
Flag-byten er den eneste, et kundemærke må omdefinere. Vi allokerer bit 2–7 til brandspecifikke flag (f.eks. "testtilstand", "diagnostik", "kvalitetskontrol på fabrikken"). Bit 0–1 er reserveret til vores firmware.
8. Feature-rapporten (Brand ID)
En 4-byte Feature-rapport i slutningen af deskriptoren indeholder et Brand ID. Feature-rapporten er skrivebeskyttet fra værten og indstilles under fabrikkens klargøring. Den tjener to formål:
- Ledsager-appen læser Brand ID for at bestemme, hvilket indholdskatalog der skal indlæses.
- QA-stationen læser Brand ID for at verificere firmware-loadets integritet.
0x09, 0x03, // Usage (Brand ID)
0x15, 0x00, // Logical Minimum 0
0x26, 0xFF, 0x00, // Logical Maximum 255
0x75, 0x08, // Report Size 8
0x95, 0x04, // Report Count 4
0xB1, 0x02, // Feature (Data, Variable, Absolute)
Den første byte er brand-indekset (vi har aktuelt 38 brand-indekser tildelt), den anden er en regionskode (1=EU, 2=US, 3=APAC, 4=MENA, 5=LATAM, 6=SSA), den tredje er en indholdsrevision (0–255), og den fjerde er en CRC over de første tre.
9. Sådan kortlægges deskriptoren til GATT
Når pennen er på BLE, serveres de samme deskriptorbytes af karakteristikken 0x2A4B (Report Descriptor). Selve rapporten sendes på karakteristikken 0x2A4D via notifikationer. Protocol Mode ligger på 0x2A4E. Brand ID Feature-rapporten eksponeres på en vendor-defineret karakteristik 0x9F01 (skrivebeskyttet) på den samme HID-tjeneste.
| USB HID-element | GATT-karakteristik-UUID | Egenskaber |
|---|---|---|
| Rapportdeskriptor | 0x2A4B | Læs |
| Rapport | 0x2A4D | Notificer, Skriv, SkrivUdenSvar |
| Protokoltilstand | 0x2A4E | Læs, Skriv |
| Brand ID | 0x9F01 (custom) | Læs |
| HID-information | 0x2A4A | Læs |
Dette er GATT-tabellen dokumenteret i TC-060.
10. Valideringspakke vi kører på hvert deskriptorbuild
Før en deskriptor sendes af sted, skal den bestå:
| Kontrol | Værktøj | Godkendelseskriterium |
|---|---|---|
| Parse-konformitet | USB-IF HID-deskriptorviser | 0 fejl, 0 advarsler |
| Boot-protokol bootbarhed | USBlyzer | Ændrer boot-rapport med succes |
| OS-enumeration | Windows 10/11 Enhedshåndtering | Vises som HID Keyboard + HID Vendor Device |
| Parring med iPadOS | iPad 9. generation, iPadOS 17 | Parres og læser descriptor |
| ChromeOS-registrering | Lenovo Chromebook 14 | Vises som inputenhed |
| macOS-registrering | MacBook Air M1 | System Information viser alle 3 interfaces |
| Android-registrering | Pixel 7a, Android 14 | InputDeviceManager rapporterer keyboard + vendor |
Hvis bare én af disse fejler, afvises descriptoren, og firmware-buildet tilbageholdes. På tværs af 86 firmware-udgivelser siden 2023 er 6 blevet afvist ved denne gate og rullet tilbage. Den mest almindelige årsag er et Usage Page-tag med forkert extended-length-kodning, som fejler macOS-kontrollen men består i Windows.
11. Almindelige fejl kunder begår
Når en ODM-kunde kommer med sit eget descriptor-udkast, er tre fejl skyld i ~80 % af afvisningerne:
- Den reserverede byte efter modifieren springes over. macOS forventer præcis 9 bytes boot keyboard-rapport (8 modifier + 1 reserveret + 6 keycode). Kunder komprimerer det ofte til 8 bytes, hvilket bryder boot-protokollen på Mac.
- Brug af
Logical Maximum 0x00FFuden det udvidede tag (0x26). Den korte form0x25 0xFFvirker for værdier op til 127. For værdier op til 255 er det udvidede tag obligatorisk. Kunder glemmer det, og descriptoren afkortes lydløst på Linux. - En Input-rapport erklæres inde i en Output-collection. HID-parseren håndhæver ikke en streng Output-only-collection, men det gør nogle OS-drivere. Vi erklærer altid Output i OID-collectionen for at gøre symmetrien eksplicit.
12. Performancebudget
På host-siden parses descriptoren én gang ved registrering. På enhedssiden rører hver rapport-samling descriptor-mappet. Vores interne benchmark på Q6/Q8 firmware 3.3.1:
| Operation | Cyklusser | Tid ved 64 MHz |
|---|---|---|
| Slå Usage Page op | 8 | 125 ns |
| Slå Usage ID op | 12 | 188 ns |
| Saml 16-byte OID-rapport | 240 | 3,75 µs |
| Send rapport på USB EP1 IN | 320 | 5,0 µs |
| Send rapport på BLE 0x2A4D | 1.840 | 28,75 µs |
| Total pr. tryk (USB) | 580 | 9,1 µs |
| Total pr. tryk (BLE) | 2.100 | 32,8 µs |
9,1 µs USB-tallet er det, der gør den 6,4 ms end-to-end-latens mulig. BLE-tallet er den dominerende komponent i 32 ms BLE-latensen.
13. Det større billede
Hvis du evaluerer en pen, der ikke offentliggør sin HID-rapport-descriptor åbent, er det det første, du bør bede om. En leverandør, der nægter at dele descriptoren, skjuler enten manglende overholdelse eller bygger på en ikke-HID-stack, der kræver driverarbejde på din side. Vores descriptor til Q6/Q8 firmware 3.3.1 er 92 bytes, og vi udgiver den under NDA til kvalificerede ODM-kunder på anmodning.
15. Relateret læsning
- Læsepen USB HID og HID over GATT
- Læsepen Bluetooth-parring SSP Secure Simple Pairing
- Læsepen BLE Service Characteristic UUID-design
- Læsepen GATT-server og notifikationsmekanisme
- Læsepen USB Type-C opladning PD QC og BC1.2
- Læsepen Bluetooth 5.0 vs 5.2 vs 5.3
- Læsepen OTA-firmwareopdateringsmekanisme
- Læsepen ordbogsfilformat og -udgave