web-dev-qa-db-fra.com

Descripteur de rapport HID de périphérique HID personnalisé

J'ai un problème avec la génération du descripteur HID . Je souhaite utiliser des rapports simples avec ID1 pour entrée et ID2 pour sortie avec 64 octets de données.

J'ai réalisé que malgré rtfming et googling, je n'ai toujours aucune idée de certains champs dans le descripteur HID.

Quelqu'un peut-il me donner s'il vous plaît un indice ou un manuel où je peux trouver la signification de tous les champs de descripteur? Tout ce que j'ai pu trouver, ce sont des exemples pour HID-mouse/joistick/keyboard.

Par exemple - REPORT_SIZE - sa taille est-elle exprimée en octets ou en bits? Et pourquoi il y a aussi REPORT_COUNT? Si le rapport contient 64 octets, LOGICAL_MAXIMUM doit être 255 ou 255 * 64?

Devrais-je écrire LOGICAL_MAX et MIN pour chaque rapport ou pas?

Ou peut-être que celui-ci (généré plutôt en devinant) suffira?

char ReportDescriptor[39] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x00,                    // USAGE (Undefined)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x01,                    //   REPORT_ID (1)
    0x09, 0x00,                    //   USAGE (Undefined)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x40,                    //   REPORT_SIZE (64)
    0x96, 0x00, 0x02,              //   REPORT_COUNT (512)
    0x81, 0x82,                    //   INPUT (Data,Var,Abs,Vol)
    0x85, 0x02,                    //   REPORT_ID (2)
    0x09, 0x00,                    //   USAGE (Undefined)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x40,                    //   REPORT_SIZE (64)
    0x96, 0x00, 0x02,              //   REPORT_COUNT (512)
    0x91, 0x82,                    //   OUTPUT (Data,Var,Abs,Vol)
    0xc0                           // END_COLLECTION
};
11
Amomum
  1. Toute la documentation officielle est disponible sur usb.org. Pour comprendre les descripteurs de rapport HID, vous devez lire certains des documents situés sur la page Informations HID . En particulier, vous devriez essayer de comprendre:

    • Le document "Device Class Definition for HID 1.11", qui décrit le format du rapport de périphérique d'interface utilisateur
    • Le document "Tables d'utilisation HID 1.12", qui décrit les valeurs de nombreuses pages d'utilisation et utilisations de ces pages pouvant apparaître dans un descripteur de rapport

    Cela dit, la documentation est notoirement obtuse et nécessitera des efforts considérables pour être digérée.

  2. REPORT_SIZE est la taille d'un rapport en bits et non en octets. Reportez-vous à REPORT_SIZE en tant que largeur d'un champ (en bits) et à REPORT_COUNT en tant que nombre de champs (de cette largeur). Cela apparaît clairement dans le document "Définition de la classe d'unités pour HID 1.11", au paragraphe 6.2.2.7 "Éléments globaux", comme suit:

    Global Item Tag     One-byte Prefix    Description
    Report Size         0111 01 nn         Unsigned integer specifying the size of the report
                                           fields in bits. This allows the parser to build an
                                           item map for the report handler to use. For more
                                           information, see Section 8: Report Protocol.
    
  3. À titre indicatif, un descripteur de rapport raisonnable (c’est-à-dire que je ne l’ai pas testé) décrit un tampon d’entrée de 64 octets (pour l’hôte avec un REPORT_ID égal à 0x01) et un tampon de sortie de 64 octets (de l’hôte avec un REPORT_ID de 0x02) pourrait être comme suit:

      0x06, 0x00, 0xFF,            // (GLOBAL) USAGE_PAGE         0xFF00 Vendor-defined 
      0xA1, 0x01,                  // (MAIN)   COLLECTION         0x01 Application (Usage=0x0: Page=, Usage=, Type=) <-- Warning: USAGE type should be CA (Application)
      0x15, 0x00,                  //   (GLOBAL) LOGICAL_MINIMUM    0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0
      0x26, 0xFF, 0x00,            //   (GLOBAL) LOGICAL_MAXIMUM    0x00FF (255) 
      0x75, 0x08,                  //   (GLOBAL) REPORT_SIZE        0x08 (8) Number of bits per field 
      0x85, 0x01,                  //   (GLOBAL) REPORT_ID          0x01 (1) 
      0x95, 0x40,                  //   (GLOBAL) REPORT_COUNT       0x40 (64) Number of fields 
      0x09, 0x01,                  //   (LOCAL)  USAGE              0xFF000001  
      0x81, 0x02,                  //   (MAIN)   INPUT              0x00000002 (64 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
      0x85, 0x02,                  //   (GLOBAL) REPORT_ID          0x02 (2) 
      0x09, 0x01,                  //   (LOCAL)  USAGE              0xFF000001  
      0x91, 0x02,                  //   (MAIN)   OUTPUT             0x00000002 (64 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
      0xC0,                        // (MAIN)   END_COLLECTION     Application
    

    Ce qui correspond aux définitions de structure de langage C suivantes:

    //--------------------------------------------------------------------------------
    // Vendor-defined inputReport 01 (Device --> Host)
    //--------------------------------------------------------------------------------
    
    typedef struct
    {
      uint8_t  reportId;                                 // Report ID = 0x01 (1)
      uint8_t  VEN_VendorDefined0001[64];                // FF00 0001  Value = 0 to 255
    } inputReport01_t;
    
    //--------------------------------------------------------------------------------
    // Vendor-defined outputReport 02 (Device <-- Host)
    //--------------------------------------------------------------------------------
    
    typedef struct
    {
      uint8_t  reportId;                                 // Report ID = 0x02 (2)
      uint8_t  VEN_VendorDefined0001[64];                // FF00 0001  Value = 0 to 255
    } outputReport02_t;
    
  4. Devez-vous spécifier LOGICAL_MINIMUM et LOGICAL_MAXIMUM pour chaque rapport? Non. 

    Certains éléments sont GLOBAL (ce qui signifie que, le descripteur de rapport étant analysé séquentiellement, leurs valeurs demeurent jusqu'à ce qu'ils soient explicitement modifiés par un autre élément GLOBAL) et d'autres sont LOCAL (ce qui signifie que leurs valeurs sont réinitialisées à leurs valeurs par défaut lorsqu'un élément PRINCIPAL est rencontré. ) LOGICAL_MINIMUM et LOGICAL_MAXIMUM sont des éléments GLOBAUX. Vous devez donc spécifier à nouveau leurs valeurs si vous souhaitez que la valeur change. À mon avis, la spécification aurait été plus claire si les noms officiels des éléments avaient été préfixés par GLOBAL_, LOCAL_ et MAIN_, mais malheureusement, nous devons tous vivre avec la spécification telle quelle.

  5. L'exemple ci-dessus a été décodé à l'aide d'un outil gratuit sur SourceForge appelé hidrdd

16
aja

Comme @aja l'a indiqué ci-dessus, la documentation USB officielle est plutôt obtuse. J'ai créé ce modèle (principalement avec l'aide de cette page) comme simple point de départ pour communiquer avec un forum personnalisé. Le code HID est destiné à remplacer le protocole du port COM virtuel. Le gros avantage de HID est qu'aucun pilote n'est requis.

uint8_t CUSTOM_HID_ReportDesc[REPORT_DESC_SIZE] =
{
   0x06, 0x00, 0xFF,    // Global  Usage page = 0xFF00 (Vendor-defined pages are in the range 0xFF00 through 0xFFFF)
   0x09, 0x01,          // Local   Usage (vendor usage 1)
   0xA1, 0x01,          // Main    Collection (application) begin
   0x15, 0x00,          // Global  Logical minimum (0) applies to each byte
   0x26, 0xFF, 0x00,    // Global  Logical maximum (255)
   0x75, 0x08,          // Global  Report Size (8 bits)

   // 14 bytes | Output message 1 (sent from Host to device)
   0x85,  1,            // Global  Report ID (cannot be 0)
   0x98, 64,            // Global  Report Count (number of Report Size fields, in this case 64 bytes)
   0x19, 0x01,          // Local   Usage Minimum (each Report Count must be associated with a Usage)
   0x19, 0x40,          // Local   Usage Maximum
   0x91, 0x02,          // Main    Output (data, array, absolute)

   // 24 bytes | Input message 1 (sent from device to Host)
   0x85,  1,            // Global  Report ID (cannot be 0)
   0x98, 64,            // Global  Report Count (number of Report Size fields)
   0x19, 0x01,          // Local   Usage Minimum (each Report Count must be associated with a Usage)
   0x19, 0x40,          // Local   Usage Maximum
   0x81, 0x02,          // Main    Input (data, array, absolute)

   // 34 bytes | Output message 2 (sent from Host to device)
   0x85,  2,            // Global  Report ID (cannot be 0)
   0x98, 12,            // Global  Report Count (number of Report Size fields)
   0x19, 0x01,          // Local   Usage Minimum (each Report Count must be associated with a Usage)
   0x19, 0x40,          // Local   Usage Maximum
   0x91, 0x02,          // Main    Output (data, array, absolute)

   // 44 bytes | Input message 2 (sent from device to Host)
   0x85,  2,            // Global  Report ID (cannot be 0)
   0x98, 57,            // Global  Report Count (number of Report Size fields)
   0x19, 0x01,          // Local   Usage Minimum (each Report Count must be associated with a Usage)
   0x19, 0x40,          // Local   Usage Maximum
   0x81, 0x02,          // Main    Input (data, array, absolute)

   // 54 bytes | End (add one byte)
   0xC0                 // Main    Collection (application) end
}

Quelques points à noter:

  • Il est facile d’ajouter davantage de paires d’entrées/sorties: il suffit de leur donner un autre ID de rapport. Chaque définition de message se compose de 10 octets, il est donc simple de s’additionner.
  • Nous gardons une trace du nombre d'octets dans le descripteur afin que la taille du tableau puisse être calculée (#define REPORT_DESC_SIZE (55)).

Du côté de Windows, j'utilise Mike O'Brien's HIDLibrary . Les rapports HID sont généralement précédés de l'ID du rapport - dans HIDLibrary, utilisez le champ HidReport.ReportID pour définir/obtenir la valeur. Du côté du tableau, rappelez-vous que le premier octet du rapport sera l'identifiant du rapport.

6
D. Shinobi

J'ai mon appareil caché personnalisé détecté par Win7 avec ceci (construit par deviner et voler des exemples):

{
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x00,                    // USAGE (Undefined)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x85, 0x01,                    //   REPORT_ID (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x40,                    //   REPORT_COUNT (64)
    0x09, 0x00,                    //   USAGE (Undefined)
    0x81, 0x82,                    //   INPUT (Data,Var,Abs,Vol) - to the Host
    0x85, 0x02,                    //   REPORT_ID (2)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x40,                    //   REPORT_COUNT (64)
    0x09, 0x00,                    //   USAGE (Undefined)
    0x91, 0x82,                    //   OUTPUT (Data,Var,Abs,Vol) - from the Host
    0xc0                           // END_COLLECTION
}; /* CustomHID_ReportDescriptor */

Je ne sais pas si cela fonctionnera correctement. Va voir.

2
Amomum

Voici un lien vers la Spec Sheet (ou "manuel") à des fins de lecture.

Pour répondre à certaines de vos questions, REPORT_SIZE est spécifié en bits et REPORT_COUNT peut être utilisé pour spécifier le nombre d '"Usages" signalés avec les propriétés indiquées. Par exemple, vous pouvez définir les propriétés pour les utilisations X et Y et définir le REPORT_COUNT en tant que 2 (un pour X et un pour Y), puis spécifier la INPUT pour ajouter ces utilisations au rapport. Continuez ensuite à décrire d'autres usages.

De plus, n'oubliez pas que les utilisations sont alignées. Puisque REPORT_COUNT est spécifié en bits, il est facile d’oublier d’aligner les utilisations sur des octets. Ainsi, si une utilisation ne fait que 1 bit, vous devez spécifier que cet octet ne contiendra pas 7 bits avant de passer à la prochaine utilisation si elle nécessite plus de 7 bits.

0
Chef Pharaoh