Updating OpenHardware Firmware 2

After quite a bit of peer review, it turns out my idea to use the unused serial field wasn’t so awesome. Thanks mostly to Stefan, we have a new proposal.

For generic USB devices you can use a firmware version extension that is now used by ColorHug, and I hope other projects too in the future. With this the fwupd daemon can obtain the firmware version without claiming the interface on the device and preventing other software from using it straight away.

To implement the firmware version extension just create an interface descriptor with class code 0xff, subclass code 0x46 and protocol 0x57 pointing to a string descriptor with the firmware version.
An example commit to the ColorHug2 firmware can be found here. It costs just 21 bytes of ROM which means it’s suitable even for resource-constrained devices like the ColorHugALS. Comments welcome.

Published by

hughsie

Richard has over 10 years of experience developing open source software. He is the maintainer of GNOME Software, PackageKit, GNOME Packagekit, GNOME Power Manager, GNOME Color Manager, colord, and UPower and also contributes to many other projects and opensource standards. Richard has three main areas of interest on the free desktop, color management, package management, and power management. Richard graduated a few years ago from the University of Surrey with a Masters in Electronics Engineering. He now works for Red Hat in the desktop group, and also manages a company selling open source calibration equipment. Richard's outside interests include taking photos and eating good food.

5 thoughts on “Updating OpenHardware Firmware 2”

  1. This looks much better to me, although not exactly what I meant. Different approach (keep the interface descriptor as is, but iInterface=0):


    #define DESC_TYPE_FWVERSION_SMALL 0
    #define DESC_TYPE_FWVERSION_LONG 1
    #define FW_MAJOR 42
    #define FW_MINOR 23
    #define FW_MICRO 1
    struct desc_fwversion_small {
    char bLength;
    char bDescriptorType;
    char bFwMajor;
    char bFwMinor;
    char bFwMicro;
    };
    struct desc_fwversion_long {
    char bLength;
    char bDescriptorType;
    uint16_t wFwMajor;
    uint16_t wFwMinor;
    uint16_t wFwMicro;
    };
    struct desc_fwversion_short fwDescS = {
    sizeof(desc_fwversion_small),
    DESC_TYPE_FWVERSION_SMALL,
    FW_MAJOR, FW_MINOR, FW_MICRO
    }
    struct desc_fwversion_short fwDescL = {
    sizeof(desc_fwversion_long),
    DESC_TYPE_FWVERSION_LONG,
    FW_MAJOR, FW_MINOR, FW_MICRO
    }

    The small descriptor is 5 bytes, the long descriptor is 8 bytes. The range is 0-255 resp 0-65535 per major/minor/micro field.

    Depending on your needs/constraints you use one of the two variants.

    To use the descriptor, you put it directly after the corresponding interface descriptor and adjust the Configuration wTotalLength by 5/8 bytes.

    Benefits:
    – The descriptor is part of the configuration descriptor, so no need to to a string descriptor lookup.
    – 3 bytes saved, range extended from 0-9 to 0-255 (for the small variant)
    Drawbacks: The string descriptor allows lax semantics, e.g. “1.23a-f”

    1. Right, the ability to have “1.2beta” or “1.2.3.4” as formats pushed me to the string descriptor route. I think it’s fine to ask firmware authors to add a small extra thing, but asking them to change all the firmware tools to accept a different format to what they’re already doing would probably be too much.

      1. Well, lets see if we can extend this and have best of both worlds:


        #define DESC_TYPE_FWVERSION_FREEFROM 2
        struct desc_fwversion_freeform {
        char bLength;
        char bDescriptorType;
        char bFwVersion[];
        };
        static const char vers_desc[] = {
        sizeof(struct desc_fwversion_freeform) + strlen("1.2beta"),
        DESC_TYPE_FWVERSION_FREEFORM,
        '1', '.', '2', 'b', 'e', 't', 'a'
        };

        You may ask, whats the benefit?
        1. No fetch of the string descriptor needed
        2. You save the ROM space for the pointer in the string descriptor table
        3. Extensibility

        Let me go into the last point. Imagine you have a system with several components, each one with its one firmware.
        You can in a later specification define a new descriptor type, with a an additional bIndex.

        1. Right, but I also see two drawbacks. The fact we have to process these custom descriptors using the raw ->extra bytes in libusb makes this hard to parse in client libs. Also, using a string descriptor means we get a nice libusb output for free that doesn’t need any custom parsing.

          Using a string descriptor also means we can share the string values with any extensions we include in the future, e.g. bootloader version, minimum supported version etc. I really think string descriptors are the way to go here.

          1. Hm, the second point will not work – you can only reference one iInterface from the Interface Descriptor. On the other hand, if you go by the seperate descriptor, you can use as many as you like. You can also combine both approaches, using an interposer descriptor:


            struct anyversion {
            char bLength;
            char bDescriptorType;
            char iVersion;
            }

            and then bDescriptorType is any of { DESC_TYPE_FWVERSION, DESC_TYPE_BOOTLOADER_VERSION, …}.
            iVersion can be retrieved using the standard GET_STRING_DESCRIPTOR.

Comments are closed.