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;
5051static enum dfu_status current_dfu_status ;
5152static size_t current_dfu_offset ;
5253
54+ static bool manifestation_complete = false;
55+
5356static uint8_t dfu_download_buffer [USB_CONTROL_BUF_SIZE ];
5457static 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 ;
5861static StateChangeCallback dfu_state_change_callback = NULL ;
5962static StatusChangeCallback dfu_status_change_callback = NULL ;
6063
@@ -90,7 +93,7 @@ static inline void dfu_set_status(enum dfu_status status) {
9093static 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
144146static 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
154169static 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+
323349static 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
333374void 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 ;
0 commit comments