]> mj.ucw.cz Git - home-hw.git/blob - lib/dfu-bootloader.c
Library: Generic DFU boot-loader
[home-hw.git] / lib / dfu-bootloader.c
1 /*
2  *      Generic DFU Bootloader
3  *
4  *      (c) 2020 Martin Mareš <mj@ucw.cz>
5  *
6  *      Based on example code from the libopencm3 project, which is
7  *      Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
8  *
9  *      Licensed under the GNU LGPL v3 or any later version.
10  */
11
12 #include "util.h"
13
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>
25
26 #include <string.h>
27
28 #ifdef BOOTLOADER_DEBUG
29 #define DEBUG(x...) debug_printf(x)
30 #else
31 #define DEBUG(x...) do { } while (0)
32 #endif
33
34 byte usbd_control_buffer[1024];
35
36 static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
37
38 static struct {
39         byte buf[sizeof(usbd_control_buffer)];
40         u16 len;
41         u32 addr;
42         u16 blocknum;
43 } prog;
44
45 static char usb_serial_number[13];
46
47 enum usb_string {
48         STR_MANUFACTURER = 1,
49         STR_DEVICE,
50         STR_SERIAL,
51 };
52
53 static const char *usb_strings[] = {
54         BOOTLOADER_MFG_NAME,
55         BOOTLOADER_DEV_NAME,
56         usb_serial_number,
57 };
58
59 const struct usb_device_descriptor dev = {
60         .bLength = USB_DT_DEVICE_SIZE,
61         .bDescriptorType = USB_DT_DEVICE,
62         .bcdUSB = 0x0200,
63         .bDeviceClass = 0,
64         .bDeviceSubClass = 0,
65         .bDeviceProtocol = 0,
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,
74 };
75
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,
83 };
84
85 const struct usb_interface_descriptor iface = {
86         .bLength = USB_DT_INTERFACE_SIZE,
87         .bDescriptorType = USB_DT_INTERFACE,
88         .bInterfaceNumber = 0,
89         .bAlternateSetting = 0,
90         .bNumEndpoints = 0,
91         .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
92         .bInterfaceSubClass = 1,
93         .bInterfaceProtocol = 2,
94         .extra = &dfu_function,
95         .extralen = sizeof(dfu_function),
96 };
97
98 const struct usb_interface ifaces[] = {{
99         .num_altsetting = 1,
100         .altsetting = &iface,
101 }};
102
103 const struct usb_config_descriptor config = {
104         .bLength = USB_DT_CONFIGURATION_SIZE,
105         .bDescriptorType = USB_DT_CONFIGURATION,
106         .wTotalLength = 0,
107         .bNumInterfaces = 1,
108         .bConfigurationValue = 1,
109         .iConfiguration = 0,
110         .bmAttributes = USB_CONFIG_ATTR_DEFAULT,        // bus-powered
111         .bMaxPower = 50,                                // multiplied by 2 mA
112         .interface = ifaces,
113 };
114
115 static byte usbdfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout)
116 {
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;
126         default:
127                 return DFU_STATUS_OK;
128         }
129 }
130
131 static void usbdfu_getstatus_complete(usbd_device *usbd_dev UNUSED, struct usb_setup_data *req UNUSED)
132 {
133         switch (usbdfu_state) {
134         case STATE_DFU_DNBUSY: {
135                 flash_unlock();
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);
142                 }
143                 flash_lock();
144                 usbdfu_state = STATE_DFU_DNLOAD_IDLE;
145                 return;
146                 }
147         case STATE_DFU_MANIFEST:
148                 /* USB device must detach, we just reset... */
149                 scb_reset_system();
150                 return; /* Will never return. */
151         default:
152                 return;
153         }
154 }
155
156 static enum usbd_request_return_codes usbdfu_control_request(usbd_device *usbd_dev,
157         struct usb_setup_data *req,
158         byte **buf,
159         u16 *len,
160         void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
161 {
162         if ((req->bmRequestType & 0x7F) != 0x21)
163                 return USBD_REQ_NOTSUPP; /* Only accept class request. */
164
165         DEBUG("DFU: req %02x in state %d\n", req->bRequest, usbdfu_state);
166
167         switch (req->bRequest) {
168         case DFU_DNLOAD:
169                 if (len == NULL || *len == 0) {
170                         usbdfu_state = STATE_DFU_MANIFEST_SYNC;
171                 } else {
172                         /* Copy download data for use on GET_STATUS. */
173                         prog.blocknum = req->wValue;
174                         prog.len = *len;
175                         memcpy(prog.buf, *buf, *len);
176                         usbdfu_state = STATE_DFU_DNLOAD_SYNC;
177                 }
178                 return USBD_REQ_HANDLED;
179         case DFU_CLRSTATUS:
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;
184         case DFU_ABORT:
185                 /* Abort returns to dfuIDLE state. */
186                 usbdfu_state = STATE_DFU_IDLE;
187                 return USBD_REQ_HANDLED;
188         case DFU_UPLOAD:
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 */
199                 *len = 6;
200                 *complete = usbdfu_getstatus_complete;
201                 return USBD_REQ_HANDLED;
202                 }
203         case DFU_GETSTATE:
204                 /* Return state with no state transition. */
205                 *buf[0] = usbdfu_state;
206                 *len = 1;
207                 return USBD_REQ_HANDLED;
208         }
209
210         return USBD_REQ_NOTSUPP;
211 }
212
213 static void usbdfu_set_config(usbd_device *usbd_dev, u16 wValue UNUSED)
214 {
215         usbd_register_control_callback(
216                                 usbd_dev,
217                                 USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
218                                 USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
219                                 usbdfu_control_request);
220 }
221
222 static void usbdfu_reset(void)
223 {
224         usbdfu_state = STATE_DFU_IDLE;
225 }
226
227 int main(void)
228 {
229         usbd_device *usbd_dev;
230
231         // Flash programming requires running on the internal oscillator
232         rcc_clock_setup_in_hsi_out_48mhz();
233
234         rcc_periph_clock_enable(RCC_GPIOA);
235         rcc_periph_clock_enable(RCC_GPIOC);
236         rcc_periph_clock_enable(RCC_USB);
237
238         rcc_periph_reset_pulse(RST_GPIOA);
239         rcc_periph_reset_pulse(RST_GPIOC);
240         rcc_periph_reset_pulse(RST_USB);
241
242 #ifdef DEBUG_USART
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);
247
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);
254
255         usart_enable(USART1);
256 #endif
257
258 #ifdef DEBUG_LED_BLUEPILL
259         // BluePill LED
260         gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
261         debug_led(1);
262 #endif
263
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);
266         systick_clear();
267         systick_counter_enable();
268
269         desig_get_unique_id_as_dfu(usb_serial_number);
270
271         DEBUG("DFU: Entered boot-loader (SN %s)\n", usb_serial_number);
272
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())
278                         ;
279         }
280
281         DEBUG("DFU: Ready\n");
282         debug_led(0);
283
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);
287
288 restart: ;
289
290         uint timeout = 5000;
291         while (timeout) {
292                 usbd_poll(usbd_dev);
293                 if (systick_get_countflag()) {
294                         timeout--;
295                         if (!(timeout & 0x3f))
296                                 debug_led_toggle();
297                 }
298         }
299
300         volatile u32 *app = (volatile u32 *) BOOTLOADER_APP_START;
301         u32 sp = app[0];
302         u32 pc = app[1];
303
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");
307                 goto restart;
308         }
309
310 #ifdef DEBUG_USART
311         while (!usart_get_flag(DEBUG_USART, USART_FLAG_TC))
312                 ;
313 #endif
314         debug_led(0);
315         cm_disable_interrupts();
316
317         /* Set vector table base address. */
318         SCB_VTOR = BOOTLOADER_APP_START & 0xFFFF;
319
320         /* Initialize master stack pointer. */
321         asm volatile("msr msp, %0"::"g" (sp));
322
323         /* Jump to application. */
324         ((void (*)(void)) pc)();
325 }