summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/stm32/mboot/dfu.h96
-rw-r--r--ports/stm32/mboot/main.c128
2 files changed, 145 insertions, 79 deletions
diff --git a/ports/stm32/mboot/dfu.h b/ports/stm32/mboot/dfu.h
new file mode 100644
index 000000000..1f53c1f06
--- /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 <stdint.h>
+
+// 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 4d7a08ace..134e40a83 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;