Expand description

Attribute to generate a HID descriptor & serialization code

You are expected to provide two inputs to this generator:

  • A struct of named fields (which follows the gen_hid_descriptor attribute)
  • A specially-formatted section describing the properties of the descriptor (this section must be provided as arguments to the gen_hid_descriptor() attribute)

The generated HID descriptor will be available as a &[u8] by calling YourStructType::desc(). YourStructType also now implements SerializedDescriptor.

As long as a descriptor describes only input or output types, and a report ID is not used, the wire format for transmitting and recieving the data described by the descriptor is simply the packed representation of the struct itself. Where report ID’s are used anywhere in the descriptor, you must prepend the relevant report ID to the packed representation of the struct prior to transmission.

If inputs and outputs are mixed within the same HID descriptor, then only the struct fields used in that direction can be present in a payload being transmitted in that direction.

If report ID’s are not used, input (device-to-host) serialization code is generated automatically, and is represented by the implementation of the AsInputReport trait.


  • Custom 32-octet array, sent from device to host
    (collection = APPLICATION, usage_page = VENDOR_DEFINED_START, usage = 0x01) = {
struct CustomInputReport {
    buff: [u8; 32],
  • Custom input / output, sent in either direction
    (collection = APPLICATION, usage_page = VENDOR_DEFINED_START, usage = 0x01) = {
struct CustomBidirectionalReport {
    input_buffer: [u8; 32],
    output_buffer: [u8; 32],

Because both inputs and outputs are used, the data format when sending / recieving is the 32 bytes in the relevant direction, NOT the full 64 bytes contained within the struct.

  • Packed bitfields
    (report_id = 0x01,) = {
        #[packed_bits 3] f1=input;
        #[packed_bits 9] f2=input;
struct CustomPackedBits {
    f1: u8,
    f2: u16,

Because the #[packed_bits] sub-attribute was used, the two input fields specified are interpreted as packed bits. As such, f1 describes 3 boolean inputs, and f2 describes 9 boolean inputs. Padding constants are automatically generated.

The #[packed_bits <num bits>] feature is intended to be used for describing button presses.

  • Customizing the settings on a report item
    (collection = APPLICATION, usage_page = VENDOR_DEFINED_START, usage = 0x01) = {
        (usage_min = X, usage_max = Y) = {
            #[item_settings data,variable,relative] x=input;
            #[item_settings data,variable,relative] y=input;
struct CustomCoords {
    x: i8,
    y: i8,

The above example describes a report which sends X & Y co-ordinates. As indicated in the #[item_settings] sub-attribute, the individual inputs are described as:

  • Datapoints (data) - as opposed to constant
  • Variable (variable) - as opposed to an array
  • Relative (relative) - as opposed to absolute

Supported struct types

The struct following the attribute must consist entirely of named fields, using only types enumerated below, or fixed-size arrays of the types enumerated below.

  • u8 / i8
  • u16 / i16
  • u32 / i32

LOGICAL_MINIMUM & LOGICAL_MAXIMUM are automatically set in the descriptor, based on the type & whether #[packed_bits] was set on the field or not.

Descriptor format

The parameters of the HID descriptor should be provided as arguments to the attribute. The arguments should follow the basic form:

    <collection-spec> OR <item-spec>;
    <collection-spec> OR <item-spec>;
    <collection-spec> OR <item-spec>


    (parameter = <constant or 0xxxx>, ...) = {
        <collection-spec> OR <item-spec>;

Note: All collection specs must end in a semicolon, except the top-level one.

Note: Parameters are a tuple, so make sure you have a trailing comma if you only have one parameter.

The valid parameters are collection, usage_page, usage, usage_min, usage_max, and report_id. These simply configure parameters that apply to contained items in the report. Use of the collection parameter automatically creates a collection feature for all items which are contained within it, and other parameters specified in the same collection-spec apply to the collection, not directly to the elements of the collection (ie: defining a collection + a usage generates a descriptor where the usage is set on the collection, not the items contained within the collection).


    #[packed_bits <num_items>] #[item_settings <setting>,...] <fieldname>=input OR output;

The two sub-attributes are both optional.

  • fieldname refers to the name of a field within the struct. All fields must be specified.
  • input fields are sent in reports from device to host. output fields are sent in reports from host to device. This matches the terminology used in the USB & HID specifications.
  • packed_bits configures the field as a set of num_items booleans rather than a number. If the number of packed bits is less than the natural bit width of the field, the remaining most-significant bits are set as constants within the report and are not used. packed_bits is typically used to implement buttons.
  • item_settings describes settings on the input/output item, as enumerated in section of the HID specification, version 1.11. By default, all items are configured as (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position).


By default generated descriptors are such to maximize compatibility. To change this behaviour, you can use a #[quirks <settings>] attribute on the relevant input/output item. For now, the only quirk is #[quirks allow_short], which allows global features to be serialized in a 1 byte form. This is disabled by default as the Windows HID parser considers it invalid.