static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
-static struct {
+struct prog_info {
byte buf[sizeof(usbd_control_buffer)];
- u16 len;
- u32 addr;
u16 blocknum;
-} prog;
+ u16 len;
+};
+
+static struct prog_info prog_page;
+
+// The first page of application code is programmed last,
+// so that we are able to detect incompletely uploaded firmware.
+static struct prog_info prog_header;
static char usb_serial_number[13];
enum usb_string {
STR_MANUFACTURER = 1,
- STR_DEVICE,
+ STR_PRODUCT,
STR_SERIAL,
};
static const char *usb_strings[] = {
BOOTLOADER_MFG_NAME,
- BOOTLOADER_DEV_NAME,
+ BOOTLOADER_PROD_NAME,
usb_serial_number,
};
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = BOOTLOADER_MFG_ID,
- .idProduct = BOOTLOADER_DEV_ID,
- .bcdDevice = BOOTLOADER_DEV_VERSION,
+ .idProduct = BOOTLOADER_PROD_ID,
+ .bcdDevice = BOOTLOADER_PROD_VERSION,
.iManufacturer = STR_MANUFACTURER,
- .iProduct = STR_DEVICE,
+ .iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIAL,
.bNumConfigurations = 1,
};
}
}
+static void program_page(struct prog_info *prog, bool need_erase)
+{
+ flash_unlock();
+ u32 baseaddr = BOOTLOADER_APP_START + prog->blocknum * dfu_function.wTransferSize;
+ DEBUG("DFU: Block %u -> %08x\n", prog->blocknum, (uint) baseaddr);
+ if (need_erase)
+ flash_erase_page(baseaddr);
+ for (uint i = 0; i < prog->len; i += 2) {
+ u16 data = *(u16 *)(prog->buf + i);
+ flash_program_half_word(baseaddr + i, data);
+ }
+ flash_lock();
+}
+
static void usbdfu_getstatus_complete(usbd_device *usbd_dev UNUSED, struct usb_setup_data *req UNUSED)
{
switch (usbdfu_state) {
- case STATE_DFU_DNBUSY: {
- flash_unlock();
- u32 baseaddr = BOOTLOADER_APP_START + prog.blocknum * dfu_function.wTransferSize;
- DEBUG("DFU: Block %u -> %08x\n", prog.blocknum, (uint) baseaddr);
- flash_erase_page(baseaddr);
- for (uint i = 0; i < prog.len; i += 2) {
- u16 *dat = (u16 *)(prog.buf + i);
- flash_program_half_word(baseaddr + i, *dat);
- }
- flash_lock();
+ case STATE_DFU_DNBUSY:
+ if (prog_page.blocknum == 0) {
+ // The first block will be programmed last
+ prog_header = prog_page;
+ flash_unlock();
+ flash_erase_page(BOOTLOADER_APP_START);
+ flash_lock();
+ } else
+ program_page(&prog_page, true);
usbdfu_state = STATE_DFU_DNLOAD_IDLE;
return;
- }
case STATE_DFU_MANIFEST:
+ // At the very end, program the first page
+ program_page(&prog_header, false);
/* USB device must detach, we just reset... */
scb_reset_system();
return; /* Will never return. */
if ((req->bmRequestType & 0x7F) != 0x21)
return USBD_REQ_NOTSUPP; /* Only accept class request. */
- DEBUG("DFU: req %02x in state %d\n", req->bRequest, usbdfu_state);
+ DEBUG("DFU: Request %02x in state %d\n", req->bRequest, usbdfu_state);
switch (req->bRequest) {
case DFU_DNLOAD:
usbdfu_state = STATE_DFU_MANIFEST_SYNC;
} else {
/* Copy download data for use on GET_STATUS. */
- prog.blocknum = req->wValue;
- prog.len = *len;
- memcpy(prog.buf, *buf, *len);
+ prog_page.blocknum = req->wValue;
+ prog_page.len = *len;
+ memcpy(prog_page.buf, *buf, *len);
usbdfu_state = STATE_DFU_DNLOAD_SYNC;
}
return USBD_REQ_HANDLED;
// Simulate USB disconnect
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12);
gpio_clear(GPIOA, GPIO11 | GPIO12);
- for (uint i=0; i<1000; i++) {
+ for (uint i=0; i<100; i++) {
while (!systick_get_countflag())
;
}
u32 pc = app[1];
DEBUG("DFU: Starting application (sp=%08x, pc=%08x)\n", (uint) sp, (uint) pc);
- if (sp & 0x2ffe0000 != 0x20000000) {
+ if ((sp & 0x2ffe0000) != 0x20000000) {
DEBUG("DFU: Suspicious SP, refusing to start\n");
goto restart;
}