A DFU file is the firmware image exactly as it is sent to the device, with a suffix of at least 16 bytes added at the end. The suffix is defined in Appendix B of the USB Device Firmware Upgrade class specification, release 1.1 (USB Implementers Forum, 2004). The flashing program reads the suffix, checks that the file is meant for the attached device, and sends only the part before it. A device never sees the suffix.
Layout
The suffix sits at the end of the file so that a reader can find it without knowing
how long the firmware is. The specification describes it backwards: it numbers the
fields with negative offsets from the last byte (dwCRC at −0,
bLength at −4, ucDfuSignature at −5, bcdDFU at
−8, idVendor at −10, idProduct at −12, bcdDevice
at −14), and says the suffix is built in that order and then reversed end for end
before it is appended. The result, as the specification’s own sample code writes it,
is the layout below, with every multi-byte number little-endian. The table lists the
fields in file order, with the usual offset from the end of the file and the offset
from the start of a 16-byte suffix.
| From end | In suffix | Size | Name | Meaning |
|---|---|---|---|---|
−16 | 0 | 2 | bcdDevice | Release number of the device firmware the file holds, in binary-coded decimal, or 0xFFFF when the file is not tied to a release. |
−14 | 2 | 2 | idProduct | USB product ID the file is for, or 0xFFFF for any product. |
−12 | 4 | 2 | idVendor | USB vendor ID the file is for, or 0xFFFF for any vendor. |
−10 | 6 | 2 | bcdDFU | Version of the suffix, in binary-coded decimal: 0x0100 (version 1.0, bytes 00 01) in DFU release 1.1, and 0x011A in an ST DfuSe file (bytes 1A 01). |
−8 | 8 | 3 | ucDfuSignature | The bytes 55 46 44, the letters UFD. Read backwards from the end of the file they spell “DFU”. |
−5 | 11 | 1 | bLength | Length of the whole suffix in bytes, 16 (0x10) for this version. |
−4 | 12 | 4 | dwCRC | CRC of every byte of the file before this field, including the rest of the suffix (see CRC). |
The specification promises that dwCRC, bLength,
ucDfuSignature and bcdDFU will not move in later revisions,
that the signature stays the same and that the CRC is always calculated the same way.
bLength lets a later revision, or a vendor, add fields in front of the ones
above: a reader takes the last bLength bytes as the suffix and the bytes
before them as the firmware. No published revision has used a length other than 16,
and the DfuSe extension keeps 16.
Matching the file to a device
Before downloading, the host compares the suffix with the device’s USB device descriptor:
idVendor0xFFFF: the file may be sent to any device, andidProductis ignored. A vendor may still add a suffix like this to keep the standard file format, or to record a release number inbcdDevice.- Any other
idVendor: the file may only be sent to a device whose descriptor has the same vendor ID. idProduct0xFFFF: any product of that vendor. Any other value: the product ID must match too.bcdDevice0xFFFFis ignored. Otherwise it is the version of the firmware in the file, for information only: it never stops a download. The specification suggests vendors use a number that rises with each release, so a host can warn before installing an older version than the device reports.
Because the host checks the file before the device switches to DFU mode, and a device
may use a different product ID in DFU mode, the specification says
idProduct should hold the product’s normal (run-time) product ID.
The host should check that ucDfuSignature holds the expected bytes and that
the CRC matches before trusting the file. A file that fails either check is not a valid
DFU file; most tools then refuse it or, with an explicit option, send the whole file as
raw firmware.
CRC
dwCRC is a 32-bit CRC calculated as in the specification’s sample code,
which uses Gary S. Brown’s 1986 CRC-32 table (reflected polynomial
0xEDB88320), starts at 0xFFFFFFFF and applies
no final inversion. In other words it is the bitwise NOT of the usual
CRC-32 that Python’s zlib.crc32() returns, which is how MicroPython’s
DfuSe tool computes it:
crc = 0xFFFFFFFF
for each byte b of the file, from offset 0 up to (file length − 4):
crc = table[(crc XOR b) AND 0xFF] XOR (crc >> 8)
dwCRC = crc (no final XOR)
equivalently: dwCRC = (NOT zlib.crc32(data)) AND 0xFFFFFFFF
table is the 256-entry CRC-32 table printed in the specification.The CRC covers the firmware and the first 12 bytes of the suffix, so it is written last, after the IDs and signature are in place. It is stored little-endian.
Worked example
The last 16 bytes of a file for a device with the (made-up) vendor ID
0x1234 and product ID 0xABCD, with no release number:
FF FF bcdDevice 0xFFFF, release not stated
CD AB idProduct 0xABCD
34 12 idVendor 0x1234
00 01 bcdDFU 0x0100, plain DFU
55 46 44 ucDfuSignature "UFD"
10 bLength 16
xx xx xx xx dwCRC CRC of every earlier byte, little-endian
The same suffix on a DfuSe file differs only in bcdDFU, which reads
1A 01. See DfuSe prefix and images
for what comes before it in that case.
Adding and checking a suffix
dfu-util’s dfu-suffix program adds (--add), checks
(--check) and removes (--delete) a suffix, with
-v/--vid, -p/--pid,
-d/--did and -S/--spec for the
vendor ID, product ID, device ID and DFU version, all in hexadecimal. Its manual notes
that the suffix is recommended rather than required.
Sources
- Universal Serial Bus Device Class Specification for Device Firmware Upgrade, version 1.1 – USB-IF, 2004; Appendix B, the DFU file suffix, and its sample CRC code
- tools/dfu.py – MicroPython’s DfuSe reader and writer, for the suffix byte order and the CRC as NOT
zlib.crc32 - dfu-suffix(1) – manual page for dfu-util’s suffix tool