Skip to content

Commit cf8eb38

Browse files
authored
Merge pull request #41 from twelho/fix-manifest-reset
Overhaul manifest/detach handling to more closely match the spec. The new DFU_WILL_DETACH option (default enabled) controls whether the bootloader will automatically detach after manifestation completes or if it will return to the IDLE state. Detect USB bus resets and trigger detach/re-enumeration as if receiving a DFU detach command.
2 parents 29fdafe + 178d909 commit cf8eb38

4 files changed

Lines changed: 64 additions & 20 deletions

File tree

src/dapboot.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@ static inline void __set_MSP(uint32_t topOfMainStack) {
3232
}
3333

3434
bool validate_application(void) {
35-
if (((uint32_t)(APP_INITIAL_STACK) & 0x2FFE0000) == 0x20000000) {
36-
return true;
37-
}
38-
return false;
35+
return ((uint32_t)(APP_INITIAL_STACK) & 0x2FFE0000) == 0x20000000;
3936
}
4037

4138
static void jump_to_application(void) __attribute__ ((noreturn));
@@ -75,7 +72,7 @@ int main(void) {
7572
}
7673

7774
usbd_device* usbd_dev = usb_setup();
78-
dfu_setup(usbd_dev, &target_manifest_app, NULL, NULL);
75+
dfu_setup(usbd_dev, validate_application, NULL, NULL);
7976
webusb_setup(usbd_dev);
8077
winusb_setup(usbd_dev);
8178

src/dfu.c

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
#include <libopencm3/usb/usbd.h>
2525
#include <libopencm3/usb/dfu.h>
2626

27-
#include "dfu.h"
2827
#include "config.h"
2928
#include "usb_conf.h"
29+
#include "dfu.h"
3030
#include "dfu_defs.h"
3131
#include "target.h"
3232
#include "dapboot.h"
@@ -40,7 +40,8 @@ const struct usb_dfu_descriptor dfu_function = {
4040
.bDescriptorType = DFU_FUNCTIONAL,
4141
.bmAttributes = ((DFU_DOWNLOAD_AVAILABLE ? USB_DFU_CAN_DOWNLOAD : 0) |
4242
(DFU_UPLOAD_AVAILABLE ? USB_DFU_CAN_UPLOAD : 0) |
43-
USB_DFU_WILL_DETACH ),
43+
(DFU_WILL_DETACH ? 0 : USB_DFU_MANIFEST_TOLERANT) |
44+
(DFU_WILL_DETACH ? USB_DFU_WILL_DETACH : 0)),
4445
.wDetachTimeout = 255,
4546
.wTransferSize = TARGET_DFU_WTRANSFERSIZE,
4647
.bcdDFUVersion = 0x0110,
@@ -50,11 +51,13 @@ static enum dfu_state current_dfu_state;
5051
static enum dfu_status current_dfu_status;
5152
static size_t current_dfu_offset;
5253

54+
static bool manifestation_complete = false;
55+
5356
static uint8_t dfu_download_buffer[USB_CONTROL_BUF_SIZE];
5457
static size_t dfu_download_size;
5558

5659
/* User callbacks */
57-
static GenericCallback dfu_manifest_request_callback = NULL;
60+
static ManifestationCallback dfu_manifest_request_callback = NULL;
5861
static StateChangeCallback dfu_state_change_callback = NULL;
5962
static StatusChangeCallback dfu_status_change_callback = NULL;
6063

@@ -90,7 +93,7 @@ static inline void dfu_set_status(enum dfu_status status) {
9093
static void dfu_on_download_complete(usbd_device* usbd_dev, struct usb_setup_data* req) {
9194
(void)usbd_dev;
9295
(void)req;
93-
96+
9497
dfu_set_state(STATE_DFU_MANIFEST_SYNC);
9598
}
9699

@@ -106,7 +109,6 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data
106109
(void)usbd_dev;
107110
(void)req;
108111

109-
110112
if (DFU_PATCH_VECTORS && current_dfu_offset == 0) {
111113
if (dfu_download_size < offsetof(vector_table_t, reserved_x001c[1])) {
112114
/* Can't handle splitting the vector table right now */
@@ -144,11 +146,24 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data
144146
static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) {
145147
(void)usbd_dev;
146148
(void)req;
149+
147150
if (dfu_manifest_request_callback) {
148-
dfu_manifest_request_callback();
149-
} else {
150-
dfu_set_status(DFU_STATUS_ERR_UNKNOWN);
151+
/* The manifestation callback returns a boolean indicating if it succeeded */
152+
if (dfu_manifest_request_callback()) {
153+
manifestation_complete = true;
154+
dfu_set_state(STATE_DFU_MANIFEST_SYNC);
155+
} else {
156+
dfu_set_status(DFU_STATUS_ERR_FIRMWARE);
157+
return; /* Avoid resetting on error */
158+
}
151159
}
160+
161+
#if DFU_WILL_DETACH
162+
/* DFU_WILL_DETACH being enabled equates to transitioning to the dfuMANIFEST-WAIT-RESET state,
163+
* which combined with bitWillDetach being set with DFU_WILL_DETACH means that the device should
164+
* generate a detach-attach sequence and enter the application, i.e. reset itself, here */
165+
dfu_on_detach_complete(NULL, NULL);
166+
#endif
152167
}
153168

154169
static enum usbd_request_return_codes
@@ -182,11 +197,18 @@ dfu_control_class_request(usbd_device *usbd_dev,
182197
break;
183198
}
184199
case STATE_DFU_MANIFEST_SYNC: {
185-
if (validate_application()) {
200+
/* According to the DFU spec the dfuMANIFEST-SYNC state is entered twice,
201+
* once after the download completes, and again after manifestation if
202+
* the device is manifestation tolerant (DFU_WILL_DETACH == 0) */
203+
if (manifestation_complete) {
204+
/* Only enter idle state after manifestation has completed successfully */
205+
manifestation_complete = false;
206+
dfu_set_state(STATE_DFU_IDLE);
207+
} else {
208+
/* Perform manifestation after download as described in the
209+
* spec regardless of if DFU_WILL_DETACH is enabled or not */
186210
dfu_set_state(STATE_DFU_MANIFEST);
187211
*complete = &dfu_on_manifest_request;
188-
} else {
189-
dfu_set_status(DFU_STATUS_ERR_FIRMWARE);
190212
}
191213
break;
192214
}
@@ -320,6 +342,10 @@ dfu_control_class_request(usbd_device *usbd_dev,
320342
return status;
321343
}
322344

345+
/* Track dfu enumeration status to distinguish between the first USB
346+
reset after bootup versus a subsequent USB reset and re-enumeration */
347+
static bool dfu_enumerated = false;
348+
323349
static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) {
324350
(void)wValue;
325351

@@ -328,16 +354,32 @@ static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) {
328354
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
329355
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
330356
dfu_control_class_request);
357+
358+
dfu_enumerated = true;
359+
}
360+
361+
static void dfu_on_usb_reset(void) {
362+
/* Ignore all USB resets until the DFU control callback has been registered, since
363+
* reset callback will fire once as the USB connection is established. Without this
364+
* the target enters a reset loop when trying to enter the bootloader. */
365+
if (!dfu_enumerated) {
366+
return;
367+
}
368+
369+
/* Perform a DFU detach (which resets the target), this enables issuing a USB bus
370+
* reset as an alternative means to submitting a DFU_DETACH command post-download. */
371+
dfu_on_detach_complete(NULL, NULL);
331372
}
332373

333374
void dfu_setup(usbd_device* usbd_dev,
334-
GenericCallback on_manifest_request,
375+
ManifestationCallback on_manifest_request,
335376
StateChangeCallback on_state_change,
336377
StatusChangeCallback on_status_change) {
337378
dfu_manifest_request_callback = on_manifest_request;
338379
dfu_state_change_callback = on_state_change;
339380
dfu_status_change_callback = on_status_change;
340381

382+
usbd_register_reset_callback(usbd_dev, dfu_on_usb_reset);
341383
usbd_register_set_config_callback(usbd_dev, dfu_set_config);
342384
current_dfu_state = STATE_DFU_IDLE;
343385
current_dfu_status = DFU_STATUS_OK;

src/dfu.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,19 @@
2222
#include <libopencm3/usb/usbd.h>
2323
#include <libopencm3/usb/dfu.h>
2424

25+
// For WebUSB compatibility
26+
#ifndef DFU_WILL_DETACH
27+
#define DFU_WILL_DETACH 1
28+
#endif
29+
2530
extern const struct usb_dfu_descriptor dfu_function;
2631

27-
typedef void (*GenericCallback)(void);
32+
typedef bool (*ManifestationCallback)(void);
2833
typedef void (*StateChangeCallback)(enum dfu_state);
2934
typedef void (*StatusChangeCallback)(enum dfu_status);
3035

3136
extern void dfu_setup(usbd_device* usbd_dev,
32-
GenericCallback on_detach_request,
37+
ManifestationCallback on_manifest_request,
3338
StateChangeCallback on_state_change,
3439
StatusChangeCallback on_status_change);
3540

src/usb_conf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222

2323
#include <libopencm3/usb/dfu.h>
2424
#include "target.h"
25-
#include "dfu.h"
2625
#include "webusb.h"
2726

2827
#include "config.h"
2928
#include "usb_conf.h"
29+
#include "dfu.h"
3030

3131
static const struct usb_device_descriptor dev = {
3232
.bLength = USB_DT_DEVICE_SIZE,

0 commit comments

Comments
 (0)