2 * Generic DFU Bootloader
4 * (c) 2020 Martin Mareš <mj@ucw.cz>
6 * Based on example code from the libopencm3 project, which is
7 * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
9 * Licensed under the GNU LGPL v3 or any later version.
14 #include <libopencm3/cm3/cortex.h>
15 #include <libopencm3/cm3/nvic.h>
16 #include <libopencm3/cm3/scb.h>
17 #include <libopencm3/cm3/systick.h>
18 #include <libopencm3/stm32/rcc.h>
19 #include <libopencm3/stm32/gpio.h>
20 #include <libopencm3/stm32/flash.h>
21 #include <libopencm3/stm32/usart.h>
22 #include <libopencm3/stm32/desig.h>
23 #include <libopencm3/usb/usbd.h>
24 #include <libopencm3/usb/dfu.h>
28 #ifdef BOOTLOADER_DEBUG
29 #define DEBUG(x...) debug_printf(x)
31 #define DEBUG(x...) do { } while (0)
34 byte usbd_control_buffer[1024];
36 static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
39 byte buf[sizeof(usbd_control_buffer)];
45 static char usb_serial_number[13];
53 static const char *usb_strings[] = {
59 const struct usb_device_descriptor dev = {
60 .bLength = USB_DT_DEVICE_SIZE,
61 .bDescriptorType = USB_DT_DEVICE,
66 .bMaxPacketSize0 = 64,
67 .idVendor = BOOTLOADER_MFG_ID,
68 .idProduct = BOOTLOADER_DEV_ID,
69 .bcdDevice = BOOTLOADER_DEV_VERSION,
70 .iManufacturer = STR_MANUFACTURER,
71 .iProduct = STR_DEVICE,
72 .iSerialNumber = STR_SERIAL,
73 .bNumConfigurations = 1,
76 const struct usb_dfu_descriptor dfu_function = {
77 .bLength = sizeof(struct usb_dfu_descriptor),
78 .bDescriptorType = DFU_FUNCTIONAL,
79 .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
80 .wDetachTimeout = 255,
81 .wTransferSize = 1024,
82 .bcdDFUVersion = 0x0100,
85 const struct usb_interface_descriptor iface = {
86 .bLength = USB_DT_INTERFACE_SIZE,
87 .bDescriptorType = USB_DT_INTERFACE,
88 .bInterfaceNumber = 0,
89 .bAlternateSetting = 0,
91 .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
92 .bInterfaceSubClass = 1,
93 .bInterfaceProtocol = 2,
94 .extra = &dfu_function,
95 .extralen = sizeof(dfu_function),
98 const struct usb_interface ifaces[] = {{
100 .altsetting = &iface,
103 const struct usb_config_descriptor config = {
104 .bLength = USB_DT_CONFIGURATION_SIZE,
105 .bDescriptorType = USB_DT_CONFIGURATION,
108 .bConfigurationValue = 1,
110 .bmAttributes = USB_CONFIG_ATTR_DEFAULT, // bus-powered
111 .bMaxPower = 50, // multiplied by 2 mA
115 static byte usbdfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout)
117 switch (usbdfu_state) {
118 case STATE_DFU_DNLOAD_SYNC:
119 usbdfu_state = STATE_DFU_DNBUSY;
120 *bwPollTimeout = 100;
121 return DFU_STATUS_OK;
122 case STATE_DFU_MANIFEST_SYNC:
123 /* Device will reset when read is complete. */
124 usbdfu_state = STATE_DFU_MANIFEST;
125 return DFU_STATUS_OK;
127 return DFU_STATUS_OK;
131 static void usbdfu_getstatus_complete(usbd_device *usbd_dev UNUSED, struct usb_setup_data *req UNUSED)
133 switch (usbdfu_state) {
134 case STATE_DFU_DNBUSY: {
136 u32 baseaddr = BOOTLOADER_APP_START + prog.blocknum * dfu_function.wTransferSize;
137 DEBUG("DFU: Block %u -> %08x\n", prog.blocknum, (uint) baseaddr);
138 flash_erase_page(baseaddr);
139 for (uint i = 0; i < prog.len; i += 2) {
140 u16 *dat = (u16 *)(prog.buf + i);
141 flash_program_half_word(baseaddr + i, *dat);
144 usbdfu_state = STATE_DFU_DNLOAD_IDLE;
147 case STATE_DFU_MANIFEST:
148 /* USB device must detach, we just reset... */
150 return; /* Will never return. */
156 static enum usbd_request_return_codes usbdfu_control_request(usbd_device *usbd_dev,
157 struct usb_setup_data *req,
160 void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
162 if ((req->bmRequestType & 0x7F) != 0x21)
163 return USBD_REQ_NOTSUPP; /* Only accept class request. */
165 DEBUG("DFU: req %02x in state %d\n", req->bRequest, usbdfu_state);
167 switch (req->bRequest) {
169 if (len == NULL || *len == 0) {
170 usbdfu_state = STATE_DFU_MANIFEST_SYNC;
172 /* Copy download data for use on GET_STATUS. */
173 prog.blocknum = req->wValue;
175 memcpy(prog.buf, *buf, *len);
176 usbdfu_state = STATE_DFU_DNLOAD_SYNC;
178 return USBD_REQ_HANDLED;
180 /* Clear error and return to dfuIDLE. */
181 if (usbdfu_state == STATE_DFU_ERROR)
182 usbdfu_state = STATE_DFU_IDLE;
183 return USBD_REQ_HANDLED;
185 /* Abort returns to dfuIDLE state. */
186 usbdfu_state = STATE_DFU_IDLE;
187 return USBD_REQ_HANDLED;
189 /* Upload not supported for now. */
190 return USBD_REQ_NOTSUPP;
191 case DFU_GETSTATUS: {
192 u32 bwPollTimeout = 0; /* 24-bit number of milliseconds */
193 (*buf)[0] = usbdfu_getstatus(usbd_dev, &bwPollTimeout);
194 (*buf)[1] = bwPollTimeout & 0xFF;
195 (*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
196 (*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
197 (*buf)[4] = usbdfu_state;
198 (*buf)[5] = 0; /* iString not used here */
200 *complete = usbdfu_getstatus_complete;
201 return USBD_REQ_HANDLED;
204 /* Return state with no state transition. */
205 *buf[0] = usbdfu_state;
207 return USBD_REQ_HANDLED;
210 return USBD_REQ_NOTSUPP;
213 static void usbdfu_set_config(usbd_device *usbd_dev, u16 wValue UNUSED)
215 usbd_register_control_callback(
217 USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
218 USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
219 usbdfu_control_request);
222 static void usbdfu_reset(void)
224 usbdfu_state = STATE_DFU_IDLE;
229 usbd_device *usbd_dev;
231 // Flash programming requires running on the internal oscillator
232 rcc_clock_setup_in_hsi_out_48mhz();
234 rcc_periph_clock_enable(RCC_GPIOA);
235 rcc_periph_clock_enable(RCC_GPIOC);
236 rcc_periph_clock_enable(RCC_USB);
238 rcc_periph_reset_pulse(RST_GPIOA);
239 rcc_periph_reset_pulse(RST_GPIOC);
240 rcc_periph_reset_pulse(RST_USB);
243 // Currently, only USART1 is supported
244 rcc_periph_clock_enable(RCC_USART1);
245 rcc_periph_reset_pulse(RST_USART1);
246 gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
248 usart_set_baudrate(USART1, 115200);
249 usart_set_databits(USART1, 8);
250 usart_set_stopbits(USART1, USART_STOPBITS_1);
251 usart_set_mode(USART1, USART_MODE_TX);
252 usart_set_parity(USART1, USART_PARITY_NONE);
253 usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
255 usart_enable(USART1);
258 #ifdef DEBUG_LED_BLUEPILL
260 gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
264 // Systick: set to overflow in 1 ms, will use only the overflow flag, no interrupts
265 systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000);
267 systick_counter_enable();
269 desig_get_unique_id_as_dfu(usb_serial_number);
271 DEBUG("DFU: Entered boot-loader (SN %s)\n", usb_serial_number);
273 // Simulate USB disconnect
274 gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12);
275 gpio_clear(GPIOA, GPIO11 | GPIO12);
276 for (uint i=0; i<1000; i++) {
277 while (!systick_get_countflag())
281 DEBUG("DFU: Ready\n");
284 usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, ARRAY_SIZE(usb_strings), usbd_control_buffer, sizeof(usbd_control_buffer));
285 usbd_register_reset_callback(usbd_dev, usbdfu_reset);
286 usbd_register_set_config_callback(usbd_dev, usbdfu_set_config);
293 if (systick_get_countflag()) {
295 if (!(timeout & 0x3f))
300 volatile u32 *app = (volatile u32 *) BOOTLOADER_APP_START;
304 DEBUG("DFU: Starting application (sp=%08x, pc=%08x)\n", (uint) sp, (uint) pc);
305 if (sp & 0x2ffe0000 != 0x20000000) {
306 DEBUG("DFU: Suspicious SP, refusing to start\n");
311 while (!usart_get_flag(DEBUG_USART, USART_FLAG_TC))
315 cm_disable_interrupts();
317 /* Set vector table base address. */
318 SCB_VTOR = BOOTLOADER_APP_START & 0xFFFF;
320 /* Initialize master stack pointer. */
321 asm volatile("msr msp, %0"::"g" (sp));
323 /* Jump to application. */
324 ((void (*)(void)) pc)();