From 14e5e8b0b6dca9dfbcd10021fbe5a8036accb4fb Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 12 Mar 2013 09:57:28 +1300 Subject: [PATCH] Separated clean and dirty bootloader sections. --- src/platforms/stm32/dfucore.c | 238 ++++++++++++++++++++++++++ src/platforms/stm32/usbdfu.c | 303 +++++++--------------------------- 2 files changed, 297 insertions(+), 244 deletions(-) create mode 100644 src/platforms/stm32/dfucore.c diff --git a/src/platforms/stm32/dfucore.c b/src/platforms/stm32/dfucore.c new file mode 100644 index 0000000..9f45a72 --- /dev/null +++ b/src/platforms/stm32/dfucore.c @@ -0,0 +1,238 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2013 Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +/* Commands sent with wBlockNum == 0 as per ST implementation. */ +#define CMD_SETADDR 0x21 +#define CMD_ERASE 0x41 + +usbd_device *usbdev; +/* We need a special large control buffer for this device: */ +u8 usbd_control_buffer[1024]; + +static enum dfu_state usbdfu_state = STATE_DFU_IDLE; + +static char *get_dev_unique_id(char *serial_no); + +static struct { + u8 buf[sizeof(usbd_control_buffer)]; + u16 len; + u32 addr; + u16 blocknum; +} prog; + +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 = 0x1D50, + .idProduct = 0x6017, + .bcdDevice = 0x0100, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .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 = 0x011A, +}; + +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, + + /* The ST Microelectronics DfuSe application needs this string. + * The format isn't documented... */ + .iInterface = 4, + + .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 = 0xC0, + .bMaxPower = 0x32, + + .interface = ifaces, +}; + +static char serial_no[9]; + +static const char *usb_strings[] = { + "Black Sphere Technologies", + PRODUCT_STRING, + serial_no, + /* This string is used by ST Microelectronics' DfuSe utility */ + IFACE_STRING, +}; + +static u8 usbdfu_getstatus(u32 *bwPollTimeout) +{ + switch(usbdfu_state) { + case STATE_DFU_DNLOAD_SYNC: + usbdfu_state = STATE_DFU_DNBUSY; + *bwPollTimeout = poll_timeout(prog.buf[0], + *(u32 *)(prog.buf + 1), + prog.blocknum); + 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 *dev, struct usb_setup_data *req) +{ + (void)req; + + switch(usbdfu_state) { + case STATE_DFU_DNBUSY: + + flash_unlock(); + if(prog.blocknum == 0) { + u32 addr = *(u32 *)(prog.buf + 1); + if (addr < APP_ADDRESS || + (addr >= max_address)) { + flash_lock(); + usbd_ep_stall_set(dev, 0, 1); + return; + } + switch(prog.buf[0]) { + case CMD_ERASE: + check_and_do_sector_erase(addr); + case CMD_SETADDR: + prog.addr = addr; + } + } else { + u32 baseaddr = prog.addr + + ((prog.blocknum - 2) * + dfu_function.wTransferSize); + flash_program_buffer(baseaddr, prog.buf, prog.len); + } + flash_lock(); + + /* We jump straight to dfuDNLOAD-IDLE, + * skipping dfuDNLOAD-SYNC + */ + usbdfu_state = STATE_DFU_DNLOAD_IDLE; + return; + + case STATE_DFU_MANIFEST: + detach(); + return; /* Will never return */ + default: + return; + } +} + +static int usbdfu_control_request(usbd_device *dev, + struct usb_setup_data *req, u8 **buf, u16 *len, + void (**complete)(usbd_device *dev, struct usb_setup_data *req)) +{ + (void)dev; + + if((req->bmRequestType & 0x7F) != 0x21) + return 0; /* Only accept class request */ + + switch(req->bRequest) { + case DFU_DNLOAD: + if((len == NULL) || (*len == 0)) { + usbdfu_state = STATE_DFU_MANIFEST_SYNC; + return 1; + } 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 1; + } + case DFU_CLRSTATUS: + /* Clear error and return to dfuIDLE */ + if(usbdfu_state == STATE_DFU_ERROR) + usbdfu_state = STATE_DFU_IDLE; + return 1; + case DFU_ABORT: + /* Abort returns to dfuIDLE state */ + usbdfu_state = STATE_DFU_IDLE; + return 1; + case DFU_UPLOAD: + /* Upload not supported for now */ + return 0; + case DFU_GETSTATUS: { + u32 bwPollTimeout = 0; /* 24-bit integer in DFU class spec */ + + (*buf)[0] = usbdfu_getstatus(&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 1; + } + case DFU_GETSTATE: + /* Return state with no state transision */ + *buf[0] = usbdfu_state; + *len = 1; + return 1; + } + + return 0; +} + diff --git a/src/platforms/stm32/usbdfu.c b/src/platforms/stm32/usbdfu.c index b582a44..910dfbb 100644 --- a/src/platforms/stm32/usbdfu.c +++ b/src/platforms/stm32/usbdfu.c @@ -1,7 +1,7 @@ /* - * This file is part of the libopencm3 project. + * This file is part of the Black Magic Debug project. * - * Copyright (C) 2010 Gareth McMullin + * Copyright (C) 2013 Gareth McMullin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,22 +31,12 @@ #warning "Unhandled STM32 family" #endif #include -#include -#include - -/* Commands sent with wBlockNum == 0 as per ST implementation. */ -#define CMD_SETADDR 0x21 -#define CMD_ERASE 0x41 #define FLASH_OBP_RDP 0x1FFFF800 #define FLASH_OBP_WRP10 0x1FFFF808 #define FLASH_OBP_RDP_KEY 0x5aa5 -usbd_device *usbdev; -/* We need a special large control buffer for this device: */ -u8 usbd_control_buffer[1024]; - #if defined (STM32_CAN) #define FLASHBLOCKSIZE 2048 #else @@ -140,268 +130,93 @@ void check_and_do_sector_erase(u32 sector) } #endif -static enum dfu_state usbdfu_state = STATE_DFU_IDLE; - -static char *get_dev_unique_id(char *serial_no); - -static struct { - u8 buf[sizeof(usbd_control_buffer)]; - u16 len; - u32 addr; - u16 blocknum; -} prog; - -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 = 0x1D50, - .idProduct = 0x6017, - .bcdDevice = 0x0100, - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = 3, - .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 = 0x011A, -}; - -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, - - /* The ST Microelectronics DfuSe application needs this string. - * The format isn't documented... */ - .iInterface = 4, - - .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 = 0xC0, - .bMaxPower = 0x32, - - .interface = ifaces, -}; - -static char serial_no[9]; - -static const char *usb_strings[] = { - "Black Sphere Technologies", #if defined(BLACKMAGIC) - "Black Magic Probe (Upgrade), (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")", +# define PRODUCT_STRING \ + "Black Magic Probe (Upgrade), (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")" #elif defined(DISCOVERY_STLINK) - "Black Magic (Upgrade) for STLink/Discovery, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")", +# define PRODUCT_STRING \ + "Black Magic (Upgrade) for STLink/Discovery, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")" #elif defined(STM32_CAN) - "Black Magic (Upgrade) for STM32_CAN, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")", +# define PRODUCT_STRING \ + "Black Magic (Upgrade) for STM32_CAN, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")" #elif defined(F4DISCOVERY) - "Black Magic (Upgrade) for F4Discovery, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")", +# define PRODUCT_STRING \ + "Black Magic (Upgrade) for F4Discovery, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")" #elif defined(USPS_F407) - "Black Magic (Upgrade) for USPS_F407, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")", +# define PRODUCT_STRING \ + "Black Magic (Upgrade) for USPS_F407, (Firmware 1.5" VERSION_SUFFIX ", build " BUILDDATE ")" #else -#warning "Unhandled board" +# warning "Unhandled board" #endif - serial_no, + /* This string is used by ST Microelectronics' DfuSe utility */ #if defined(BLACKMAGIC) - "@Internal Flash /0x08000000/8*001Ka,120*001Kg" +# define IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,120*001Kg" #elif defined(DISCOVERY_STLINK) - "@Internal Flash /0x08000000/8*001Ka,56*001Kg" +# define IFACE_STRING "@Internal Flash /0x08000000/8*001Ka,56*001Kg" #elif defined(STM32_CAN) - "@Internal Flash /0x08000000/4*002Ka,124*002Kg" +# define IFACE_STRING "@Internal Flash /0x08000000/4*002Ka,124*002Kg" #elif defined(F4DISCOVERY) || defined(USPS_F407) - "@Internal Flash /0x08000000/1*016Ka,3*016Kg,1*064Kg,7*128Kg" +# define IFACE_STRING "@Internal Flash /0x08000000/1*016Ka,3*016Kg,1*064Kg,7*128Kg" #else -#warning "Unhandled board" +# warning "Unhandled board" #endif -}; -static u8 usbdfu_getstatus(u32 *bwPollTimeout) +static u32 poll_timeout(uint8_t cmd, uint32_t addr, uint16_t blocknum) { - switch(usbdfu_state) { - case STATE_DFU_DNLOAD_SYNC: - usbdfu_state = STATE_DFU_DNBUSY; #if defined(STM32F4) - /* Programming 256 word with 100 us(max) per word*/ - *bwPollTimeout = 26; - /* Erase for big pages on STM2/4 needs "long" time - Try not to hit USB timeouts*/ - if ((prog.blocknum == 0) && (prog.buf[0] == CMD_ERASE)) { - u32 addr = *(u32 *)(prog.buf + 1); - get_sector_num(addr); - if(addr == sector_addr[sector_num]) - *bwPollTimeout = sector_erase_time[sector_num]; - } -#else - *bwPollTimeout = 100; -#endif - 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; + /* Erase for big pages on STM2/4 needs "long" time + Try not to hit USB timeouts*/ + if ((blocknum == 0) && (cmd == CMD_ERASE)) { + get_sector_num(addr); + if(addr == sector_addr[sector_num]) + return sector_erase_time[sector_num]; } + + /* Programming 256 word with 100 us(max) per word*/ + return 26; +#else + (void)cmd; + (void)addr; + (void)blocknum; + return 100; +#endif } -static void -usbdfu_getstatus_complete(usbd_device *dev, struct usb_setup_data *req) +static void flash_program_buffer(uint32_t baseaddr, void *buf, int len) { int i; - (void)req; - - switch(usbdfu_state) { - case STATE_DFU_DNBUSY: - - flash_unlock(); - if(prog.blocknum == 0) { - u32 addr = *(u32 *)(prog.buf + 1); - if (addr < APP_ADDRESS || - (addr >= max_address)) { - flash_lock(); - usbd_ep_stall_set(dev, 0, 1); - return; - } - switch(prog.buf[0]) { - case CMD_ERASE: - check_and_do_sector_erase(addr); - case CMD_SETADDR: - prog.addr = addr; - } - } else { - u32 baseaddr = prog.addr + - ((prog.blocknum - 2) * - dfu_function.wTransferSize); #if defined (STM32F4) - for(i = 0; i < prog.len; i += 4) - flash_program_word(baseaddr + i, - *(u32*)(prog.buf+i), - FLASH_PROGRAM_X32); + for(i = 0; i < len; i += 4) + flash_program_word(baseaddr + i, + *(u32*)(buf+i), + FLASH_PROGRAM_X32); #else - for(i = 0; i < prog.len; i += 2) - flash_program_half_word(baseaddr + i, - *(u16*)(prog.buf+i)); + for(i = 0; i < len; i += 2) + flash_program_half_word(baseaddr + i, + *(u16*)(buf+i)); #endif - } - flash_lock(); +} - /* We jump straight to dfuDNLOAD-IDLE, - * skipping dfuDNLOAD-SYNC - */ - usbdfu_state = STATE_DFU_DNLOAD_IDLE; - return; - - case STATE_DFU_MANIFEST: +static void detach(void) +{ #if defined (DISCOVERY_STLINK) - /* Disconnect USB cable by resetting USB Device - and pulling USB_DP low*/ - rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN); - rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN); - rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN); - rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN); - gpio_clear(GPIOA, GPIO12); - gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, - GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12); + /* Disconnect USB cable by resetting USB Device + and pulling USB_DP low*/ + rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN); + rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1ENR_USBEN); + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN); + gpio_clear(GPIOA, GPIO12); + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12); #else /* USB device must detach, we just reset... */ #endif - scb_reset_system(); - return; /* Will never return */ - default: - return; - } + scb_reset_system(); } -static int usbdfu_control_request(usbd_device *dev, - struct usb_setup_data *req, u8 **buf, u16 *len, - void (**complete)(usbd_device *dev, struct usb_setup_data *req)) -{ - (void)dev; - - if((req->bmRequestType & 0x7F) != 0x21) - return 0; /* Only accept class request */ - - switch(req->bRequest) { - case DFU_DNLOAD: - if((len == NULL) || (*len == 0)) { - usbdfu_state = STATE_DFU_MANIFEST_SYNC; - return 1; - } 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 1; - } - case DFU_CLRSTATUS: - /* Clear error and return to dfuIDLE */ - if(usbdfu_state == STATE_DFU_ERROR) - usbdfu_state = STATE_DFU_IDLE; - return 1; - case DFU_ABORT: - /* Abort returns to dfuIDLE state */ - usbdfu_state = STATE_DFU_IDLE; - return 1; - case DFU_UPLOAD: - /* Upload not supported for now */ - return 0; - case DFU_GETSTATUS: { - u32 bwPollTimeout = 0; /* 24-bit integer in DFU class spec */ - - (*buf)[0] = usbdfu_getstatus(&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 1; - } - case DFU_GETSTATE: - /* Return state with no state transision */ - *buf[0] = usbdfu_state; - *len = 1; - return 1; - } - - return 0; -} +#include "dfucore.c" int main(void) { @@ -552,7 +367,7 @@ int main(void) USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, usbdfu_control_request); -#if defined(BLACKMAGIG) +#if defined(BLACKMAGIC) gpio_set(GPIOA, GPIO8); gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO8); @@ -600,7 +415,7 @@ static char *get_dev_unique_id(char *s) void sys_tick_handler() { - #if defined(DISCOVERY_STLINK) +#if defined(DISCOVERY_STLINK) if (rev == 0) gpio_toggle(GPIOA, led_idle_run); else