USB标准中Functional Descriptor是什么?DFU开发及libusb使用疑问
Let's break down your questions clearly— I remember being confused about class-specific descriptors when I first started with USB DFU too, so I get where you're coming from.
1. What is a Functional Descriptor?
Unlike standard USB descriptors (Device, Configuration, Interface, Endpoint) that define basic hardware capabilities, Functional Descriptors are class-specific—they exist to share extra details unique to a particular USB device class (in your case, DFU).
For Run-Time DFU, this descriptor tells the host critical information about how the device behaves in DFU mode, like:
- Whether the device supports firmware downloads (
bmAttributesbit 0) or uploads (bit 1) - How long the device waits before detaching from USB to enter DFU mode (
wDetachTimeOut) - The maximum size of data transfers allowed during DFU operations (
wTransferSize) - The version of the DFU specification the device complies with (
bcdDFUVersion)
It’s always tied to a specific DFU interface, and sits right after the Interface Descriptor in the configuration descriptor hierarchy.
2. How to Retrieve a DFU Functional Descriptor with libusb
Libusb doesn’t have a built-in function to directly fetch this descriptor, but you can extract it from the configuration descriptor’s extra data. Here’s a practical, commented example:
First, define the DFU Functional Descriptor structure (matching the DFU 1.1 spec):
#include <libusb-1.0/libusb.h> #include <stdint.h> #include <stdio.h> #include <endian.h> // DFU Functional Descriptor structure from the DFU 1.1 specification typedef struct { uint8_t bLength; // Size of this descriptor (always 9 bytes) uint8_t bDescriptorType; // DFU descriptor type (0x21) uint8_t bDescriptorSubtype;// Run-Time DFU subtype (0x01) uint16_t bmAttributes; // Device DFU capabilities (bits 0-4) uint16_t wDetachTimeOut; // Timeout in ms before detaching to DFU mode uint16_t wTransferSize; // Maximum DFU data transfer size uint16_t bcdDFUVersion; // DFU spec version (0x0110 for v1.1) } dfu_functional_descriptor;
Then, the code to locate and parse the descriptor:
int main() { libusb_context *ctx = NULL; libusb_device_handle *dev_handle = NULL; int r; // Initialize libusb context r = libusb_init(&ctx); if (r < 0) { fprintf(stderr, "Failed to initialize libusb: %s\n", libusb_strerror(r)); return 1; } // Replace with your device's actual VID/PID dev_handle = libusb_open_device_with_vid_pid(ctx, 0x1234, 0x5678); if (!dev_handle) { fprintf(stderr, "Could not open target device\n"); libusb_exit(ctx); return 1; } // Fetch the active configuration descriptor struct libusb_config_descriptor *config = NULL; r = libusb_get_active_config_descriptor(libusb_get_device(dev_handle), &config); if (r < 0) { fprintf(stderr, "Failed to get config descriptor: %s\n", libusb_strerror(r)); libusb_close(dev_handle); libusb_exit(ctx); return 1; } // Iterate through interfaces to find the DFU interface for (int i = 0; i < config->bNumInterfaces; i++) { const struct libusb_interface *iface = &config->interface[i]; for (int j = 0; j < iface->num_altsetting; j++) { const struct libusb_interface_descriptor *iface_desc = &iface->altsetting[j]; // Check if this is a DFU interface (Class 0xFE, Subclass 0x01) if (iface_desc->bInterfaceClass == 0xFE && iface_desc->bInterfaceSubClass == 0x01) { printf("Found DFU interface\n"); // Scan the interface's extra data for the Functional Descriptor const uint8_t *extra = iface_desc->extra; int extra_len = iface_desc->extra_length; while (extra_len > 0) { uint8_t desc_len = extra[0]; uint8_t desc_type = extra[1]; // Match the DFU Functional Descriptor (type 0x21, correct length) if (desc_type == 0x21 && desc_len == sizeof(dfu_functional_descriptor)) { const dfu_functional_descriptor *dfu_desc = (const dfu_functional_descriptor *)extra; // Print parsed details (convert little-endian to host byte order) printf("\nDFU Functional Descriptor Details:\n"); printf("- Capabilities (bmAttributes): 0x%04X\n", le16toh(dfu_desc->bmAttributes)); printf("- Detach Timeout: %d ms\n", le16toh(dfu_desc->wDetachTimeOut)); printf("- Max Transfer Size: %d bytes\n", le16toh(dfu_desc->wTransferSize)); printf("- DFU Spec Version: %d.%d\n", (dfu_desc->bcdDFUVersion >> 8) & 0xFF, dfu_desc->bcdDFUVersion & 0xFF); } // Move to the next descriptor in the extra data buffer extra += desc_len; extra_len -= desc_len; } } } } // Cleanup resources libusb_free_config_descriptor(config); libusb_close(dev_handle); libusb_exit(ctx); return 0; }
Key Tips for This Example:
- Swap
0x1234and0x5678with your device’s actual Vendor ID and Product ID. - Use
le16toh()to convert USB’s little-endian values to your host system’s byte order (critical for cross-platform compatibility). - DFU interfaces are identified by
bInterfaceClass = 0xFE(Application Specific) andbInterfaceSubClass = 0x01(DFU). - Class-specific descriptors like this one are stored in the interface’s
extradata buffer.
内容的提问来源于stack exchange,提问作者Monku

