Authored by Arvind Aiyar [MSFT]
One of the descriptors that a USB HID device needs to support is the HID Report Descriptor. HID devices transmit data using HID reports, and the Report Descriptor is the blueprint for interpreting the data that is being sent across the wire. Typically, a USB Host will request the HID Report Descriptor when enumerating the device.
In the Windows USB HID stack, there is an upper limit of 4K on the size of the report descriptor - this is related to the maximum transfer size limits for Control transfers on USB low speed. For most HID devices, this size limit is never attained. However, with newer HID devices, especially sensors, there is a possibility that the descriptor may be larger in size than 4k.
In this blog post, we will go over some common methods to reduce the size of your HID report descriptor. It might be useful to have a working knowledge of the HID Protocol Specification to better follow through the examples.
Avoiding Re-definition of Global Items
Per the HID spec, there are certain items that are considered Global. In other words, their value once declared, is carried over to the lines following it until it needs to be changed, at which point it can be declared again. Therefore, you don't need to repeatedly declare same value for global items. Here is an example:
In the above example, there are several items which are Global and thus they need not be re-defined repeatedly. Also, the two Feature items of the same format can be gathered into a single Feature item.0x05, 0x20, // USAGE_PAGE (Sensors)
0x09, 0x01, // USAGE (Sensor: All)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (1)
0x05, 0x20, // USAGE_PAGE (Sensors) <-- Re-defined Global
0x09, 0x73, // USAGE (Motion: Accelerometer 3D)
0xa1, 0x00, // COLLECTION (Physical)
0x0a, 0x09, 0x03, // USAGE (Property: Sensor Connection Type)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x02, // LOGICAL_MAXIMUM (2)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x0a, 0x30, 0x08, // USAGE (SEL: PC_INTEGRATED)
0x0a, 0x31, 0x08, // USAGE (SEL: PC_ATTACHED)
0x0a, 0x32, 0x08, // USAGE (SEL: PC_EXTERNAL)
0xb1, 0x00, // FEATURE (Data,Ary,Abs)
0x0a, 0x16, 0x03, // USAGE (Property: Reporting State)
0x15, 0x00, // LOGICAL_MINIMUM (0) <-- Re-defined Global
0x25, 0x02, // LOGICAL_MAXIMUM (5)
0x75, 0x08, // REPORT_SIZE (8) <-- Re-defined Global
0x95, 0x01, // REPORT_COUNT (1) <-- Re-defined Global
0x0a, 0x40, 0x08, // USAGE (SEL: NO EVENTS)
0x0a, 0x41, 0x08, // USAGE (SEL: ALL EVENTS)
0x0a, 0x42, 0x08, // USAGE (SEL: THRESHOLD EVENTS)
0x0a, 0x43, 0x08, // USAGE (SEL: NO EVENTS WAKE)
0x0a, 0x44, 0x08, // USAGE (SEL: ALL EVENTS WAKE)
0x0a, 0x45, 0x08, // USAGE (SEL: THRESHOLD EVENTS WAKE)
0xb1, 0x00, // FEATURE (Data,Ary,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
0x05, 0x20, // USAGE_PAGE (Sensors) <-- Definition carries forward
0x09, 0x01, // USAGE (Sensor: All)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (1)
0x09, 0x73, // USAGE (Motion: Accelerometer 3D)
0xa1, 0x00, // COLLECTION (Physical)
0x0a, 0x09, 0x03, // USAGE (Property: Sensor Connection Type)
0x15, 0x00, // LOGICAL_MINIMUM (0) <-- Definition carries forward
0x25, 0x02, // LOGICAL_MAXIMUM (2)
0x0a, 0x30, 0x08, // USAGE (SEL: PC_INTEGRATED)
0x0a, 0x31, 0x08, // USAGE (SEL: PC_ATTACHED)
0x0a, 0x32, 0x08, // USAGE (SEL: PC_EXTERNAL)
0x0a, 0x16, 0x03, // USAGE (Property: Reporting State)
0x25, 0x02, // LOGICAL_MAXIMUM (5)
0x0a, 0x40, 0x08, // USAGE (SEL: NO EVENTS)
0x0a, 0x41, 0x08, // USAGE (SEL: ALL EVENTS)
0x0a, 0x42, 0x08, // USAGE (SEL: THRESHOLD EVENTS)
0x0a, 0x43, 0x08, // USAGE (SEL: NO EVENTS WAKE)
0x0a, 0x44, 0x08, // USAGE (SEL: ALL EVENTS WAKE)
0x0a, 0x45, 0x08, // USAGE (SEL: THRESHOLD EVENTS WAKE)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2) <-- Increased to 2
0xb1, 0x00, // FEATURE (Data,Ary,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
Using Usage Ranges
Another technique that can reduce descriptor size is using usage ranges. Whenever there are contiguous usage values, they can all be combined into a usage range. Using a snippet from the report descriptor above:
0x0a, 0x30, 0x08, // USAGE (SEL: PC_INTEGRATED)
0x0a, 0x31, 0x08, // USAGE (SEL: PC_ATTACHED)
0x0a, 0x32, 0x08, // USAGE (SEL: PC_EXTERNAL)
Can be transformed into:
0x19, 0x30, 0x08, // USAGE MIN (SEL: PC_INTEGRATED)
0x29, 0x32, 0x08, // USAGE MAX (SEL: PC_EXTERNAL)
And …
0x0a, 0x40, 0x08, // USAGE (SEL: NO EVENTS)
0x0a, 0x41, 0x08, // USAGE (SEL: ALL EVENTS)
0x0a, 0x42, 0x08, // USAGE (SEL: THRESHOLD EVENTS)
0x0a, 0x43, 0x08, // USAGE (SEL: NO EVENTS WAKE)
0x0a, 0x44, 0x08, // USAGE (SEL: ALL EVENTS WAKE)
Into:
0x19, 0x40, 0x08, // USAGE MIN (SEL: NO EVENTS)
0x29, 0x44, 0x08, // USAGE (SEL: ALL EVENTS WAKE)
Report IDs and Default Values
When there is only one report of a given type – Input, Output or Feature – it is not necessary to specify a Report ID. A Report ID is required only when there are multiple Input reports for example (same for Feature and Output).
Also, certain items have inbuilt default values and do not need to be defined explicitly. For example a definition like:
0x55, 0x00, // UNIT_EXPONENT (0)
0x65, 0x00, // UNIT (None)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
is not necessary as these are already the default values.
Feedback
These are just some techniques that can be employed to get your HID report descriptor size reduced. If you have more tips/tricks to share, please post it here as comments.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.