]> mj.ucw.cz Git - home-hw.git/commitdiff
DFU: Flash first page last
authorMartin Mares <mj@ucw.cz>
Mon, 24 Feb 2020 14:58:22 +0000 (15:58 +0100)
committerMartin Mares <mj@ucw.cz>
Mon, 24 Feb 2020 14:58:22 +0000 (15:58 +0100)
bsb/bootloader/config.h
lib/dfu-bootloader.c

index 232fd4a8b73c0bd6e89499f327be9ffdc13b5954..6900748062bbd1157e67a46a5bd57476a2cd405f 100644 (file)
@@ -18,7 +18,7 @@
 #define BOOTLOADER_DEBUG
 #define BOOTLOADER_APP_START 0x08002000
 #define BOOTLOADER_MFG_ID 0x4242
-#define BOOTLOADER_DEV_ID 0x0004
-#define BOOTLOADER_DEV_VERSION 0x0100
+#define BOOTLOADER_PROD_ID 0x0004
+#define BOOTLOADER_PROD_VERSION 0x0100
 #define BOOTLOADER_MFG_NAME "United Computer Wizards"
-#define BOOTLOADER_DEV_NAME "BSB Interface (boot-loader)"
+#define BOOTLOADER_PROD_NAME "BSB Interface (boot-loader)"
index 4f792ce085f20be89ab353461691ecdfdbc200c9..ce01a41c5173872c6c4dd45ec738a6e360fd8caa 100644 (file)
@@ -35,24 +35,29 @@ byte usbd_control_buffer[1024];
 
 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,
 };
 
@@ -65,10 +70,10 @@ const struct usb_device_descriptor dev = {
        .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,
 };
@@ -128,23 +133,37 @@ static byte usbdfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout)
        }
 }
 
+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. */
@@ -162,7 +181,7 @@ static enum usbd_request_return_codes usbdfu_control_request(usbd_device *usbd_d
        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:
@@ -170,9 +189,9 @@ static enum usbd_request_return_codes usbdfu_control_request(usbd_device *usbd_d
                        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;
@@ -273,7 +292,7 @@ int main(void)
        // 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())
                        ;
        }
@@ -302,7 +321,7 @@ restart: ;
        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;
        }