Configuration descriptor: Data structures

Last time, we got to the Address state, but it’s not yet the end of bus
enumeration. At the very least, we need to have a Configuration
descriptor and to communicate it to the host when asked.

Let’s first define the structure types and constants related to the
Configuration descriptor, straight from the USB and HID specs.

Note that the HID descriptor contains an array of HID class descriptor
headers. We are going to have just one such header for the Report
descriptor.
Configuration descriptor: Actual data

In the Device descriptor we said we have one Configuration. So the host
will next ask us for the descriptor of Configuration 1, by sending us a
Device `GET_DESCRIPTOR` request with `wValue` containing the
Configuration descriptor type in the high byte and 1 (the configuration
index) in the low byte.

In response to this request, we need to transmit a contiguous buffer
containing the configuration descriptor itself, then each of the
Interface descriptors with all of their Endpoint descriptors
interleaved. As a HID device, we are also expected to send with each
Interface descriptor its HID descriptor, right before the Endpoint
descriptors; then the host will ask us for the class-specific Report
descriptor.

For now, we will have a single Interface, with a single Endpoint. (Later
we will want another Interface, when dealing with media keys.)

Here’s the overall structure of our descriptors:

* Configuration
  * Interface 0
    * HID for Interface 0
    * Endpoint 1
  * (later) Interface 1
    * (later) HID for Interface 1
    * (later) Endpoint 2

In order to have all descriptors in a contiguous block, we define a
structure type containing all descriptors, and a constant of that type.
Configuration descriptor: Report descriptor

According to the HID spec, the report descriptor, unlike other
descriptors, is not a fixed-structure data record. Rather, it is a byte
sequence encoding the structure of reports. The report descriptor is
composed of items, with each item having a tag and optionally a value.

The easiest way to define such a structure would be as an array of
bytes, with actual byte values and their meaning in comments. The
reference implementation in the MG84FL54 SDK goes this way:

```c
code BYTE KB_HID_REPORT[]=                       //
  { 0x05,0x01,                                   // Usage Page (Generic Desktop Control)
    0x09,0x06,                                   // Usage (Keyboard)
    0xA1,0x01,                                   // Collection (Application)
    […]
```

While technically possible, it is very much like programming in machine
code. You don’t really program in it, you program in at least an
assembly language, and have it automatically translated to machine code.

What we want here is a domain-specific language, a means for us to
specify the items in human-readable form that would be translated into
suitable binary form.

The HID spec says an item may be short or long, depending on how much
data it carries. It also says there are no long items defined yet, so we
are left with short ones. A short item may have no argument, or a 1-, 2-
or 4-byte argument, and there are specific formats for each data size.

Since there is no easy way to determine at compile time which data size
each item wants, we will just specify this ourselves, by defining four
macros, one for each size. Each macro will expand to the appropriate
sequence of bytes.

Here we define the reference report descriptor for a HID Boot keyboard,
transcribed straight from the HID spec. The Boot keyboard protocol is
designed to allow BIOSes to work with USB keyboards without having to
parse report descriptors. All that a BIOS is expected to do is tell the
device “hey I’m a dumb BIOS, go easy on me” and the device will then
send reports according to this reference report descriptor. When a real
OS boots up, it will reset the device in normal mode, and the keyboard
may then enable all kinds of fancy features that need a different report
format, like NKRO (Boot only allows 8+6KRO, with 8 modifiers and up to 6
normal keys) or media keys.
Configuration descriptor: Handling requests

Now that we’ve defined the descriptors, let’s write the code to return
them when requested.

The Configuration descriptor is returned through the same standard
`GET_DESCRIPTOR` request targeted at the device. The low byte of
`wValue` will contain the index of the configuration being requested.
Note how the _index_ of the configuration is not necessarily the same as
its _value_.

The HID descriptor and the HID Report descriptor are requested at the
Interface. (Recall that the HID spec says it should be the interface that
claims to conform to the HID class, not the whole device.) Otherwise,
it’s the same old `GET_DESCRIPTOR` request.
## Configuration descriptor: Testing

Now, when we plug in our device, the kernel logs show this:

```
[36945.708070] usb 2-3.1: new full-speed USB device number 15 using ehci-pci
[36945.818065] usb 2-3.1: New USB device found, idVendor=0e6a, idProduct=030d
[36945.818070] usb 2-3.1: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[36945.818315] usb 2-3.1: can't set config #1, error -32
```

That is, it knows our vendor and product IDs, it got our Manufacturer,
Product, and Serial Number strings (which we do not set yet), and it
decided to choose configuration 1 (our only, as we told it), but was
unable because we don’t handle the `SET_CONFIGURATION` request.

This and other requests valid in the Address state will be the focus of
the next series.