Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix detach request callback being treated as manifest request callback #41

Merged
merged 6 commits into from
Sep 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/dapboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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);

Expand Down
66 changes: 54 additions & 12 deletions src/dfu.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/dfu.h>

#include "dfu.h"
#include "config.h"
#include "usb_conf.h"
#include "dfu.h"
#include "dfu_defs.h"
#include "target.h"
#include "dapboot.h"
Expand All @@ -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_WILL_DETACH ),
(DFU_WILL_DETACH ? 0 : USB_DFU_MANIFEST_TOLERANT) |
(DFU_WILL_DETACH ? USB_DFU_WILL_DETACH : 0)),
.wDetachTimeout = 255,
.wTransferSize = TARGET_DFU_WTRANSFERSIZE,
.bcdDFUVersion = 0x0110,
Expand All @@ -50,11 +51,13 @@ static enum dfu_state current_dfu_state;
static enum dfu_status current_dfu_status;
static size_t current_dfu_offset;

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;

Expand Down Expand Up @@ -90,7 +93,7 @@ static inline void dfu_set_status(enum dfu_status status) {
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);
}

Expand All @@ -106,7 +109,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 */
Expand Down Expand Up @@ -144,11 +146,24 @@ 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);
/* 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 */
}
}

#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
Expand Down Expand Up @@ -182,11 +197,18 @@ dfu_control_class_request(usbd_device *usbd_dev,
break;
}
case STATE_DFU_MANIFEST_SYNC: {
if (validate_application()) {
/* 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);
} else {
/* 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;
} else {
dfu_set_status(DFU_STATUS_ERR_FIRMWARE);
}
break;
}
Expand Down Expand Up @@ -320,6 +342,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;

Expand All @@ -328,16 +354,32 @@ 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 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;
}

/* 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;
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;
Expand Down
9 changes: 7 additions & 2 deletions src/dfu.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/dfu.h>

// 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);
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_detach_request,
ManifestationCallback on_manifest_request,
StateChangeCallback on_state_change,
StatusChangeCallback on_status_change);

Expand Down
2 changes: 1 addition & 1 deletion src/usb_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@

#include <libopencm3/usb/dfu.h>
#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,
Expand Down