diff --git a/ports/stm32/mboot/dfu.h b/ports/stm32/mboot/dfu.h new file mode 100644 index 0000000000000000000000000000000000000000..1f53c1f069b790ba45fe98e81203f5d96cccd7f8 --- /dev/null +++ b/ports/stm32/mboot/dfu.h @@ -0,0 +1,96 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_STM32_MBOOT_DFU_H +#define MICROPY_INCLUDED_STM32_MBOOT_DFU_H + +#include + +// DFU spec: https://www.usb.org/sites/default/files/DFU_1.1.pdf + +#define DFU_XFER_SIZE (2048) + +// DFU class requests +enum { + DFU_DETACH = 0, + DFU_DNLOAD = 1, + DFU_UPLOAD = 2, + DFU_GETSTATUS = 3, + DFU_CLRSTATUS = 4, + DFU_GETSTATE = 5, + DFU_ABORT = 6, +}; + +// DFU States +typedef enum { + DFU_STATE_IDLE = 2, + DFU_STATE_BUSY = 4, + DFU_STATE_DNLOAD_IDLE = 5, + DFU_STATE_MANIFEST = 7, + DFU_STATE_UPLOAD_IDLE = 9, + DFU_STATE_ERROR = 0xa, +} dfu_state_t; + +typedef enum { + DFU_CMD_NONE = 0, + DFU_CMD_EXIT = 1, + DFU_CMD_UPLOAD = 7, + DFU_CMD_DNLOAD = 8, +} dfu_cmd_t; + +// Error status flags +typedef enum { + DFU_STATUS_OK = 0x00, // No error condition is present. + DFU_STATUS_ERROR_TARGET = 0x01, // File is not targeted for use by this device. + DFU_STATUS_ERROR_FILE = 0x02, // File is for this device but fails some vendor-specific verification test. + DFU_STATUS_ERROR_WRITE = 0x03, // Device is unable to write memory. + DFU_STATUS_ERROR_ERASE = 0x04, // Memory erase function failed. + DFU_STATUS_ERROR_CHECK_ERASED = 0x05, // Memory erase check failed. + DFU_STATUS_ERROR_PROG = 0x06, // Program memory function failed. + DFU_STATUS_ERROR_VERIFY = 0x07, // Programmed memory failed verification. + DFU_STATUS_ERROR_ADDRESS = 0x08, // Cannot program memory due to received address that is out of range. + DFU_STATUS_ERROR_NOTDONE = 0x09, // Received DFU_DNLOAD with wLength = 0, but device does not think it has all of the data yet. + DFU_STATUS_ERROR_FIRMWARE = 0x0A, // Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations. + DFU_STATUS_ERROR_VENDOR = 0x0B, // iString indicates a vendor-specific error. + DFU_STATUS_ERROR_USBR = 0x0C, // Device detected unexpected USB reset signaling. + DFU_STATUS_ERROR_POR = 0x0D, // Device detected unexpected power on reset. + DFU_STATUS_ERROR_UNKNOWN = 0x0E, // Something went wrong, but the device does not know what it was. + DFU_STATUS_ERROR_STALLEDPKT = 0x0F, // Device stalled an unexpected request. +} dfu_status_t; + +typedef struct _dfu_state_t { + dfu_state_t state; + dfu_cmd_t cmd; + dfu_status_t status; + uint8_t error; + uint16_t wBlockNum; + uint16_t wLength; + uint32_t addr; + uint8_t buf[DFU_XFER_SIZE] __attribute__((aligned(4))); +} dfu_context_t; + +static dfu_context_t dfu_context SECTION_NOZERO_BSS; + +#endif // MICROPY_INCLUDED_STM32_MBOOT_DFU_H diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 4d7a08ace6ffc8f0105e23a16d2ec660a9fed00e..134e40a839e3601be5ac31cd464df82039db712b 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -33,6 +33,7 @@ #include "storage.h" #include "i2cslave.h" #include "mboot.h" +#include "dfu.h" // Using polling is about 10% faster than not using it (and using IRQ instead) // This DFU code with polling runs in about 70% of the time of the ST bootloader @@ -905,113 +906,80 @@ uint8_t i2c_slave_process_tx_byte(void) { /******************************************************************************/ // DFU -#define DFU_XFER_SIZE (2048) - -enum { - DFU_DNLOAD = 1, - DFU_UPLOAD = 2, - DFU_GETSTATUS = 3, - DFU_CLRSTATUS = 4, - DFU_ABORT = 6, -}; - -enum { - DFU_STATUS_IDLE = 2, - DFU_STATUS_BUSY = 4, - DFU_STATUS_DNLOAD_IDLE = 5, - DFU_STATUS_MANIFEST = 7, - DFU_STATUS_UPLOAD_IDLE = 9, - DFU_STATUS_ERROR = 0xa, -}; - -enum { - DFU_CMD_NONE = 0, - DFU_CMD_EXIT = 1, - DFU_CMD_UPLOAD = 7, - DFU_CMD_DNLOAD = 8, -}; - -typedef struct _dfu_state_t { - int status; - int cmd; - uint16_t wBlockNum; - uint16_t wLength; - uint32_t addr; - uint8_t buf[DFU_XFER_SIZE] __attribute__((aligned(4))); -} dfu_state_t; - -static dfu_state_t dfu_state SECTION_NOZERO_BSS; - static void dfu_init(void) { - dfu_state.status = DFU_STATUS_IDLE; - dfu_state.cmd = DFU_CMD_NONE; - dfu_state.addr = 0x08000000; + dfu_context.state = DFU_STATE_IDLE; + dfu_context.cmd = DFU_CMD_NONE; + dfu_context.addr = 0x08000000; } static int dfu_process_dnload(void) { int ret = -1; - if (dfu_state.wBlockNum == 0) { + if (dfu_context.wBlockNum == 0) { // download control commands - if (dfu_state.wLength >= 1 && dfu_state.buf[0] == 0x41) { - if (dfu_state.wLength == 1) { + if (dfu_context.wLength >= 1 && dfu_context.buf[0] == 0x41) { + if (dfu_context.wLength == 1) { // mass erase ret = do_mass_erase(); - } else if (dfu_state.wLength == 5) { + } else if (dfu_context.wLength == 5) { // erase page uint32_t next_addr; - ret = do_page_erase(get_le32(&dfu_state.buf[1]), &next_addr); + ret = do_page_erase(get_le32(&dfu_context.buf[1]), &next_addr); } - } else if (dfu_state.wLength >= 1 && dfu_state.buf[0] == 0x21) { - if (dfu_state.wLength == 5) { + } else if (dfu_context.wLength >= 1 && dfu_context.buf[0] == 0x21) { + if (dfu_context.wLength == 5) { // set address - dfu_state.addr = get_le32(&dfu_state.buf[1]); + dfu_context.addr = get_le32(&dfu_context.buf[1]); ret = 0; } } - } else if (dfu_state.wBlockNum > 1) { + } else if (dfu_context.wBlockNum > 1) { // write data to memory - uint32_t addr = (dfu_state.wBlockNum - 2) * DFU_XFER_SIZE + dfu_state.addr; - ret = do_write(addr, dfu_state.buf, dfu_state.wLength); + uint32_t addr = (dfu_context.wBlockNum - 2) * DFU_XFER_SIZE + dfu_context.addr; + ret = do_write(addr, dfu_context.buf, dfu_context.wLength); } if (ret == 0) { - return DFU_STATUS_DNLOAD_IDLE; + return DFU_STATE_DNLOAD_IDLE; } else { - return DFU_STATUS_ERROR; + return DFU_STATE_ERROR; } } static void dfu_handle_rx(int cmd, int arg, int len, const void *buf) { if (cmd == DFU_CLRSTATUS) { // clear status - dfu_state.status = DFU_STATUS_IDLE; - dfu_state.cmd = DFU_CMD_NONE; + dfu_context.state = DFU_STATE_IDLE; + dfu_context.cmd = DFU_CMD_NONE; + dfu_context.status = DFU_STATUS_OK; + dfu_context.error = 0; } else if (cmd == DFU_ABORT) { // clear status - dfu_state.status = DFU_STATUS_IDLE; - dfu_state.cmd = DFU_CMD_NONE; + dfu_context.state = DFU_STATE_IDLE; + dfu_context.cmd = DFU_CMD_NONE; + dfu_context.status = DFU_STATUS_OK; + dfu_context.error = 0; } else if (cmd == DFU_DNLOAD) { if (len == 0) { // exit DFU - dfu_state.cmd = DFU_CMD_EXIT; + dfu_context.cmd = DFU_CMD_EXIT; } else { // download - dfu_state.cmd = DFU_CMD_DNLOAD; - dfu_state.wBlockNum = arg; - dfu_state.wLength = len; - memcpy(dfu_state.buf, buf, len); + dfu_context.cmd = DFU_CMD_DNLOAD; + dfu_context.wBlockNum = arg; + dfu_context.wLength = len; + memcpy(dfu_context.buf, buf, len); } } } static void dfu_process(void) { - if (dfu_state.status == DFU_STATUS_MANIFEST) { + if (dfu_context.state == DFU_STATE_MANIFEST) { do_reset(); } - if (dfu_state.status == DFU_STATUS_BUSY) { - if (dfu_state.cmd == DFU_CMD_DNLOAD) { - dfu_state.cmd = DFU_CMD_NONE; - dfu_state.status = dfu_process_dnload(); + if (dfu_context.state == DFU_STATE_BUSY) { + if (dfu_context.cmd == DFU_CMD_DNLOAD) { + dfu_context.cmd = DFU_CMD_NONE; + dfu_context.state = dfu_process_dnload(); } } } @@ -1019,32 +987,34 @@ static void dfu_process(void) { static int dfu_handle_tx(int cmd, int arg, int len, uint8_t *buf, int max_len) { if (cmd == DFU_UPLOAD) { if (arg >= 2) { - dfu_state.cmd = DFU_CMD_UPLOAD; - uint32_t addr = (arg - 2) * max_len + dfu_state.addr; + dfu_context.cmd = DFU_CMD_UPLOAD; + uint32_t addr = (arg - 2) * max_len + dfu_context.addr; do_read(addr, len, buf); return len; } } else if (cmd == DFU_GETSTATUS && len == 6) { // execute command and get status - switch (dfu_state.cmd) { + switch (dfu_context.cmd) { case DFU_CMD_NONE: break; case DFU_CMD_EXIT: - dfu_state.status = DFU_STATUS_MANIFEST; + dfu_context.state = DFU_STATE_MANIFEST; break; case DFU_CMD_UPLOAD: - dfu_state.status = DFU_STATUS_UPLOAD_IDLE; + dfu_context.state = DFU_STATE_UPLOAD_IDLE; break; case DFU_CMD_DNLOAD: - dfu_state.status = DFU_STATUS_BUSY; + dfu_context.state = DFU_STATE_BUSY; break; + default: + dfu_context.state = DFU_STATE_BUSY; } - buf[0] = 0; - buf[1] = dfu_state.cmd; // TODO is this correct? - buf[2] = 0; - buf[3] = 0; - buf[4] = dfu_state.status; - buf[5] = 0; + buf[0] = dfu_context.status; // bStatus + buf[1] = 0; // bwPollTimeout (ms) + buf[2] = 0; // bwPollTimeout (ms) + buf[3] = 0; // bwPollTimeout (ms) + buf[4] = dfu_context.state; // bState + buf[5] = dfu_context.error; // iString return 6; } return -1;