Search code examples
usbhid

Non-application top-level collection?


The USB HID spec states

Each top level collection must be an application collection and reports may not span more than one top level collection.

However, one of the examples provided in the USB HID report descriptor tool, namely the display example, has a top-level collection of type logical with no application collection:

0x05, 0x14,                    // USAGE_PAGE (Alphnumeric Display)
0x09, 0x01,                    // USAGE (Alphanumeric Display)
0x15, 0x00,                    // LOGICAL_MINIMUM (0)
0xa1, 0x02,                    // COLLECTION (Logical)
0x09, 0x20,                    //   USAGE (Display Attributes Report)
0xa1, 0x02,                    //   COLLECTION (Logical)
0x09, 0x35,                    //     USAGE (Rows)
0x09, 0x36,                    //     USAGE (Columns)
0x09, 0x3d,                    //     USAGE (Character Width)
0x09, 0x3e,                    //     USAGE (Character Height)
0x85, 0x01,                    //     REPORT_ID (1)
0x25, 0x1f,                    //     LOGICAL_MAXIMUM (31)
0x75, 0x05,                    //     REPORT_SIZE (5)
0x95, 0x04,                    //     REPORT_COUNT (4)
0xb1, 0x03,                    //     FEATURE (Cnst,Var,Abs)
0x75, 0x01,                    //     REPORT_SIZE (1)
0x95, 0x03,                    //     REPORT_COUNT (3)
0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
0x09, 0x21,                    //     USAGE (ASCII Character Set)
0x09, 0x22,                    //     USAGE (Data Read Back)
0x09, 0x29,                    //     USAGE (Vertical Scroll)
0xb1, 0x03,                    //     FEATURE (Cnst,Var,Abs)
0x95, 0x03,                    //     REPORT_COUNT (3)
0xb1, 0x03,                    //     FEATURE (Cnst,Var,Abs)
0xc0,                          //   END_COLLECTION
0x75, 0x08,                    //   REPORT_SIZE (8)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x25, 0x02,                    //   LOGICAL_MAXIMUM (2)
0x09, 0x2d,                    //   USAGE (Display Status)
0xa1, 0x02,                    //   COLLECTION (Logical)
0x09, 0x2e,                    //     USAGE (Stat Not Ready)
0x09, 0x2f,                    //     USAGE (Stat Ready)
0x09, 0x30,                    //     USAGE (Err Not a loadable character)
0x81, 0x40,                    //     INPUT (Data,Ary,Abs,Null)
0xc0,                          //   END_COLLECTION
0x09, 0x32,                    //   USAGE (Cursor Position Report)
0xa1, 0x02,                    //   COLLECTION (Logical)
0x85, 0x02,                    //     REPORT_ID (2)
0x75, 0x04,                    //     REPORT_SIZE (4)
0x95, 0x01,                    //     REPORT_COUNT (1)
0x25, 0x0f,                    //     LOGICAL_MAXIMUM (15)
0x09, 0x34,                    //     USAGE (Column)
0xb1, 0x22,                    //     FEATURE (Data,Var,Abs,NPrf)
0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
0x09, 0x33,                    //     USAGE (Row)
0xb1, 0x22,                    //     FEATURE (Data,Var,Abs,NPrf)
0xc0,                          //   END_COLLECTION
0x09, 0x2b,                    //   USAGE (Character Report)
0xa1, 0x02,                    //   COLLECTION (Logical)
0x85, 0x03,                    //     REPORT_ID (3)
0x75, 0x08,                    //     REPORT_SIZE (8)
0x95, 0x04,                    //     REPORT_COUNT (4)
0x25, 0x7e,                    //     LOGICAL_MAXIMUM (126)
0x09, 0x2c,                    //     USAGE (Display Data)
0xb2, 0x02, 0x01,              //     FEATURE (Data,Var,Abs,Buf)
0xc0,                          //   END_COLLECTION
0x85, 0x04,                    //   REPORT_ID (4)
0x09, 0x3b,                    //   USAGE (Font Report)
0xa1, 0x02,                    //   COLLECTION (Logical)
0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
0x25, 0x7e,                    //     LOGICAL_MAXIMUM (126)
0x75, 0x08,                    //     REPORT_SIZE (8)
0x95, 0x01,                    //     REPORT_COUNT (1)
0x09, 0x2c,                    //     USAGE (Display Data)
0x91, 0x02,                    //     OUTPUT (Data,Var,Abs)
0x95, 0x05,                    //     REPORT_COUNT (5)
0x09, 0x3c,                    //     USAGE (Font Data)
0x92, 0x02, 0x01,              //     OUTPUT (Data,Var,Abs,Buf)
0xc0,                          //   END_COLLECTION
0xc0,                          // END_COLLECTION

Is this allowed?


Solution

  • No, it is not allowed.

    The example given seems to be quite similar to the example in "Appendix A.8 A Device with a Display" in the HID Usage Tables v1.12 specification which does have a top level application collection defined.

    I'd say the example you posted may have some transcription errors - especially since the top level Usage selected (0x00140001) has Usage Type of CA and so is intended to be used on an Application Collection item rather than a Logical Collection (see page 109 of the Usage Tables document).

    The first logical collection should be changed to:

    0xa1, 0x01,                    // COLLECTION (Application)
    

    It will then be parsed (albeit with bit alignment issues due to sections having been omitted from the original example) as:

    //--------------------------------------------------------------------------------
    // Decoded Application Collection
    //--------------------------------------------------------------------------------
    
    /*
    05 14        (GLOBAL) USAGE_PAGE         0x0014 Alphanumeric Display Page 
    09 01        (LOCAL)  USAGE              0x00140001 Alphanumeric Display (CA=Application Collection) 
    15 00        (GLOBAL) LOGICAL_MINIMUM    0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
    A1 01        (MAIN)   COLLECTION         0x00000001 Application (Usage=0x00140001: Page=Alphanumeric Display Page, Usage=Alphanumeric Display, Type=CA)
    09 20          (LOCAL)  USAGE              0x00140020 Display Attributes Report (CL=Logical Collection) 
    A1 02          (MAIN)   COLLECTION         0x00000002 Logical (Usage=0x00140020: Page=Alphanumeric Display Page, Usage=Display Attributes Report, Type=CL)
    09 35            (LOCAL)  USAGE              0x00140035 Rows (SV=Static Value) 
    09 36            (LOCAL)  USAGE              0x00140036 Columns (SV=Static Value) 
    09 3D            (LOCAL)  USAGE              0x0014003D Character Width (SV=Static Value) 
    09 3E            (LOCAL)  USAGE              0x0014003E Character Height (SV=Static Value) 
    85 01            (GLOBAL) REPORT_ID          0x01 (1) 
    25 1F            (GLOBAL) LOGICAL_MAXIMUM    0x1F (31)  
    75 05            (GLOBAL) REPORT_SIZE        0x05 (5) Number of bits per field  
    95 04            (GLOBAL) REPORT_COUNT       0x04 (4) Number of fields  
    B1 03            (MAIN)   FEATURE            0x00000003 (4 fields x 5 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
    75 01            (GLOBAL) REPORT_SIZE        0x01 (1) Number of bits per field  
    95 03            (GLOBAL) REPORT_COUNT       0x03 (3) Number of fields  
    25 01            (GLOBAL) LOGICAL_MAXIMUM    0x01 (1)  
    09 21            (LOCAL)  USAGE              0x00140021 ASCII Character Set (SF=Static Flag) 
    09 22            (LOCAL)  USAGE              0x00140022 Data Read Back (SF=Static Flag) 
    09 29            (LOCAL)  USAGE              0x00140029 Vertical Scroll (SFDF=Static Flag or Dynamic Flag) 
    B1 03            (MAIN)   FEATURE            0x00000003 (3 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
    95 03            (GLOBAL) REPORT_COUNT       0x03 (3) Number of fields <-- Redundant: REPORT_COUNT is already 3 
    B1 03            (MAIN)   FEATURE            0x00000003 (3 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
    C0             (MAIN)   END_COLLECTION     Logical 
    75 08          (GLOBAL) REPORT_SIZE        0x08 (8) Number of bits per field  
    95 01          (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields  
    25 02          (GLOBAL) LOGICAL_MAXIMUM    0x02 (2)  
    09 2D          (LOCAL)  USAGE              0x0014002D Display Status (CL=Logical Collection) 
    A1 02          (MAIN)   COLLECTION         0x00000002 Logical (Usage=0x0014002D: Page=Alphanumeric Display Page, Usage=Display Status, Type=CL)
    09 2E            (LOCAL)  USAGE              0x0014002E Stat Not Ready (Sel=Selector) 
    09 2F            (LOCAL)  USAGE              0x0014002F Stat Ready (Sel=Selector) 
    09 30            (LOCAL)  USAGE              0x00140030 Err Not a loadable character (Sel=Selector) 
    81 40            (MAIN)   INPUT              0x00000040 (1 field x 8 bits) 0=Data 0=Array 0=Absolute 
    C0             (MAIN)   END_COLLECTION     Logical 
    09 32          (LOCAL)  USAGE              0x00140032 Cursor Position Report (CL=Logical Collection) 
    A1 02          (MAIN)   COLLECTION         0x00000002 Logical (Usage=0x00140032: Page=Alphanumeric Display Page, Usage=Cursor Position Report, Type=CL)
    85 02            (GLOBAL) REPORT_ID          0x02 (2) 
    75 04            (GLOBAL) REPORT_SIZE        0x04 (4) Number of bits per field  
    95 01            (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1 
    25 0F            (GLOBAL) LOGICAL_MAXIMUM    0x0F (15)  
    09 34            (LOCAL)  USAGE              0x00140034 Column (DV=Dynamic Value) 
    B1 22            (MAIN)   FEATURE            0x00000022 (1 field x 4 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 1=NoPrefState 0=NoNull 0=NonVolatile 0=Bitmap 
    25 01            (GLOBAL) LOGICAL_MAXIMUM    0x01 (1)  
    09 33            (LOCAL)  USAGE              0x00140033 Row (DV=Dynamic Value) 
    B1 22            (MAIN)   FEATURE            0x00000022 (1 field x 4 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 1=NoPrefState 0=NoNull 0=NonVolatile 0=Bitmap 
    C0             (MAIN)   END_COLLECTION     Logical 
    09 2B          (LOCAL)  USAGE              0x0014002B Character Report (CL=Logical Collection) 
    A1 02          (MAIN)   COLLECTION         0x00000002 Logical (Usage=0x0014002B: Page=Alphanumeric Display Page, Usage=Character Report, Type=CL)
    85 03            (GLOBAL) REPORT_ID          0x03 (3) 
    75 08            (GLOBAL) REPORT_SIZE        0x08 (8) Number of bits per field  
    95 04            (GLOBAL) REPORT_COUNT       0x04 (4) Number of fields  
    25 7E            (GLOBAL) LOGICAL_MAXIMUM    0x7E (126)  
    09 2C            (LOCAL)  USAGE              0x0014002C Display Data (DV=Dynamic Value) 
    B2 0201          (MAIN)   FEATURE            0x00000102 (4 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer 
    C0             (MAIN)   END_COLLECTION     Logical 
    85 04          (GLOBAL) REPORT_ID          0x04 (4) 
    09 3B          (LOCAL)  USAGE              0x0014003B Font Report (CL=Logical Collection) 
    A1 02          (MAIN)   COLLECTION         0x00000002 Logical (Usage=0x0014003B: Page=Alphanumeric Display Page, Usage=Font Report, Type=CL)
    15 00            (GLOBAL) LOGICAL_MINIMUM    0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
    25 7E            (GLOBAL) LOGICAL_MAXIMUM    0x7E (126) <-- Redundant: LOGICAL_MAXIMUM is already 126 
    75 08            (GLOBAL) REPORT_SIZE        0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8 
    95 01            (GLOBAL) REPORT_COUNT       0x01 (1) Number of fields  
    09 2C            (LOCAL)  USAGE              0x0014002C Display Data (DV=Dynamic Value) 
    91 02            (MAIN)   OUTPUT             0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap 
    95 05            (GLOBAL) REPORT_COUNT       0x05 (5) Number of fields  
    09 3C            (LOCAL)  USAGE              0x0014003C Font Data (BB=Buffered Bytes) 
    92 0201          (MAIN)   OUTPUT             0x00000102 (5 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer 
    C0             (MAIN)   END_COLLECTION     Logical 
    C0           (MAIN)   END_COLLECTION     Application 
    */
    
    //--------------------------------------------------------------------------------
    // Alphanumeric Display Page featureReport 01 (Device <-> Host)
    //--------------------------------------------------------------------------------
    
    typedef struct
    {
      uint8_t  reportId;                                 // Report ID = 0x01 (1)
                                                         // Collection: AlphanumericDisplay DisplayAttributesReport
      uint8_t  AD_AlphanumericDisplayDisplayAttributesReportRows : 5; // Usage 0x00140035: Rows, Value = 0 to 31
      uint8_t  AD_AlphanumericDisplayDisplayAttributesReportColumns : 5; // Usage 0x00140036: Columns, Value = 0 to 31
      uint8_t  AD_AlphanumericDisplayDisplayAttributesReportCharacterWidth : 5; // Usage 0x0014003D: Character Width, Value = 0 to 31
      uint8_t  AD_AlphanumericDisplayDisplayAttributesReportCharacterHeight : 5; // Usage 0x0014003E: Character Height, Value = 0 to 31
      uint8_t  AD_AlphanumericDisplayDisplayAttributesReportAsciiCharacterSet : 1; // Usage 0x00140021: ASCII Character Set, Value = 0 to 1
      uint8_t  AD_AlphanumericDisplayDisplayAttributesReportDataReadBack : 1; // Usage 0x00140022: Data Read Back, Value = 0 to 1
      uint8_t  AD_AlphanumericDisplayDisplayAttributesReportVerticalScroll : 1; // Usage 0x00140029: Vertical Scroll, Value = 0 to 1
      uint8_t  : 1;                                      // Pad
      uint8_t  : 1;                                      // Pad
      uint8_t  : 1;                                      // Pad
    } featureReport01_t;
    
    
    //--------------------------------------------------------------------------------
    // Alphanumeric Display Page featureReport 02 (Device <-> Host)
    //--------------------------------------------------------------------------------
    
    typedef struct
    {
      uint8_t  reportId;                                 // Report ID = 0x02 (2)
                                                         // Collection: AlphanumericDisplay CursorPositionReport
      uint8_t  AD_AlphanumericDisplayCursorPositionReportColumn : 4; // Usage 0x00140034: Column, Value = 0 to 15
      uint8_t  AD_AlphanumericDisplayCursorPositionReportRow : 4; // Usage 0x00140033: Row, Value = 0 to 1
    } featureReport02_t;
    
    
    //--------------------------------------------------------------------------------
    // Alphanumeric Display Page featureReport 03 (Device <-> Host)
    //--------------------------------------------------------------------------------
    
    typedef struct
    {
      uint8_t  reportId;                                 // Report ID = 0x03 (3)
                                                         // Collection: AlphanumericDisplay CharacterReport
      uint8_t  AD_AlphanumericDisplayCharacterReportDisplayData[4]; // Usage 0x0014002C: Display Data, Value = 0 to 126
    } featureReport03_t;
    
    
    //--------------------------------------------------------------------------------
    // Alphanumeric Display Page inputReport 01 (Device --> Host)
    //--------------------------------------------------------------------------------
    
    typedef struct
    {
      uint8_t  reportId;                                 // Report ID = 0x01 (1)
                                                         // Collection: AlphanumericDisplay DisplayStatus
      uint8_t  AD_AlphanumericDisplayDisplayStatus;      // Value = 0 to 2
                                                         // Value 0 = Usage 0x0014002E: Stat Not Ready
                                                         // Value 1 = Usage 0x0014002F: Stat Ready
                                                         // Value 2 = Usage 0x00140030: Err Not a loadable character
    } inputReport01_t;
    
    
    //--------------------------------------------------------------------------------
    // Alphanumeric Display Page outputReport 04 (Device <-- Host)
    //--------------------------------------------------------------------------------
    
    typedef struct
    {
      uint8_t  reportId;                                 // Report ID = 0x04 (4)
                                                         // Collection: AlphanumericDisplay FontReport
      uint8_t  AD_AlphanumericDisplayFontReportDisplayData; // Usage 0x0014002C: Display Data, Value = 0 to 126
      uint8_t  AD_AlphanumericDisplayFontReportFontData[5]; // Usage 0x0014003C: Font Data, Value = 0 to 126
    } outputReport04_t;