stm32/mboot: Implement DFU mass erase.

The implementation internally uses sector erase to wipe everything except
the sector(s) that mboot lives in (by erasing starting from
APPLICATION_ADDR).

The erase command can take some time (eg an STM32F765 with 2MB of flash
takes 8 to 10 seconds).  This time is normally enough to make pydfu.py fail
with a timeout.  The DFU standard includes a mechanism for the DFU device
to request a longer timeout as part of the get-status response just before
starting an operation.  This timeout functionality has been implemented
here.
This commit is contained in:
Andrew Leech 2020-03-05 16:05:36 +11:00 committed by Damien George
parent 4d6f60d428
commit 8bbaa20227
2 changed files with 52 additions and 11 deletions

View File

@ -67,6 +67,12 @@ typedef enum {
DFU_CMD_DNLOAD = 8,
} dfu_cmd_t;
enum {
DFU_CMD_DNLOAD_SET_ADDRESS = 0x21,
DFU_CMD_DNLOAD_ERASE = 0x41,
DFU_CMD_DNLOAD_READ_UNPROTECT = 0x92,
};
// Error status flags
typedef enum {
DFU_STATUS_OK = 0x00, // No error condition is present.

View File

@ -441,6 +441,11 @@ static int usrbtn_state(void) {
/******************************************************************************/
// FLASH
#if defined(STM32WB)
#define FLASH_END FLASH_END_ADDR
#endif
#define APPLICATION_FLASH_LENGTH (FLASH_END + 1 - APPLICATION_ADDR)
#ifndef MBOOT_SPIFLASH_LAYOUT
#define MBOOT_SPIFLASH_LAYOUT ""
#endif
@ -464,8 +469,9 @@ static int usrbtn_state(void) {
#endif
static int mboot_flash_mass_erase(void) {
// TODO
return -1;
// Erase all flash pages after mboot.
int ret = flash_erase(APPLICATION_ADDR, APPLICATION_FLASH_LENGTH / sizeof(uint32_t));
return ret;
}
static int mboot_flash_page_erase(uint32_t addr, uint32_t *next_addr) {
@ -523,7 +529,7 @@ static int mboot_flash_write(uint32_t addr, const uint8_t *src8, size_t len) {
// Writable address space interface
static int do_mass_erase(void) {
// TODO
// TODO spiflash erase ?
return mboot_flash_mass_erase();
}
@ -791,20 +797,45 @@ static void dfu_init(void) {
dfu_context.addr = 0x08000000;
}
// The DFU_GETSTATUS response before dfu_process_dnload is run should include the needed timeout adjustments
static size_t get_timeout_ms(void) {
if (dfu_context.wBlockNum == 0) {
// download control commands
if (dfu_context.wLength >= 1 && dfu_context.buf[0] == DFU_CMD_DNLOAD_ERASE) {
if (dfu_context.wLength == 1) {
// mass erase command
// It takes 10-12 seconds to erase a 2MB stm part. Extrapolate a suitable timeout from this.
return APPLICATION_FLASH_LENGTH / 170;
} else if (dfu_context.wLength == 5) {
// erase page command
return 500;
}
}
} else if (dfu_context.wBlockNum > 1) {
// write data to memory command
return 500;
}
return 0;
}
static int dfu_process_dnload(void) {
int ret = -1;
if (dfu_context.wBlockNum == 0) {
// download control commands
if (dfu_context.wLength >= 1 && dfu_context.buf[0] == 0x41) {
if (dfu_context.wLength >= 1 && dfu_context.buf[0] == DFU_CMD_DNLOAD_ERASE) {
if (dfu_context.wLength == 1) {
// mass erase
ret = do_mass_erase();
if (ret != 0) {
dfu_context.cmd = DFU_CMD_NONE;
}
} else if (dfu_context.wLength == 5) {
// erase page
uint32_t next_addr;
ret = do_page_erase(get_le32(&dfu_context.buf[1]), &next_addr);
}
} else if (dfu_context.wLength >= 1 && dfu_context.buf[0] == 0x21) {
} else if (dfu_context.wLength >= 1 && dfu_context.buf[0] == DFU_CMD_DNLOAD_SET_ADDRESS) {
if (dfu_context.wLength == 5) {
// set address
dfu_context.addr = get_le32(&dfu_context.buf[1]);
@ -888,12 +919,16 @@ static int dfu_handle_tx(int cmd, int arg, int len, uint8_t *buf, int max_len) {
default:
dfu_context.state = DFU_STATE_BUSY;
}
size_t timeout_ms = get_timeout_ms();
buf[0] = dfu_context.status; // bStatus
buf[1] = 0; // bwPollTimeout (ms)
buf[2] = 0; // bwPollTimeout (ms)
buf[3] = 0; // bwPollTimeout (ms)
buf[1] = (timeout_ms >> 16) & 0xFF; // bwPollTimeout (ms)
buf[2] = (timeout_ms >> 8) & 0xFF; // bwPollTimeout (ms)
buf[3] = timeout_ms & 0xFF; // bwPollTimeout (ms)
buf[4] = dfu_context.state; // bState
buf[5] = dfu_context.error; // iString
// Clear errors now they've been sent
dfu_context.status = DFU_STATUS_OK;
dfu_context.error = 0;
return 6;
}
return -1;