From 3d48137c00511b3f2d35511482d1a76f8d06382d Mon Sep 17 00:00:00 2001 From: root Date: Fri, 26 Feb 2021 12:12:38 +0000 Subject: works --- boot/dfu.c | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 boot/dfu.c (limited to 'boot/dfu.c') diff --git a/boot/dfu.c b/boot/dfu.c new file mode 100644 index 0000000..f931a6e --- /dev/null +++ b/boot/dfu.c @@ -0,0 +1,197 @@ +#include "project.h" + +/* Commands sent with wBlockNum == 0 as per ST implementation. */ +#define CMD_SETADDR 0x21 +#define CMD_ERASE 0x41 + + +/* We need a special large control buffer for this device: */ + +static enum dfu_state usbdfu_state = STATE_DFU_IDLE; + +static uint32_t sector_bases[] = { + 0x08000000, + 0x08004000, + 0x08008000, + 0x0800C000, + 0x08010000, + 0x08020000, + 0x08040000, + 0x08060000, + 0x08080000, + 0x080A0000, + 0x080C0000, + 0x080E0000, +}; + +#define N_SECTORS (sizeof(sector_bases)/sizeof(sector_bases[0])) + +static struct { + uint8_t buf[sizeof (usbd_control_buffer)]; + uint16_t len; + uint32_t addr; + uint16_t blocknum; +} prog; + +const struct usb_dfu_descriptor dfu_function = { + .bLength = sizeof (struct usb_dfu_descriptor), + .bDescriptorType = DFU_FUNCTIONAL, + .bmAttributes = USB_DFU_CAN_DOWNLOAD, + .wDetachTimeout = 255, + .wTransferSize = 1024, + .bcdDFUVersion = 0x011A, +}; + +const struct usb_interface_descriptor dfu_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), +}; + +static uint8_t usbdfu_getstatus (usbd_device *usbd_dev, uint32_t *bwPollTimeout) +{ + (void)usbd_dev; + + 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 int usbdfu_getstatus_complete (usbd_device *usbd_dev, struct usb_setup_data *req) +{ + unsigned i; + (void)req; + (void)usbd_dev; + + switch (usbdfu_state) { + case STATE_DFU_DNBUSY: + flash_unlock(); + + if (prog.blocknum == 0) { + switch (prog.buf[0]) { + case CMD_ERASE: { + uint32_t *dat = (uint32_t *) (prog.buf + 1); + + for (i = 0; i < N_SECTORS; ++i) { + if (*dat == sector_bases[i]) + flash_erase_sector (i, FLASH_CR_PROGRAM_X32); + } + } + + /*Fall through*/ + case CMD_SETADDR: { + uint32_t *dat = (uint32_t *) (prog.buf + 1); + prog.addr = *dat; + } + } + } else { + uint32_t baseaddr = prog.addr + ((prog.blocknum - 2) * + dfu_function.wTransferSize); + + for (i = 0; i < prog.len; i += 4) { + uint32_t *dat = (uint32_t *) (prog.buf + i); + flash_program_word (baseaddr + i, *dat); + } + } + + flash_lock(); + + /* Jump straight to dfuDNLOAD-IDLE, skipping dfuDNLOAD-SYNC. */ + usbdfu_state = STATE_DFU_DNLOAD_IDLE; + return 0; + + case STATE_DFU_MANIFEST: + /* USB device must detach, we just reset... */ + scb_reset_system(); + return 0; /* Will never return. */ + + default: + return 0; + } +} + +int usbdfu_control_request (usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, + uint16_t *len, int (**complete) (usbd_device *usbd_dev, struct usb_setup_data *req)) +{ + + 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: { + uint32_t bwPollTimeout = 0; /* 24-bit integer in DFU class spec */ + (*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 1; + } + + case DFU_GETSTATE: + /* Return state with no state transision. */ + *buf[0] = usbdfu_state; + *len = 1; + return 1; + } + + return 0; +} + + -- cgit v1.2.3