Générateur de descripteur USB HID : composite, retour de force, structures C

Illustration : Générateur de descripteur USB HID

Écrit par

dans

Écrire un descripteur de rapport HID à la main, c’est aligner des octets dont le sens dépend de tout ce qui précède. Cet outil part de ce que contient le périphérique (boutons, axes, pédales, clavier, retour de force, canal de configuration), génère le descripteur, le découpe en interfaces pour un périphérique composite, et fournit les structures C des rapports, avec les erreurs classiques signalées au fil de la saisie.

TéléchargerDans ce navigateur

Ouvrir accepte un projet .json, un fichier C ou un en-tête contenant un ou plusieurs tableaux d’octets (un par interface), ou un descripteur binaire.

Plusieurs fonctions dans la même interface : un seul descripteur, Report ID attribués automatiquement. Interfaces différentes : un descripteur et un endpoint par interface (périphérique composite).

Rapports et contrôles de cohérence

Composer plutôt qu’aligner des octets

Le mode Composer décrit le périphérique par ce qu’il contient : des fonctions (joystick, manette, clavier, souris, touches multimédia, canal vendeur), et dans chacune des commandes (boutons, axes, chapeaux, pédales, LED, tampons de données, retour de force). Le descripteur est généré à chaque modification : l’outil n’émet un élément Global que lorsque sa valeur change, ajoute le remplissage nécessaire pour qu’un champ de 8 bits ou plus commence sur un octet, et choisit les plages logiques à partir de la résolution demandée.

Le mode Éditeur d’éléments reste disponible pour retoucher le résultat ligne par ligne, charger un modèle ou décoder un descripteur existant. Une modification à ce niveau est signalée dans le composeur, qui l’écraserait en régénérant.

Enregistrer, recharger, exporter

Un projet s’enregistre en fichier .json : il contient la composition, la vitesse USB et les descripteurs de chaque interface, y compris les retouches faites dans l’éditeur d’éléments. Il peut aussi être gardé dans le navigateur, sous un nom, pour y revenir sans fichier. « Ouvrir un fichier » relit un projet, mais aussi un fichier C ou un en-tête existant (un tableau d’octets par interface, commentaires ignorés) ou un descripteur binaire, extrait par exemple avec usbhid-dump ou depuis /sys/bus/hid/devices/*/report_descriptor sous Linux. Les boutons de téléchargement produisent directement le .c du descripteur, le .h des structures et le binaire brut.

Périphériques composites

Deux façons de réunir plusieurs fonctions dans un même périphérique USB :

  • Une seule interface, plusieurs collections Application : un seul descripteur, un seul endpoint, chaque fonction préfixée par son Report ID. C’est le cas du volant à retour de force avec un canal de configuration vendeur. Les Report ID sont attribués automatiquement en évitant ceux du bloc PID ; on peut les fixer à la main, l’outil vérifie alors les collisions.
  • Plusieurs interfaces : un descripteur, un descripteur HID de classe et un endpoint par interface. C’est indispensable pour garder le protocole boot du clavier ou de la souris (BIOS, UEFI), qui ne connaît ni les Report ID ni les collections multiples. Le numéro d’interface se règle sur chaque fonction ; l’outil indique pour chacune bInterfaceSubClass, bInterfaceProtocol et la taille minimale de wMaxPacketSize.

Les structures C

L’onglet Structures C produit un en-tête prêt à inclure dans le firmware : une structure packed par rapport et par type (Input, Output, Feature), l’octet de Report ID et sa constante, des membres typés (int16_t, uint8_t[6]…) quand le champ est aligné sur l’octet, des champs de bits sinon, et un _Static_assert qui vérifie à la compilation que la taille correspond au descripteur. Les boutons sont regroupés en un entier ou un tableau d’octets (bit n = bouton n+1), les champs Array (touches du clavier, type d’effet PID) deviennent des index, les membres des collections logiques du PID sont préfixés (direction_1, axes_enable_x).

La disposition des champs de bits suit GCC et Clang en little-endian (Cortex-M, AVR, RP2040, x86), bit de poids faible en premier, comme HID. MSVC ne la garantit pas : côté PC sous Visual Studio, mieux vaut lire ces champs par masque et décalage.

Comment se lit un descripteur HID

Un descripteur de rapport HID n’est pas une structure figée : c’est un petit programme que l’hôte exécute ligne à ligne pour en déduire le format des rapports. Chaque élément commence par un octet de préfixe : les bits 7–4 donnent l’étiquette, les bits 3–2 le type (Main, Global, Local) et les bits 1–0 la taille des données qui suivent (0, 1, 2 ou 4 octets, la valeur 3 signifiant 4).

TypePortéeExemples
GlobalReste en vigueur jusqu’à redéfinition, y compris d’une collection à l’autreUsage Page, Logical Min/Max, Report Size, Report Count, Report ID, Unit
LocalConsommé par l’élément Main suivant, puis effacéUsage, Usage Minimum/Maximum
MainCrée un champ ou ouvre/ferme une collectionInput, Output, Feature, Collection, End Collection

Toute la difficulté vient de cette mémoire implicite : un Report Size oublié après un chapeau de 4 bits, et tous les axes qui suivent passent en 4 bits. Autre piège classique : Logical Minimum/Maximum, Physical Minimum/Maximum et Unit Exponent sont signés, et 0x25 0xFF vaut −1, pas 255 (il faut 0x26 0xFF 0x00). L’outil choisit toujours la plus petite taille qui conserve le signe, et l’import signale les descripteurs existants qui tombent dans le panneau.

Retour de force : la page PID (0x0F)

Les volants et joysticks à retour de force s’appuient sur la page Physical Interface Device, définie par l’USB-IF dans un document séparé. Elle décrit les effets (constant, rampe, périodiques, ressort, amortisseur, inertie, friction), leurs enveloppes et conditions, l’allocation des effets (Create New Effect, Block Load, Pool) et le pilotage global (Device Control, Device Gain). Windows n’expose le retour de force que si cette arborescence est complète et placée dans la collection du joystick.

Le bloc PID généré provient du descripteur du projet OpenFFBoard (Yannick Richter, licence MIT), éprouvé sur de nombreux volants direct drive. Le composeur l’adapte : 1 ou 2 axes motorisés (Axes Enable, Direction et Type Specific Block Offset), nombre d’effets en mémoire, et rapport Device Control au format de la norme (un index de 1 à 6) ou au format d’OpenFFBoard (6 bits indépendants). Il vérifie que les axes motorisés existent bien dans les axes déclarés de la fonction : le retour de force porte sur X (et Y), pas sur l’usage Steering.

Ce que l’outil vérifie

  • Report ID : plage 1…255, doublons entre fonctions, collisions avec les rapports PID de même type ;
  • taille de chaque rapport face au paquet interrupt de la vitesse choisie (8, 64 ou 1 024 octets, ID compris) : au-delà, le rapport passe en plusieurs paquets, ce qui est permis mais ralentit sa transmission ;
  • numérotation continue des interfaces, perte du protocole boot quand un clavier ou une souris partage une interface ;
  • limites de DirectInput (8 axes, 128 boutons, 4 chapeaux) et affichage de joy.cpl limité à 32 boutons ;
  • axes relatifs non signés, plages de 32 bits non signées impossibles, usages déclarés deux fois ;
  • au niveau des éléments : collections non fermées, champs avant le premier Report ID, rapports qui ne tombent pas sur un octet, Logical Maximum inférieur au Minimum, plage logique trop large pour le Report Size, champs sans usage.

Il ne remplace pas un essai sur l’hôte : sous Windows, joy.cpl et le Gestionnaire de périphériques (un « Code 10 » vient souvent d’une longueur wDescriptorLength fausse), sous Linux usbhid-dump et hid-decode (paquet hid-tools) montrent ce que le noyau a compris.

Références

Spécifications USB-IF (portail : usb.org/hid)

Outils

  • Waratah (Microsoft hidtools) : compilateur de descripteurs HID à partir d’une description texte, référencé par l’USB-IF ;
  • hid-tools : hid-decode pour relire un descripteur, hid-recorder et hid-replay pour enregistrer un périphérique réel ou en simuler un sous Linux ;
  • TinyUSB : pile USB embarquée (RP2040, STM32, nRF, ESP32-S2/S3…) pour essayer un descripteur sur du vrai matériel ;
  • OpenFFBoard : firmware de volant à retour de force dont provient le bloc PID (licence MIT).

Commentaires

Laisser un commentaire