From 7f8279d380018174d4fcc317d66b9465e899237d Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Mon, 6 Sep 2021 22:45:43 +0300 Subject: [PATCH 1/6] Fix detach request callback being treated as manifest request callback This prevents the bootloader from detaching prematurely after download and makes it compliant with the DFU 1.1 spec. Many flashing routines do not expect an automatic detach, so I've assumed the DFU spec option to return to idle after download as what was originally intended (an automatic reset can still be achieved by passing the -R flag to dfu-util for example), in other words the bootloader was intended to have the USB_DFU_MANIFEST_TOLERANT attribute since there is no STATE_DFU_MANIFEST_WAIT_RESET in the code. --- src/dapboot.c | 2 +- src/dfu.c | 8 +++----- src/dfu.h | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/dapboot.c b/src/dapboot.c index 48d49ec..3c415fd 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -75,7 +75,7 @@ int main(void) { } usbd_device* usbd_dev = usb_setup(); - dfu_setup(usbd_dev, &target_manifest_app, NULL, NULL); + dfu_setup(usbd_dev, NULL, NULL, NULL); webusb_setup(usbd_dev); winusb_setup(usbd_dev); diff --git a/src/dfu.c b/src/dfu.c index 9ab88ce..eb843db 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -40,7 +40,7 @@ const struct usb_dfu_descriptor dfu_function = { .bDescriptorType = DFU_FUNCTIONAL, .bmAttributes = ((DFU_DOWNLOAD_AVAILABLE ? USB_DFU_CAN_DOWNLOAD : 0) | (DFU_UPLOAD_AVAILABLE ? USB_DFU_CAN_UPLOAD : 0) | - USB_DFU_WILL_DETACH ), + USB_DFU_MANIFEST_TOLERANT), .wDetachTimeout = 255, .wTransferSize = TARGET_DFU_WTRANSFERSIZE, .bcdDFUVersion = 0x0110, @@ -106,7 +106,6 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data (void)usbd_dev; (void)req; - if (DFU_PATCH_VECTORS && current_dfu_offset == 0) { if (dfu_download_size < offsetof(vector_table_t, reserved_x001c[1])) { /* Can't handle splitting the vector table right now */ @@ -144,10 +143,9 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; + if (dfu_manifest_request_callback) { dfu_manifest_request_callback(); - } else { - dfu_set_status(DFU_STATUS_ERR_UNKNOWN); } } @@ -183,7 +181,7 @@ dfu_control_class_request(usbd_device *usbd_dev, } case STATE_DFU_MANIFEST_SYNC: { if (validate_application()) { - dfu_set_state(STATE_DFU_MANIFEST); + dfu_set_state(STATE_DFU_IDLE); *complete = &dfu_on_manifest_request; } else { dfu_set_status(DFU_STATUS_ERR_FIRMWARE); diff --git a/src/dfu.h b/src/dfu.h index 9d53520..30e9dbe 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -29,7 +29,7 @@ typedef void (*StateChangeCallback)(enum dfu_state); typedef void (*StatusChangeCallback)(enum dfu_status); extern void dfu_setup(usbd_device* usbd_dev, - GenericCallback on_detach_request, + GenericCallback on_manifest_request, StateChangeCallback on_state_change, StatusChangeCallback on_status_change); From bc98fc40c662a5d2a33a7449c292dee21019d918 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Fri, 10 Sep 2021 23:24:26 +0300 Subject: [PATCH 2/6] Add DFU_WILL_DETACH option for automatic detach/reset and enable it by default Enabling DFU_WILL_DETACH will set bitManifestationTolerant = 0 and bitWillDetach = 1, disabling it will set the opposite. If DFU_WILL_DETACH is set, the target will automatically detach and reset following a successful reprogramming, and if it's not set, the target will enter the dfuIDLE state. Both of these behaviors are described in the DFU 1.1 spec. DFU_WILL_DETACH is enabled by default for WebUSB compatibility, as some platforms will apparently not instruct the target to reset after flashing. --- src/dfu.c | 15 +++++++++++++-- src/dfu.h | 5 +++++ src/usb_conf.c | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index eb843db..aba22a5 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -24,9 +24,9 @@ #include #include -#include "dfu.h" #include "config.h" #include "usb_conf.h" +#include "dfu.h" #include "dfu_defs.h" #include "target.h" #include "dapboot.h" @@ -40,7 +40,8 @@ const struct usb_dfu_descriptor dfu_function = { .bDescriptorType = DFU_FUNCTIONAL, .bmAttributes = ((DFU_DOWNLOAD_AVAILABLE ? USB_DFU_CAN_DOWNLOAD : 0) | (DFU_UPLOAD_AVAILABLE ? USB_DFU_CAN_UPLOAD : 0) | - USB_DFU_MANIFEST_TOLERANT), + (DFU_WILL_DETACH ? 0 : USB_DFU_MANIFEST_TOLERANT) | + (DFU_WILL_DETACH ? USB_DFU_WILL_DETACH : 0)), .wDetachTimeout = 255, .wTransferSize = TARGET_DFU_WTRANSFERSIZE, .bcdDFUVersion = 0x0110, @@ -87,12 +88,14 @@ static inline void dfu_set_status(enum dfu_status status) { current_dfu_status = status; } +#if !DFU_WILL_DETACH static void dfu_on_download_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; dfu_set_state(STATE_DFU_MANIFEST_SYNC); } +#endif static void dfu_on_detach_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; @@ -140,6 +143,7 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data } } +#if !DFU_WILL_DETACH static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; @@ -148,6 +152,7 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data dfu_manifest_request_callback(); } } +#endif static enum usbd_request_return_codes dfu_control_class_request(usbd_device *usbd_dev, @@ -179,6 +184,7 @@ dfu_control_class_request(usbd_device *usbd_dev, *complete = &dfu_on_download_request; break; } +#if !DFU_WILL_DETACH case STATE_DFU_MANIFEST_SYNC: { if (validate_application()) { dfu_set_state(STATE_DFU_IDLE); @@ -188,6 +194,7 @@ dfu_control_class_request(usbd_device *usbd_dev, } break; } +#endif #endif default: { break; @@ -227,7 +234,11 @@ dfu_control_class_request(usbd_device *usbd_dev, memcpy(dfu_download_buffer, *buf, dfu_download_size); dfu_set_state(STATE_DFU_DNLOAD_SYNC); } else { +#if DFU_WILL_DETACH + *complete = &dfu_on_detach_complete; +#else *complete = &dfu_on_download_complete; +#endif } break; } diff --git a/src/dfu.h b/src/dfu.h index 30e9dbe..a8a5e1b 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -22,6 +22,11 @@ #include #include +// For WebUSB compatibility +#ifndef DFU_WILL_DETACH +#define DFU_WILL_DETACH 1 +#endif + extern const struct usb_dfu_descriptor dfu_function; typedef void (*GenericCallback)(void); diff --git a/src/usb_conf.c b/src/usb_conf.c index dc88e6a..591f17e 100644 --- a/src/usb_conf.c +++ b/src/usb_conf.c @@ -22,11 +22,11 @@ #include #include "target.h" -#include "dfu.h" #include "webusb.h" #include "config.h" #include "usb_conf.h" +#include "dfu.h" static const struct usb_device_descriptor dev = { .bLength = USB_DT_DEVICE_SIZE, From da0ec9df402aa41fcd6f11b6b658a5f61cbe8e16 Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Fri, 10 Sep 2021 18:19:59 -0700 Subject: [PATCH 3/6] Adjust DFU_WILL_DETACH behavior Always enter dfuMANIFEST-SYNC, but subsequently reset to manifest if the firmware is valid. Call the target manifest callback if provided, but reset in case the callback is absent or fails to reset the device. --- src/dapboot.c | 2 +- src/dfu.c | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/dapboot.c b/src/dapboot.c index 3c415fd..4c851cf 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -75,7 +75,7 @@ int main(void) { } usbd_device* usbd_dev = usb_setup(); - dfu_setup(usbd_dev, NULL, NULL, NULL); + dfu_setup(usbd_dev, target_manifest_app, NULL, NULL); webusb_setup(usbd_dev); winusb_setup(usbd_dev); diff --git a/src/dfu.c b/src/dfu.c index aba22a5..67fe518 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -88,14 +88,12 @@ static inline void dfu_set_status(enum dfu_status status) { current_dfu_status = status; } -#if !DFU_WILL_DETACH static void dfu_on_download_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; - + dfu_set_state(STATE_DFU_MANIFEST_SYNC); } -#endif static void dfu_on_detach_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; @@ -143,7 +141,7 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data } } -#if !DFU_WILL_DETACH +#if DFU_WILL_DETACH static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; @@ -151,6 +149,10 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data if (dfu_manifest_request_callback) { dfu_manifest_request_callback(); } + + /* Reset and maybe launch the application if the manifest callback + didn't already do it */ + scb_reset_system(); } #endif @@ -184,17 +186,21 @@ dfu_control_class_request(usbd_device *usbd_dev, *complete = &dfu_on_download_request; break; } -#if !DFU_WILL_DETACH case STATE_DFU_MANIFEST_SYNC: { if (validate_application()) { - dfu_set_state(STATE_DFU_IDLE); +#if DFU_WILL_DETACH + /* Manifest by resetting after responding */ + dfu_set_state(STATE_DFU_MANIFEST); *complete = &dfu_on_manifest_request; +#else + /* Return to the idle state and await commands */ + dfu_set_state(STATE_DFU_IDLE); +#endif } else { dfu_set_status(DFU_STATUS_ERR_FIRMWARE); } break; } -#endif #endif default: { break; @@ -234,11 +240,7 @@ dfu_control_class_request(usbd_device *usbd_dev, memcpy(dfu_download_buffer, *buf, dfu_download_size); dfu_set_state(STATE_DFU_DNLOAD_SYNC); } else { -#if DFU_WILL_DETACH - *complete = &dfu_on_detach_complete; -#else *complete = &dfu_on_download_complete; -#endif } break; } From 9bf8c4d8f56a3bf572a9db998a807e34216195f6 Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Fri, 10 Sep 2021 18:22:24 -0700 Subject: [PATCH 4/6] Detect USB requests and reset or enter error state Per the DFU spec, a USB reset should cause the device to either reenumerate as the application or enter an error state if invalid. Register a USB reset callback to detect and handle USB resets, taking care not to trigger the first time the bootloader is enumerated. Run the manifest callback if present when manifesting via USB reset. --- src/dfu.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/dfu.c b/src/dfu.c index 67fe518..e376afc 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -331,6 +331,10 @@ dfu_control_class_request(usbd_device *usbd_dev, return status; } +/* Track dfu enumeration status to distinguish between the first USB + reset after bootup versus a subsequent USB reset and re-enumeration */ +static bool dfu_enumerated = false; + static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) { (void)wValue; @@ -339,6 +343,35 @@ static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) { USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, dfu_control_class_request); + + dfu_enumerated = true; +} + +static void dfu_on_usb_reset(void) { + /* Ignore the first USB reset after boot, before anyone has had + a chance to do anything to the device; otherwise we'd never + enter DFU mode when the application is valid */ + if (!dfu_enumerated) { + return; + } + + /* On subsequent USB reset requests, either manifest by resetting + or enter the error state if firmware is invalid, per the spec */ + if (validate_application()) { + /* Manifest by resetting after responding */ + dfu_set_state(STATE_DFU_MANIFEST); + + if (dfu_manifest_request_callback) { + dfu_manifest_request_callback(); + } + + /* Reset and launch the application if the manifest callback + didn't already do it */ + scb_reset_system(); + } else { + /* Enter the error state and await further commands */ + dfu_set_status(DFU_STATUS_ERR_FIRMWARE); + } } void dfu_setup(usbd_device* usbd_dev, @@ -349,6 +382,7 @@ void dfu_setup(usbd_device* usbd_dev, dfu_state_change_callback = on_state_change; dfu_status_change_callback = on_status_change; + usbd_register_reset_callback(usbd_dev, dfu_on_usb_reset); usbd_register_set_config_callback(usbd_dev, dfu_set_config); current_dfu_state = STATE_DFU_IDLE; current_dfu_status = DFU_STATUS_OK; From 191a0925a9971389d7aaae236d6a55890d50ed3b Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Fri, 10 Sep 2021 18:52:56 -0700 Subject: [PATCH 5/6] Refine manifest request event criteria If the firmware has been written to at least once, treat a detach/USB reset as an implicit manifestation request and run the manifest request callback. Otherwise treat it as a detach request and simply reset. --- src/dfu.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index e376afc..e75480e 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -51,6 +51,8 @@ static enum dfu_state current_dfu_state; static enum dfu_status current_dfu_status; static size_t current_dfu_offset; +static bool dfu_modified_firmware = false; + static uint8_t dfu_download_buffer[USB_CONTROL_BUF_SIZE]; static size_t dfu_download_size; @@ -131,6 +133,9 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data bool ok = target_flash_program_array(dest, data, dfu_download_size/2); target_flash_lock(); + /* Record that we touched the application firmware */ + dfu_modified_firmware = true; + if (ok) { current_dfu_offset += dfu_download_size; /* We could go back to STATE_DFU_DNLOAD_SYNC, but then @@ -141,7 +146,6 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data } } -#if DFU_WILL_DETACH static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; @@ -154,7 +158,6 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data didn't already do it */ scb_reset_system(); } -#endif static enum usbd_request_return_codes dfu_control_class_request(usbd_device *usbd_dev, @@ -314,7 +317,11 @@ dfu_control_class_request(usbd_device *usbd_dev, #endif case DFU_DETACH: { - *complete = &dfu_on_detach_complete; + if (dfu_modified_firmware) { + *complete = &dfu_on_manifest_request; + } else { + *complete = &dfu_on_detach_complete; + } status = USBD_REQ_HANDLED; break; } @@ -355,14 +362,16 @@ static void dfu_on_usb_reset(void) { return; } - /* On subsequent USB reset requests, either manifest by resetting + /* On subsequent USB reset requests, either reset/manifest or enter the error state if firmware is invalid, per the spec */ if (validate_application()) { - /* Manifest by resetting after responding */ - dfu_set_state(STATE_DFU_MANIFEST); + if (dfu_modified_firmware) { + /* Manifest by resetting after responding */ + dfu_set_state(STATE_DFU_MANIFEST); - if (dfu_manifest_request_callback) { - dfu_manifest_request_callback(); + if (dfu_manifest_request_callback) { + dfu_manifest_request_callback(); + } } /* Reset and launch the application if the manifest callback From 178d90974c9b09687f6115fe767c494d8ff3b8e1 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Sat, 11 Sep 2021 21:24:28 +0300 Subject: [PATCH 6/6] Implement proper manifestation according to the spec The initial behavior of entering dfuMANIFEST-SYNC was incorrect, sorry about that. I've now thoroughly read the DFU spec, which actually states that dfuMANIFEST-SYNC shall be entered *twice* after a download operation if bitMainfestationTolerant = 1 (i.e. DFU_WILL_DETACH == 0), once after the download completes, which begins manifestation by transitioning to dfuMANIFEST, and again after manifestation has completed successfully, and only after that should the transition to dfuIDLE be made. This means that there should be *two* DFU_GETSTATUS calls from the host, and indeed dfu-util does exactly this (verified using Wireshark). Manifesting on USB reset or on detach is not according to spec. It should only happen right after a download operation has finished. For this I've overhauled the dfu_on_manifest_request() function to work as it should with both DFU_WILL_DETACH == 1 and DFU_WILL_DETACH == 0. Validation should actually be part of manifestation, and I've now added it as the manifestation callback (since there is no other manifestation work to be done). The scb_reset_system() call in the USB reset callback is what you need to perform detach-by-USB-reset, but to avoid having random reset calls in multiple places it makes much more sense to call the actual dfu_on_detach_complete() callback which will then perform the reset. The USB reset callback should also not validate, e.g. a blank flash with just the bootloader should not trigger a firmware error status, so I've removed that logic. --- src/dapboot.c | 7 ++--- src/dfu.c | 80 ++++++++++++++++++++++----------------------------- src/dfu.h | 4 +-- 3 files changed, 38 insertions(+), 53 deletions(-) diff --git a/src/dapboot.c b/src/dapboot.c index 4c851cf..551bc71 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -32,10 +32,7 @@ static inline void __set_MSP(uint32_t topOfMainStack) { } bool validate_application(void) { - if (((uint32_t)(APP_INITIAL_STACK) & 0x2FFE0000) == 0x20000000) { - return true; - } - return false; + return ((uint32_t)(APP_INITIAL_STACK) & 0x2FFE0000) == 0x20000000; } static void jump_to_application(void) __attribute__ ((noreturn)); @@ -75,7 +72,7 @@ int main(void) { } usbd_device* usbd_dev = usb_setup(); - dfu_setup(usbd_dev, target_manifest_app, NULL, NULL); + dfu_setup(usbd_dev, validate_application, NULL, NULL); webusb_setup(usbd_dev); winusb_setup(usbd_dev); diff --git a/src/dfu.c b/src/dfu.c index e75480e..8923130 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -51,13 +51,13 @@ static enum dfu_state current_dfu_state; static enum dfu_status current_dfu_status; static size_t current_dfu_offset; -static bool dfu_modified_firmware = false; +static bool manifestation_complete = false; static uint8_t dfu_download_buffer[USB_CONTROL_BUF_SIZE]; static size_t dfu_download_size; /* User callbacks */ -static GenericCallback dfu_manifest_request_callback = NULL; +static ManifestationCallback dfu_manifest_request_callback = NULL; static StateChangeCallback dfu_state_change_callback = NULL; static StatusChangeCallback dfu_status_change_callback = NULL; @@ -133,9 +133,6 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data bool ok = target_flash_program_array(dest, data, dfu_download_size/2); target_flash_lock(); - /* Record that we touched the application firmware */ - dfu_modified_firmware = true; - if (ok) { current_dfu_offset += dfu_download_size; /* We could go back to STATE_DFU_DNLOAD_SYNC, but then @@ -151,12 +148,22 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data (void)req; if (dfu_manifest_request_callback) { - dfu_manifest_request_callback(); + /* The manifestation callback returns a boolean indicating if it succeeded */ + if (dfu_manifest_request_callback()) { + manifestation_complete = true; + dfu_set_state(STATE_DFU_MANIFEST_SYNC); + } else { + dfu_set_status(DFU_STATUS_ERR_FIRMWARE); + return; /* Avoid resetting on error */ + } } - /* Reset and maybe launch the application if the manifest callback - didn't already do it */ - scb_reset_system(); +#if DFU_WILL_DETACH + /* DFU_WILL_DETACH being enabled equates to transitioning to the dfuMANIFEST-WAIT-RESET state, + * which combined with bitWillDetach being set with DFU_WILL_DETACH means that the device should + * generate a detach-attach sequence and enter the application, i.e. reset itself, here */ + dfu_on_detach_complete(NULL, NULL); +#endif } static enum usbd_request_return_codes @@ -190,17 +197,18 @@ dfu_control_class_request(usbd_device *usbd_dev, break; } case STATE_DFU_MANIFEST_SYNC: { - if (validate_application()) { -#if DFU_WILL_DETACH - /* Manifest by resetting after responding */ - dfu_set_state(STATE_DFU_MANIFEST); - *complete = &dfu_on_manifest_request; -#else - /* Return to the idle state and await commands */ + /* According to the DFU spec the dfuMANIFEST-SYNC state is entered twice, + * once after the download completes, and again after manifestation if + * the device is manifestation tolerant (DFU_WILL_DETACH == 0) */ + if (manifestation_complete) { + /* Only enter idle state after manifestation has completed successfully */ + manifestation_complete = false; dfu_set_state(STATE_DFU_IDLE); -#endif } else { - dfu_set_status(DFU_STATUS_ERR_FIRMWARE); + /* Perform manifestation after download as described in the + * spec regardless of if DFU_WILL_DETACH is enabled or not */ + dfu_set_state(STATE_DFU_MANIFEST); + *complete = &dfu_on_manifest_request; } break; } @@ -317,11 +325,7 @@ dfu_control_class_request(usbd_device *usbd_dev, #endif case DFU_DETACH: { - if (dfu_modified_firmware) { - *complete = &dfu_on_manifest_request; - } else { - *complete = &dfu_on_detach_complete; - } + *complete = &dfu_on_detach_complete; status = USBD_REQ_HANDLED; break; } @@ -355,36 +359,20 @@ static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) { } static void dfu_on_usb_reset(void) { - /* Ignore the first USB reset after boot, before anyone has had - a chance to do anything to the device; otherwise we'd never - enter DFU mode when the application is valid */ + /* Ignore all USB resets until the DFU control callback has been registered, since + * reset callback will fire once as the USB connection is established. Without this + * the target enters a reset loop when trying to enter the bootloader. */ if (!dfu_enumerated) { return; } - /* On subsequent USB reset requests, either reset/manifest - or enter the error state if firmware is invalid, per the spec */ - if (validate_application()) { - if (dfu_modified_firmware) { - /* Manifest by resetting after responding */ - dfu_set_state(STATE_DFU_MANIFEST); - - if (dfu_manifest_request_callback) { - dfu_manifest_request_callback(); - } - } - - /* Reset and launch the application if the manifest callback - didn't already do it */ - scb_reset_system(); - } else { - /* Enter the error state and await further commands */ - dfu_set_status(DFU_STATUS_ERR_FIRMWARE); - } + /* Perform a DFU detach (which resets the target), this enables issuing a USB bus + * reset as an alternative means to submitting a DFU_DETACH command post-download. */ + dfu_on_detach_complete(NULL, NULL); } void dfu_setup(usbd_device* usbd_dev, - GenericCallback on_manifest_request, + ManifestationCallback on_manifest_request, StateChangeCallback on_state_change, StatusChangeCallback on_status_change) { dfu_manifest_request_callback = on_manifest_request; diff --git a/src/dfu.h b/src/dfu.h index a8a5e1b..d447b22 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -29,12 +29,12 @@ extern const struct usb_dfu_descriptor dfu_function; -typedef void (*GenericCallback)(void); +typedef bool (*ManifestationCallback)(void); typedef void (*StateChangeCallback)(enum dfu_state); typedef void (*StatusChangeCallback)(enum dfu_status); extern void dfu_setup(usbd_device* usbd_dev, - GenericCallback on_manifest_request, + ManifestationCallback on_manifest_request, StateChangeCallback on_state_change, StatusChangeCallback on_status_change);