## Introduction

When `fwupd` starts it enumerates all PCI and USB hardware and generates Instance IDs based on the reported vendor and product so that it can match the device to a plugin. The plugin knows how to communicate with the device (for instance using vendor-specific USB control transfers) and also knows how to parse the firmware and deliver it to the device.

Before a vendor can ship a firmware on the LVFS to a machine using Linux (or ChromeOS) the fwupd package often must be updated so that it knows what plugin to use for that specific device. At this point other overridden quirk data can also be set. For instance, the icon, long summary or even per-plugin flags that change device behavior. For example `colorhug.quirk`:

    [USB\VID_273F&PID_1001]
    Plugin = colorhug
    Flags = self-recovery
    Icon = colorimeter-colorhug

Updating the fwupd binary package might take anywhere from a few weeks to several years due to various Linux distribution policies. Clearly this isn’t good when the majority of firmware updates are distributed to address security issues.

One suggestion would be to put this quirk information into the existing update metadata provided by the configured remote, but this has several problems:

- The daemon needs to enumerate hardware that does not have updates on the main LVFS remote.
- A lot of machines using fwupd have never connected to the internet and we still want to enumerate hardware for audit and verification purposes.
- Re-enumerating all physical hardware (to get the latest quirks) when updating the metadata is not straightforward.
- The VID/PID is not necessarily the information that the plugin matches to assign the firmware stream, and so this would have to be a separate facet of metadata — which may have to include quirks too.

Matching devices to plugins should be thought of as orthogonal to matching firmware to devices. To simplify deployment it should be possible to allow the device itself to specify the plugin to use and optionally additional quirk data.

## Specification

Devices that implement a fwupd BOS DS20 descriptor can be updated without always needing to update the runtime version of fwupd for updated quirk entries, assuming the device is updatable by the existing vendor plugin.

USB devices can create a new platform device capability BOS “Binary Object Store” descriptor with `bDevCapabilityType=0x05` and a fwupd-specific UUID, specifically `010aec63-f574-52cd-9dda-2852550d94f0`. This UUID is generated type-5 SHA-1 hash of the word `fwupd` using a DNS namespace. Using this custom UUID will ensure that Microsoft Windows does not try to parse the capability descriptor as a Microsoft OS 2.0 descriptor set.

## Implementation

Create a BOS DS20 descriptor such as:

    1C 10 05 00 63 ec 0a 01 74 f5 cd 52 9d da 28 52 55 0d 94 f0 0e 09 01 00 20 00 2a 00
    ├┘ ├┘ ├┘ ├┘ └─────────────┬───────────────────────────────┘ └─┬───────┘ └─┬─┘ ├┘ ├┘
    │  │  │  │                └─PlatformCapabilityUUID            │   wLength─┘   │  │
    │  │  │  └─bReserved                                dwVersion─┘   bVendorCode─┘  │
    │  │  └────bDevCapability                                         bAltEnumCmd────┘
    │  └───────bDescriptorType
    └──────────bLength

The `dwVersion` encoded here is fwupd `1.9.14`, which is also the first version that with working BOS DS20 support.

The BOS descriptors are sorted by the requester and can appear in any order. The descriptor with the highest `dwVersion` that is not newer than the current running daemon version is used. This means that `dwVersion` is effectively the minimum version of fwupd that should read the descriptor. This allows devices to set different quirks depending on the fwupd version, although in practice this should not be required. A suitable bVendorCode should be chosen that is not used for existing device operation.

- The `dwVersion` parameter must be larger than `0x0001090e`, i.e. 1.9.14.
- The `bAltEnumCmd` parameter must be zero.
- The `PlatformCapabilityUUID` must be `010aec63-f574-52cd-9dda-2852550d94f0`.

Then allow the device to reply to a USB control request with the following parameters:

    transfer-direction: device-to-host
    request-type: vendor
    recipient: device
    bRequest: {value of bVendorCode in the BOS descriptor}
    wValue: 0x00
    wIndex: 0x07
    wLength: {value of wLength in the BOS descriptor}

The device should return something like:

    50 6c 75 67 69 6e 3d 64 66 75 0a 49 63 6f 6e 3d 63 6f 6d 70 75 74 65 72 0a 00 00 00 00 00 00 00

…which is the UTF-8 quirk data, e.g.

    Plugin=dfu
    Icon=computer

The UTF-8 quirk data must not contain Windows CRLF-style line endings.

## Workflow

To generate the fwupd DS20 descriptor save a file such as `fw-ds20.builder.xml`:

    <firmware gtype="FuUsbDeviceFwDs20">
      <idx>42</idx>   <!-- bVendorCode -->
      <size>32</size> <!-- wLength -->
    </firmware>

Then run `fwupdtool firmware-build fw-ds20.builder.xml fw-ds20.bin`.

To generate the control transfer response, save a file such as `fw-ds20.quirk`:

    [USB\VID_273F&PID_1004]
    Plugin = dfu
    Icon = computer

Then run `contrib/generate-ds20.py fw-ds20.quirk --bufsz 32`. The maximum buffer size is typically hardcoded for the device and may be specified in a microcontroller datasheet.

## Prior Art

### Hardcoded Class & Subclass

The fwupd project already support two plugins that use the USB class code, rather than the exact instance ID with the VID/PID. For example, this DFU entry means “match any USB device with class 0xFE (application specific) and subclass 0x01”:

    [USB\CLASS_FE&SUBCLASS_01]
    Plugin = dfu

These kind of devices do not need any device to plugin mapping (although, they still might need a quirk if they are non-complaint in some way, for example needing `Flags = detach-for-attach`) – but in the most cases they just work.

The same can be done for Fastboot devices, matching class `0xFF` (vendor specific), subclass `0x42` and protocol `0x03`, although there is the same caveat for non-compliant devices that need quirk entries like `FastbootOperationDelay = 250`:

    [USB\CLASS_FF&SUBCLASS_42&PROT_03]
    Plugin = fastboot

#### Microsoft OS Descriptors 1.0

The Microsoft OS Descriptors 1.0 Specification defines a device-specific variable-length metadata block of `CompatibleID`:`SubCompatibleID` on a string index of `0xEE` where the `CompatibleID` and `SubCompatibleID` are also both hardcoded at 8 bytes.

Using `FWUPDPLU` or `FWUPDFLA` as the `CompatibleID` would be acceptable, but we could not fit the plugin name (e.g. `logitech-bulkcontroller`) or the GUID (16 bytes) in an 8 byte `SubCompatibleID`. Some non-compliant devices also hang and stop working when probing this specific string index.

#### Microsoft OS Descriptors 2.0

The Microsoft OS Descriptors 2.0 Specification is more useful as it defines a new device capability that can return a variable length vendor-defined section of data, using a UUID as a key. Using the `BOS` “Binary Object Store” descriptor is only available for devices using USB specification version 2.1 and newer. The BOS descriptor is used for Wireless USB details, USB 2.0 extensions, SuperSpeed USB connection details and a Container ID.

The BOS descriptor does give the OS the ability to parse the platform capability, which is `bDevCapabilityType=0x05`. For UUID `D8DD60DF-4589-4CC7-9CD2-659D9E648A9F` this is identified as a structured blob of data Microsoft Windows uses for the suspend mode of the device.

Creating a new `bDevCapabilityType` would allow vendors to store a binary blob (e.g. `Plugin=foobarbaz\nFlags=QuirkValueHere\n`) but that would be out-of-specification and difficult to implement.