forked from stm32duino/Arduino_Core_STM32
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[USB] Support DFU runtime protocol along CDC
This adds support for the DFU runtime protocol, which allows resetting into the bootloader using a DFU command. This allows e.g. dfu-util to handle the complete firmware upload, including the needed reset. This consists of a number of changes: - An extra interface is added to the USB configuration descriptor. This descriptor has two parts (interface descriptor and functional descriptor) which together indicate to a host that this device supports DFU. - Control packets to this new interface are detected by the CDC code an forwarded to a new USBD_DFU_Runtime_Control() function. - This new function handles the DFU GET_STATE, GET_STATUS and DFU_DETACH commands. The former are optional, but simple enough, the latter is mandatory and handles resetting into the bootloader. - The CDC device descriptor is changed to become a composite device (CDC and DFU). This allows operating systems (in particular Windows, Linux did not really need this) to identify two different subdevices, and install different drivers for each (on Windows, this is serusb for the CDC part and WinUSB/libusb for the DFU part). Without this, dfu-util on Windows could not access the DFU commands when the serial driver was loaded. Because the CDC functionality already exposes two interfaces (which together form a single serial port), an IAD (Interface Association Descriptor) is inserted before these interfaces to group them together in a single subdevice. No IAD is needed for the DFU interface, since it is just a single interface. To become a composite device, the device class must be changed from CDC to a composite device class. This was originally class 0/0/0, but together with the IAD, a new EF/2/1 deviceclass was also introduced, which is used now. Note that this only adds descriptors and a command handler on the default control endpoint, so no extra (scarce) endpoints are used by this, just a bit of memory. This commit is still a bit rough, because: - The DFU descriptors and code are now pulled in directly by the CDC code (and HID is not supported yet). Ideally, there should be some kind of pluggable USB library where different interfaces can be registered independent of each other (see also stm32duino#687). - The interface number is hardcoded in the DFU descriptor. - The reset to bootloader happens immediately, while it might be better to wait a short while to allow the current USB transaction to complete. - DFU support is unconditionally advertised, while not all boards might support DFU.
- Loading branch information
1 parent
59e0103
commit 1173c3d
Showing
5 changed files
with
231 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
/* Define to prevent recursive inclusion -------------------------------------*/ | ||
#ifndef __USB_DFU_RUNTIME_H | ||
#define __USB_DFU_RUNTIME_H | ||
|
||
#include <bootloader.h> | ||
|
||
/**************************************************/ | ||
/* DFU Requests DFU states */ | ||
/**************************************************/ | ||
#define APP_STATE_IDLE 0U | ||
#define APP_STATE_DETACH 1U | ||
#define DFU_STATE_IDLE 2U | ||
#define DFU_STATE_DNLOAD_SYNC 3U | ||
#define DFU_STATE_DNLOAD_BUSY 4U | ||
#define DFU_STATE_DNLOAD_IDLE 5U | ||
#define DFU_STATE_MANIFEST_SYNC 6U | ||
#define DFU_STATE_MANIFEST 7U | ||
#define DFU_STATE_MANIFEST_WAIT_RESET 8U | ||
#define DFU_STATE_UPLOAD_IDLE 9U | ||
#define DFU_STATE_ERROR 10U | ||
|
||
/**************************************************/ | ||
/* DFU errors */ | ||
/**************************************************/ | ||
#define DFU_ERROR_NONE 0x00U | ||
#define DFU_ERROR_TARGET 0x01U | ||
#define DFU_ERROR_FILE 0x02U | ||
#define DFU_ERROR_WRITE 0x03U | ||
#define DFU_ERROR_ERASE 0x04U | ||
#define DFU_ERROR_CHECK_ERASED 0x05U | ||
#define DFU_ERROR_PROG 0x06U | ||
#define DFU_ERROR_VERIFY 0x07U | ||
#define DFU_ERROR_ADDRESS 0x08U | ||
#define DFU_ERROR_NOTDONE 0x09U | ||
#define DFU_ERROR_FIRMWARE 0x0AU | ||
#define DFU_ERROR_VENDOR 0x0BU | ||
#define DFU_ERROR_USB 0x0CU | ||
#define DFU_ERROR_POR 0x0DU | ||
#define DFU_ERROR_UNKNOWN 0x0EU | ||
#define DFU_ERROR_STALLEDPKT 0x0FU | ||
|
||
typedef enum { | ||
DFU_DETACH = 0U, | ||
DFU_DNLOAD, | ||
DFU_UPLOAD, | ||
DFU_GETSTATUS, | ||
DFU_CLRSTATUS, | ||
DFU_GETSTATE, | ||
DFU_ABORT | ||
} DFU_RequestTypeDef; | ||
|
||
#define DFU_DESCRIPTOR_TYPE 0x21U | ||
|
||
// Device will detach by itself (alternative is that the host sends a | ||
// USB reset within DETACH_TIMEOUT). | ||
#define DFU_RT_ATTR_WILL_DETACH 0x08U | ||
// Device is still accessible on USB after flashing (manifestation). | ||
// Probably not so relevant in runtime mode | ||
#define DFU_RT_ATTR_MANIFESTATION_TOLERANT 0x04U | ||
#define DFU_RT_ATTR_CAN_UPLOAD 0x02U | ||
#define DFU_RT_ATTR_CAN_DNLOAD 0x01U | ||
|
||
// Of these, only WILL_DETACH is relevant at runtime, but specify | ||
// CAN_UPLOAD and CAN_DNLOAD too, just in case there is a tool that | ||
// somehow checks these before resetting. | ||
#define DFU_RT_ATTRS DFU_RT_ATTR_WILL_DETACH \ | ||
| DFU_RT_ATTR_CAN_UPLOAD | DFU_RT_ATTR_CAN_DNLOAD | ||
|
||
// Detach timeout is only relevant when ATTR_WILL_DETACH is unset | ||
#define DFU_RT_DETACH_TIMEOUT 0 | ||
// This should be only relevant for actual firmware uploads (the actual | ||
// value is read from the bootloader after reset), but specify a | ||
// conservative value here in case any tool fails to reread the value | ||
// after reset. | ||
// The max packet size for EP0 control transfers is specified in the | ||
// device descriptor. | ||
#define DFU_RT_TRANSFER_SIZE 64 | ||
#define DFU_RT_DFU_VERSION 0x0101 // DFU 1.1 | ||
|
||
#define DFU_RT_IFACE_NUM 2 // XXX: Hardcoded | ||
|
||
#define DFU_RT_IFACE_DESC_SIZE 18U | ||
#define DFU_RT_IFACE_DESC \ | ||
/*DFU Runtime interface descriptor*/ \ | ||
0x09, /* bLength: Endpoint Descriptor size */ \ | ||
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */ \ | ||
DFU_RT_IFACE_NUM, /* bInterfaceNumber: Number of Interface */ \ | ||
0x00, /* bAlternateSetting: Alternate setting */ \ | ||
0x00, /* bNumEndpoints: no endpoints used (only control endpoint) */ \ | ||
0xFE, /* bInterfaceClass: Application Specific */ \ | ||
0x01, /* bInterfaceSubClass: Device Firmware Upgrade Code*/ \ | ||
0x01, /* bInterfaceProtocol: Runtime Protocol*/ \ | ||
/* TODO: Put a meaningful string here, which shows up in the Windows * */ \ | ||
/* device manager when no driver is installed yet. */ \ | ||
0x00, /* iInterface: */ \ | ||
\ | ||
/*DFU Runtime Functional Descriptor*/ \ | ||
0x09, /* bFunctionLength */ \ | ||
DFU_DESCRIPTOR_TYPE, /* bDescriptorType: DFU Functional */ \ | ||
DFU_RT_ATTRS, /* bmAttributes: DFU Attributes */ \ | ||
LOBYTE(DFU_RT_DETACH_TIMEOUT), /* wDetachTimeout */ \ | ||
HIBYTE(DFU_RT_DETACH_TIMEOUT), \ | ||
LOBYTE(DFU_RT_TRANSFER_SIZE), /* wTransferSize */ \ | ||
HIBYTE(DFU_RT_TRANSFER_SIZE), \ | ||
LOBYTE(DFU_RT_DFU_VERSION), /* bcdDFUVersion */ \ | ||
HIBYTE(DFU_RT_DFU_VERSION) | ||
|
||
/** | ||
* @brief USBD_DFU_Runtime_Control | ||
* Manage the DFU interface control requests | ||
* @param bRequest: Command code from request | ||
* @param wValue: Value from request | ||
* @param data: Buffer for result | ||
* @param length: Number of data to be sent (in bytes) | ||
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL | ||
*/ | ||
static int8_t USBD_DFU_Runtime_Control(uint8_t bRequest, uint16_t wValue, uint8_t *data, uint16_t len) | ||
{ | ||
UNUSED(wValue); | ||
switch (bRequest) { | ||
case DFU_GETSTATUS: | ||
if (len != 6) { | ||
return (USBD_FAIL); | ||
} | ||
|
||
data[0] = DFU_ERROR_NONE; | ||
// Minimum delay until next GET_STATUS | ||
data[1] = data[2] = data[3] = 0; | ||
data[4] = APP_STATE_IDLE; | ||
// State string descriptor | ||
data[5] = 0; | ||
|
||
return (USBD_OK); | ||
|
||
case DFU_DETACH: | ||
scheduleBootloaderReset(); | ||
return (USBD_OK); | ||
|
||
case DFU_GETSTATE: | ||
if (len != 1) { | ||
return (USBD_FAIL); | ||
} | ||
data[0] = APP_STATE_IDLE; | ||
return (USBD_OK); | ||
|
||
default: | ||
return (USBD_FAIL); | ||
} | ||
} | ||
|
||
#endif // __USB_DFU_RUNTIME_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters