4242:0001 Experiments with clock
4242:0002 SSR module
4242:0003 BSB gateway
+4242:0004 BSB gateway bootloader
cafe:cafe KSP Space Alert thermometer
cafe:caff KSP Space Alert accelerometer
ROOT=../..
BINARY=bootloader
-OBJS=main.o
-LIB_OBJS=util-debug.o
+OBJS=
+LIB_OBJS=util-debug.o dfu-bootloader.o
include $(ROOT)/mk/bluepill.mk
#define DEBUG_USART USART1
#define DEBUG_LED_BLUEPILL
+
+// Bootloader settings
+
+#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_MFG_NAME "United Computer Wizards"
+#define BOOTLOADER_DEV_NAME "BSB Interface (boot-loader)"
+++ /dev/null
-/*
- * Bootloader for the BSB Interface
- *
- * (c) 2020 Martin Mareš <mj@ucw.cz>
- *
- * Based on example code from the libopencm3 project, which is
- * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
- *
- * Licensed under the GNU LGPL v3 or any later version.
- */
-
-#include "util.h"
-
-#include <libopencm3/cm3/cortex.h>
-#include <libopencm3/cm3/nvic.h>
-#include <libopencm3/cm3/scb.h>
-#include <libopencm3/cm3/systick.h>
-#include <libopencm3/stm32/rcc.h>
-#include <libopencm3/stm32/gpio.h>
-#include <libopencm3/stm32/flash.h>
-#include <libopencm3/stm32/usart.h>
-#include <libopencm3/stm32/desig.h>
-#include <libopencm3/usb/usbd.h>
-#include <libopencm3/usb/dfu.h>
-
-#include <string.h>
-
-#define DEBUG_BOOTLOADER
-
-#ifdef DEBUG_BOOTLOADER
-#define DEBUG(x...) debug_printf(x)
-#else
-#define DEBUG(x...) do { } while (0)
-#endif
-
-#define APP_ADDRESS 0x08002000
-
-byte usbd_control_buffer[1024];
-
-static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
-
-static struct {
- byte buf[sizeof(usbd_control_buffer)];
- u16 len;
- u32 addr;
- u16 blocknum;
-} prog;
-
-static char usb_serial_number[13];
-
-enum usb_string {
- STR_MANUFACTURER = 1,
- STR_DEVICE,
- STR_SERIAL,
-};
-
-static const char *usb_strings[] = {
- "United Computer Wizards",
- "Boot Loader",
- usb_serial_number,
-};
-
-const struct usb_device_descriptor dev = {
- .bLength = USB_DT_DEVICE_SIZE,
- .bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = 0x0200,
- .bDeviceClass = 0,
- .bDeviceSubClass = 0,
- .bDeviceProtocol = 0,
- .bMaxPacketSize0 = 64,
- .idVendor = 0x4242,
- .idProduct = 0xdead,
- .bcdDevice = 0x0100,
- .iManufacturer = STR_MANUFACTURER,
- .iProduct = STR_DEVICE,
- .iSerialNumber = STR_SERIAL,
- .bNumConfigurations = 1,
-};
-
-const struct usb_dfu_descriptor dfu_function = {
- .bLength = sizeof(struct usb_dfu_descriptor),
- .bDescriptorType = DFU_FUNCTIONAL,
- .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
- .wDetachTimeout = 255,
- .wTransferSize = 1024,
- .bcdDFUVersion = 0x0100,
-};
-
-const struct usb_interface_descriptor iface = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0,
- .bAlternateSetting = 0,
- .bNumEndpoints = 0,
- .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 2,
- .extra = &dfu_function,
- .extralen = sizeof(dfu_function),
-};
-
-const struct usb_interface ifaces[] = {{
- .num_altsetting = 1,
- .altsetting = &iface,
-}};
-
-const struct usb_config_descriptor config = {
- .bLength = USB_DT_CONFIGURATION_SIZE,
- .bDescriptorType = USB_DT_CONFIGURATION,
- .wTotalLength = 0,
- .bNumInterfaces = 1,
- .bConfigurationValue = 1,
- .iConfiguration = 0,
- .bmAttributes = USB_CONFIG_ATTR_DEFAULT, // bus-powered
- .bMaxPower = 50, // multiplied by 2 mA
- .interface = ifaces,
-};
-
-static byte usbdfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout)
-{
- switch (usbdfu_state) {
- case STATE_DFU_DNLOAD_SYNC:
- usbdfu_state = STATE_DFU_DNBUSY;
- *bwPollTimeout = 100;
- return DFU_STATUS_OK;
- case STATE_DFU_MANIFEST_SYNC:
- /* Device will reset when read is complete. */
- usbdfu_state = STATE_DFU_MANIFEST;
- return DFU_STATUS_OK;
- default:
- return DFU_STATUS_OK;
- }
-}
-
-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 = APP_ADDRESS + 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();
- usbdfu_state = STATE_DFU_DNLOAD_IDLE;
- return;
- }
- case STATE_DFU_MANIFEST:
- /* USB device must detach, we just reset... */
- scb_reset_system();
- return; /* Will never return. */
- default:
- return;
- }
-}
-
-static enum usbd_request_return_codes usbdfu_control_request(usbd_device *usbd_dev,
- struct usb_setup_data *req,
- byte **buf,
- u16 *len,
- void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
-{
- 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);
-
- switch (req->bRequest) {
- case DFU_DNLOAD:
- if (len == NULL || *len == 0) {
- 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);
- usbdfu_state = STATE_DFU_DNLOAD_SYNC;
- }
- return USBD_REQ_HANDLED;
- case DFU_CLRSTATUS:
- /* Clear error and return to dfuIDLE. */
- if (usbdfu_state == STATE_DFU_ERROR)
- usbdfu_state = STATE_DFU_IDLE;
- return USBD_REQ_HANDLED;
- case DFU_ABORT:
- /* Abort returns to dfuIDLE state. */
- usbdfu_state = STATE_DFU_IDLE;
- return USBD_REQ_HANDLED;
- case DFU_UPLOAD:
- /* Upload not supported for now. */
- return USBD_REQ_NOTSUPP;
- case DFU_GETSTATUS: {
- u32 bwPollTimeout = 0; /* 24-bit number of milliseconds */
- (*buf)[0] = usbdfu_getstatus(usbd_dev, &bwPollTimeout);
- (*buf)[1] = bwPollTimeout & 0xFF;
- (*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
- (*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
- (*buf)[4] = usbdfu_state;
- (*buf)[5] = 0; /* iString not used here */
- *len = 6;
- *complete = usbdfu_getstatus_complete;
- return USBD_REQ_HANDLED;
- }
- case DFU_GETSTATE:
- /* Return state with no state transition. */
- *buf[0] = usbdfu_state;
- *len = 1;
- return USBD_REQ_HANDLED;
- }
-
- return USBD_REQ_NOTSUPP;
-}
-
-static void usbdfu_set_config(usbd_device *usbd_dev, u16 wValue UNUSED)
-{
- usbd_register_control_callback(
- usbd_dev,
- USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
- USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
- usbdfu_control_request);
-}
-
-static void usbdfu_reset(void)
-{
- usbdfu_state = STATE_DFU_IDLE;
-}
-
-int main(void)
-{
- usbd_device *usbd_dev;
-
- // Flash programming requires running on the internal oscillator
- rcc_clock_setup_in_hsi_out_48mhz();
-
- rcc_periph_clock_enable(RCC_GPIOA);
- rcc_periph_clock_enable(RCC_GPIOC);
- rcc_periph_clock_enable(RCC_USB);
-
- rcc_periph_reset_pulse(RST_GPIOA);
- rcc_periph_reset_pulse(RST_GPIOC);
- rcc_periph_reset_pulse(RST_USB);
-
-#ifdef DEBUG_BOOTLOADER
- rcc_periph_clock_enable(RCC_USART1);
- rcc_periph_reset_pulse(RST_USART1);
- gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
-
- usart_set_baudrate(USART1, 115200);
- usart_set_databits(USART1, 8);
- usart_set_stopbits(USART1, USART_STOPBITS_1);
- usart_set_mode(USART1, USART_MODE_TX);
- usart_set_parity(USART1, USART_PARITY_NONE);
- usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
-
- usart_enable(USART1);
-#endif
-
- // BluePill LED
- gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
- debug_led(1);
-
- // Systick: set to overflow in 1 ms, will use only the overflow flag, no interrupts
- systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000);
- systick_clear();
- systick_counter_enable();
-
- desig_get_unique_id_as_dfu(usb_serial_number);
-
- DEBUG("Entered boot-loader\n");
-
- // 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++) {
- while (!systick_get_countflag())
- ;
- }
-
- DEBUG("DFU: Ready\n");
- debug_led(0);
-
- usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, ARRAY_SIZE(usb_strings), usbd_control_buffer, sizeof(usbd_control_buffer));
- usbd_register_reset_callback(usbd_dev, usbdfu_reset);
- usbd_register_set_config_callback(usbd_dev, usbdfu_set_config);
-
-restart: ;
-
- uint timeout = 5000;
- while (timeout) {
- usbd_poll(usbd_dev);
- if (systick_get_countflag()) {
- timeout--;
- if (!(timeout & 0x3f))
- debug_led_toggle();
- }
- }
-
- volatile u32 *app = (volatile u32 *) APP_ADDRESS;
- u32 sp = app[0];
- u32 pc = app[1];
-
- DEBUG("DFU: Starting application (sp=%08x, pc=%08x)\n", (uint) sp, (uint) pc);
- if (sp & 0x2ffe0000 != 0x20000000) {
- DEBUG("DFU: Suspicious SP, refusing to start\n");
- goto restart;
- }
-
- while (!usart_get_flag(DEBUG_USART, USART_FLAG_TC))
- ;
- debug_led(0);
- cm_disable_interrupts();
-
- /* Set vector table base address. */
- SCB_VTOR = APP_ADDRESS & 0xFFFF;
-
- /* Initialize master stack pointer. */
- asm volatile("msr msp, %0"::"g" (sp));
-
- /* Jump to application. */
- ((void (*)(void)) pc)();
-}
--- /dev/null
+/*
+ * Generic DFU Bootloader
+ *
+ * (c) 2020 Martin Mareš <mj@ucw.cz>
+ *
+ * Based on example code from the libopencm3 project, which is
+ * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * Licensed under the GNU LGPL v3 or any later version.
+ */
+
+#include "util.h"
+
+#include <libopencm3/cm3/cortex.h>
+#include <libopencm3/cm3/nvic.h>
+#include <libopencm3/cm3/scb.h>
+#include <libopencm3/cm3/systick.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/flash.h>
+#include <libopencm3/stm32/usart.h>
+#include <libopencm3/stm32/desig.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/usb/dfu.h>
+
+#include <string.h>
+
+#ifdef BOOTLOADER_DEBUG
+#define DEBUG(x...) debug_printf(x)
+#else
+#define DEBUG(x...) do { } while (0)
+#endif
+
+byte usbd_control_buffer[1024];
+
+static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
+
+static struct {
+ byte buf[sizeof(usbd_control_buffer)];
+ u16 len;
+ u32 addr;
+ u16 blocknum;
+} prog;
+
+static char usb_serial_number[13];
+
+enum usb_string {
+ STR_MANUFACTURER = 1,
+ STR_DEVICE,
+ STR_SERIAL,
+};
+
+static const char *usb_strings[] = {
+ BOOTLOADER_MFG_NAME,
+ BOOTLOADER_DEV_NAME,
+ usb_serial_number,
+};
+
+const struct usb_device_descriptor dev = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = 64,
+ .idVendor = BOOTLOADER_MFG_ID,
+ .idProduct = BOOTLOADER_DEV_ID,
+ .bcdDevice = BOOTLOADER_DEV_VERSION,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_DEVICE,
+ .iSerialNumber = STR_SERIAL,
+ .bNumConfigurations = 1,
+};
+
+const struct usb_dfu_descriptor dfu_function = {
+ .bLength = sizeof(struct usb_dfu_descriptor),
+ .bDescriptorType = DFU_FUNCTIONAL,
+ .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
+ .wDetachTimeout = 255,
+ .wTransferSize = 1024,
+ .bcdDFUVersion = 0x0100,
+};
+
+const struct usb_interface_descriptor iface = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 2,
+ .extra = &dfu_function,
+ .extralen = sizeof(dfu_function),
+};
+
+const struct usb_interface ifaces[] = {{
+ .num_altsetting = 1,
+ .altsetting = &iface,
+}};
+
+const struct usb_config_descriptor config = {
+ .bLength = USB_DT_CONFIGURATION_SIZE,
+ .bDescriptorType = USB_DT_CONFIGURATION,
+ .wTotalLength = 0,
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = USB_CONFIG_ATTR_DEFAULT, // bus-powered
+ .bMaxPower = 50, // multiplied by 2 mA
+ .interface = ifaces,
+};
+
+static byte usbdfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout)
+{
+ switch (usbdfu_state) {
+ case STATE_DFU_DNLOAD_SYNC:
+ usbdfu_state = STATE_DFU_DNBUSY;
+ *bwPollTimeout = 100;
+ return DFU_STATUS_OK;
+ case STATE_DFU_MANIFEST_SYNC:
+ /* Device will reset when read is complete. */
+ usbdfu_state = STATE_DFU_MANIFEST;
+ return DFU_STATUS_OK;
+ default:
+ return DFU_STATUS_OK;
+ }
+}
+
+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();
+ usbdfu_state = STATE_DFU_DNLOAD_IDLE;
+ return;
+ }
+ case STATE_DFU_MANIFEST:
+ /* USB device must detach, we just reset... */
+ scb_reset_system();
+ return; /* Will never return. */
+ default:
+ return;
+ }
+}
+
+static enum usbd_request_return_codes usbdfu_control_request(usbd_device *usbd_dev,
+ struct usb_setup_data *req,
+ byte **buf,
+ u16 *len,
+ void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
+{
+ 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);
+
+ switch (req->bRequest) {
+ case DFU_DNLOAD:
+ if (len == NULL || *len == 0) {
+ 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);
+ usbdfu_state = STATE_DFU_DNLOAD_SYNC;
+ }
+ return USBD_REQ_HANDLED;
+ case DFU_CLRSTATUS:
+ /* Clear error and return to dfuIDLE. */
+ if (usbdfu_state == STATE_DFU_ERROR)
+ usbdfu_state = STATE_DFU_IDLE;
+ return USBD_REQ_HANDLED;
+ case DFU_ABORT:
+ /* Abort returns to dfuIDLE state. */
+ usbdfu_state = STATE_DFU_IDLE;
+ return USBD_REQ_HANDLED;
+ case DFU_UPLOAD:
+ /* Upload not supported for now. */
+ return USBD_REQ_NOTSUPP;
+ case DFU_GETSTATUS: {
+ u32 bwPollTimeout = 0; /* 24-bit number of milliseconds */
+ (*buf)[0] = usbdfu_getstatus(usbd_dev, &bwPollTimeout);
+ (*buf)[1] = bwPollTimeout & 0xFF;
+ (*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
+ (*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
+ (*buf)[4] = usbdfu_state;
+ (*buf)[5] = 0; /* iString not used here */
+ *len = 6;
+ *complete = usbdfu_getstatus_complete;
+ return USBD_REQ_HANDLED;
+ }
+ case DFU_GETSTATE:
+ /* Return state with no state transition. */
+ *buf[0] = usbdfu_state;
+ *len = 1;
+ return USBD_REQ_HANDLED;
+ }
+
+ return USBD_REQ_NOTSUPP;
+}
+
+static void usbdfu_set_config(usbd_device *usbd_dev, u16 wValue UNUSED)
+{
+ usbd_register_control_callback(
+ usbd_dev,
+ USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
+ USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
+ usbdfu_control_request);
+}
+
+static void usbdfu_reset(void)
+{
+ usbdfu_state = STATE_DFU_IDLE;
+}
+
+int main(void)
+{
+ usbd_device *usbd_dev;
+
+ // Flash programming requires running on the internal oscillator
+ rcc_clock_setup_in_hsi_out_48mhz();
+
+ rcc_periph_clock_enable(RCC_GPIOA);
+ rcc_periph_clock_enable(RCC_GPIOC);
+ rcc_periph_clock_enable(RCC_USB);
+
+ rcc_periph_reset_pulse(RST_GPIOA);
+ rcc_periph_reset_pulse(RST_GPIOC);
+ rcc_periph_reset_pulse(RST_USB);
+
+#ifdef DEBUG_USART
+ // Currently, only USART1 is supported
+ rcc_periph_clock_enable(RCC_USART1);
+ rcc_periph_reset_pulse(RST_USART1);
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
+
+ usart_set_baudrate(USART1, 115200);
+ usart_set_databits(USART1, 8);
+ usart_set_stopbits(USART1, USART_STOPBITS_1);
+ usart_set_mode(USART1, USART_MODE_TX);
+ usart_set_parity(USART1, USART_PARITY_NONE);
+ usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
+
+ usart_enable(USART1);
+#endif
+
+#ifdef DEBUG_LED_BLUEPILL
+ // BluePill LED
+ gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
+ debug_led(1);
+#endif
+
+ // Systick: set to overflow in 1 ms, will use only the overflow flag, no interrupts
+ systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000);
+ systick_clear();
+ systick_counter_enable();
+
+ desig_get_unique_id_as_dfu(usb_serial_number);
+
+ DEBUG("DFU: Entered boot-loader (SN %s)\n", usb_serial_number);
+
+ // 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++) {
+ while (!systick_get_countflag())
+ ;
+ }
+
+ DEBUG("DFU: Ready\n");
+ debug_led(0);
+
+ usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, ARRAY_SIZE(usb_strings), usbd_control_buffer, sizeof(usbd_control_buffer));
+ usbd_register_reset_callback(usbd_dev, usbdfu_reset);
+ usbd_register_set_config_callback(usbd_dev, usbdfu_set_config);
+
+restart: ;
+
+ uint timeout = 5000;
+ while (timeout) {
+ usbd_poll(usbd_dev);
+ if (systick_get_countflag()) {
+ timeout--;
+ if (!(timeout & 0x3f))
+ debug_led_toggle();
+ }
+ }
+
+ volatile u32 *app = (volatile u32 *) BOOTLOADER_APP_START;
+ u32 sp = app[0];
+ u32 pc = app[1];
+
+ DEBUG("DFU: Starting application (sp=%08x, pc=%08x)\n", (uint) sp, (uint) pc);
+ if (sp & 0x2ffe0000 != 0x20000000) {
+ DEBUG("DFU: Suspicious SP, refusing to start\n");
+ goto restart;
+ }
+
+#ifdef DEBUG_USART
+ while (!usart_get_flag(DEBUG_USART, USART_FLAG_TC))
+ ;
+#endif
+ debug_led(0);
+ cm_disable_interrupts();
+
+ /* Set vector table base address. */
+ SCB_VTOR = BOOTLOADER_APP_START & 0xFFFF;
+
+ /* Initialize master stack pointer. */
+ asm volatile("msr msp, %0"::"g" (sp));
+
+ /* Jump to application. */
+ ((void (*)(void)) pc)();
+}