From b2deea67628b7069ed4e5d0cde84005738463514 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 23 Mar 2022 22:28:58 +1100 Subject: [PATCH] stm32/mboot: Consolidate all UI and add general state change hooks. All user interface (LED, button) code has been moved to ui.c, and the interface to this code with the rest of the system now goes through calls to mboot_state_change(). This state-change function can be overridden by a board to fully customise the user interface behaviour. Signed-off-by: Damien George --- ports/stm32/mboot/Makefile | 1 + ports/stm32/mboot/fsload.c | 14 +- ports/stm32/mboot/main.c | 206 ++++-------------------------- ports/stm32/mboot/mboot.h | 40 ++++++ ports/stm32/mboot/ui.c | 255 +++++++++++++++++++++++++++++++++++++ 5 files changed, 321 insertions(+), 195 deletions(-) create mode 100644 ports/stm32/mboot/ui.c diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 61a102d02d..db39daf882 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -111,6 +111,7 @@ SRC_C += \ gzstream.c \ pack.c \ sdcard.c \ + ui.c \ vfs_fat.c \ vfs_lfs.c \ drivers/bus/softspi.c \ diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index 2b674369f0..231b07afa5 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -118,6 +118,7 @@ static int fsload_program_file(bool write_to_flash) { uint32_t num_elems = get_le32(buf + 270); size_t file_offset_target = file_offset; + size_t bytes_processed = 0; // Parse each element for (size_t elem = 0; elem < num_elems; ++elem) { @@ -163,6 +164,8 @@ static int fsload_program_file(bool write_to_flash) { } elem_addr += l; s -= l; + bytes_processed += l; + mboot_state_change(MBOOT_STATE_FSLOAD_PROGRESS, write_to_flash << 31 | bytes_processed); } file_offset += elem_size; @@ -295,17 +298,6 @@ int fsload_process(void) { ret = fsload_validate_and_program_file(&ctx, methods, fname); } - // Flash LEDs based on success/failure of update - for (int i = 0; i < 4; ++i) { - if (ret == 0) { - led_state_all(7); - } else { - led_state_all(1); - } - mp_hal_delay_ms(100); - led_state_all(0); - mp_hal_delay_ms(100); - } return ret; } elem += elem[-1]; diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 10a09fe3e1..37c7c0cf5d 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -109,7 +109,7 @@ #define APP_VALIDITY_BITS (0x00000003) // For 1ms system ticker. -static volatile uint32_t systick_ms; +volatile uint32_t systick_ms; // Global dfu state dfu_context_t dfu_context SECTION_NOZERO_BSS; @@ -397,112 +397,6 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) { gpio->OSPEEDR = (gpio->OSPEEDR & ~(3 << (2 * pin))) | (speed << (2 * pin)); } -/******************************************************************************/ -// LED - -#if defined(MBOOT_LED1) -#define LED0 MBOOT_LED1 -#elif defined(MICROPY_HW_LED1) -#define LED0 MICROPY_HW_LED1 -#endif - -#if defined(MBOOT_LED2) -#define LED1 MBOOT_LED2 -#elif defined(MICROPY_HW_LED2) -#define LED1 MICROPY_HW_LED2 -#endif - -#if defined(MBOOT_LED3) -#define LED2 MBOOT_LED3 -#elif defined(MICROPY_HW_LED3) -#define LED2 MICROPY_HW_LED3 -#endif - -#if defined(MBOOT_LED4) -#define LED3 MBOOT_LED4 -#elif defined(MICROPY_HW_LED4) -#define LED3 MICROPY_HW_LED4 -#endif - -// For flashing states: bit 0 is "active", bit 1 is "inactive", bits 2-6 are flash rate. -typedef enum { - LED0_STATE_OFF = 0, - LED0_STATE_ON = 1, - LED0_STATE_SLOW_FLASH = (20 << 2) | 1, - LED0_STATE_FAST_FLASH = (2 << 2) | 1, - LED0_STATE_SLOW_INVERTED_FLASH = (20 << 2) | 2, -} led0_state_t; - -static led0_state_t led0_cur_state = LED0_STATE_OFF; -static uint32_t led0_ms_interval = 0; -static int led0_toggle_count = 0; - -MP_WEAK void led_init(void) { - #if defined(MBOOT_BOARD_LED_INIT) - // Custom LED init function provided by the board. - MBOOT_BOARD_LED_INIT(); - #else - // Init LEDs using GPIO calls. - mp_hal_pin_output(LED0); - #ifdef LED1 - mp_hal_pin_output(LED1); - #endif - #ifdef LED2 - mp_hal_pin_output(LED2); - #endif - #ifdef LED3 - mp_hal_pin_output(LED3); - #endif - #endif - - led0_cur_state = LED0_STATE_OFF; -} - -MP_WEAK void led_state(uint32_t led, int val) { - #if defined(MBOOT_BOARD_LED_STATE) - // Custom LED state function provided by the board. - return MBOOT_BOARD_LED_STATE(led, val); - #else - // Set LEDs using GPIO calls. - if (val) { - MICROPY_HW_LED_ON(led); - } else { - MICROPY_HW_LED_OFF(led); - } - #endif -} - -void led_state_all(unsigned int mask) { - led_state(LED0, mask & 1); - #ifdef LED1 - led_state(LED1, mask & 2); - #endif - #ifdef LED2 - led_state(LED2, mask & 4); - #endif - #ifdef LED3 - led_state(LED3, mask & 8); - #endif -} - -void led0_state(led0_state_t state) { - led0_cur_state = state; - if (state == LED0_STATE_OFF || state == LED0_STATE_ON) { - led_state(LED0, state); - } -} - -void led0_update() { - if (led0_cur_state != LED0_STATE_OFF && systick_ms - led0_ms_interval > 50) { - uint8_t rate = (led0_cur_state >> 2) & 0x1f; - led0_ms_interval += 50; - if (++led0_toggle_count >= rate) { - led0_toggle_count = 0; - } - led_state(LED0, (led0_cur_state & (led0_toggle_count == 0 ? 1 : 2))); - } -} - /******************************************************************************/ // FLASH @@ -617,8 +511,9 @@ static int spiflash_page_erase(mp_spiflash_t *spif, uint32_t addr, uint32_t n_bl #endif int hw_page_erase(uint32_t addr, uint32_t *next_addr) { + mboot_state_change(MBOOT_STATE_ERASE_START, addr); + int ret = -1; - led0_state(LED0_STATE_ON); #if defined(MBOOT_SPIFLASH_ADDR) if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) { @@ -638,12 +533,14 @@ int hw_page_erase(uint32_t addr, uint32_t *next_addr) { ret = mboot_flash_page_erase(addr, next_addr); } - led0_state((ret == 0) ? LED0_STATE_SLOW_FLASH : LED0_STATE_SLOW_INVERTED_FLASH); + mboot_state_change(MBOOT_STATE_ERASE_END, ret); + return ret; } void hw_read(mboot_addr_t addr, size_t len, uint8_t *buf) { - led0_state(LED0_STATE_FAST_FLASH); + mboot_state_change(MBOOT_STATE_READ_START, addr); + #if defined(MBOOT_SPIFLASH_ADDR) if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) { mp_spiflash_read(MBOOT_SPIFLASH_SPIFLASH, addr - MBOOT_SPIFLASH_ADDR, len, buf); @@ -668,12 +565,14 @@ void hw_read(mboot_addr_t addr, size_t len, uint8_t *buf) { // Other addresses, just read directly from memory memcpy(buf, (void *)(uintptr_t)addr, len); } - led0_state(LED0_STATE_SLOW_FLASH); + + mboot_state_change(MBOOT_STATE_READ_END, 0); } int hw_write(uint32_t addr, const uint8_t *src8, size_t len) { + mboot_state_change(MBOOT_STATE_WRITE_START, addr); + int ret = -1; - led0_state(LED0_STATE_FAST_FLASH); #if defined(MBOOT_SPIFLASH_ADDR) if (MBOOT_SPIFLASH_ADDR <= addr && addr < MBOOT_SPIFLASH_ADDR + MBOOT_SPIFLASH_BYTE_SIZE) { ret = mp_spiflash_write(MBOOT_SPIFLASH_SPIFLASH, addr - MBOOT_SPIFLASH_ADDR, len, src8); @@ -691,7 +590,8 @@ int hw_write(uint32_t addr, const uint8_t *src8, size_t len) { dfu_context.error = MBOOT_ERROR_STR_INVALID_ADDRESS_IDX; } - led0_state((ret == 0) ? LED0_STATE_SLOW_FLASH : LED0_STATE_SLOW_INVERTED_FLASH); + mboot_state_change(MBOOT_STATE_WRITE_END, ret); + return ret; } @@ -707,9 +607,10 @@ int do_page_erase(uint32_t addr, uint32_t *next_addr) { void do_read(mboot_addr_t addr, size_t len, uint8_t *buf) { #if MBOOT_ENABLE_PACKING // Read disabled on packed (encrypted) mode. + mboot_state_change(MBOOT_STATE_READ_START, addr); dfu_context.status = DFU_STATUS_ERROR_FILE; dfu_context.error = MBOOT_ERROR_STR_INVALID_READ_IDX; - led0_state(LED0_STATE_SLOW_INVERTED_FLASH); + mboot_state_change(MBOOT_STATE_READ_END, -MBOOT_ERRNO_FLASH_READ_DISALLOWED); #else hw_read(addr, len, buf); #endif @@ -1360,71 +1261,6 @@ static int pyb_usbdd_shutdown(void) { /******************************************************************************/ // main -#if defined(MBOOT_BOARD_GET_RESET_MODE) - -static inline int mboot_get_reset_mode(void) { - return MBOOT_BOARD_GET_RESET_MODE(); -} - -#else - -#define RESET_MODE_NUM_STATES (4) -#define RESET_MODE_TIMEOUT_CYCLES (8) -#ifdef LED2 -#ifdef LED3 -#define RESET_MODE_LED_STATES 0x8421 -#else -#define RESET_MODE_LED_STATES 0x7421 -#endif -#else -#define RESET_MODE_LED_STATES 0x3210 -#endif - -static void usrbtn_init(void) { - mp_hal_pin_config(MICROPY_HW_USRSW_PIN, MP_HAL_PIN_MODE_INPUT, MICROPY_HW_USRSW_PULL, 0); -} - -static int usrbtn_state(void) { - return mp_hal_pin_read(MICROPY_HW_USRSW_PIN) == MICROPY_HW_USRSW_PRESSED; -} - -static int mboot_get_reset_mode(void) { - usrbtn_init(); - int reset_mode = BOARDCTRL_RESET_MODE_NORMAL; - if (usrbtn_state()) { - // Cycle through reset modes while USR is held - // Timeout is roughly 20s, where reset_mode=1 - systick_init(); - led_init(); - reset_mode = 0; - for (int i = 0; i < (RESET_MODE_NUM_STATES * RESET_MODE_TIMEOUT_CYCLES + 1) * 32; i++) { - if (i % 32 == 0) { - if (++reset_mode > RESET_MODE_NUM_STATES) { - reset_mode = BOARDCTRL_RESET_MODE_NORMAL; - } - uint8_t l = RESET_MODE_LED_STATES >> ((reset_mode - 1) * 4); - led_state_all(l); - } - if (!usrbtn_state()) { - break; - } - mp_hal_delay_ms(19); - } - // Flash the selected reset mode - for (int i = 0; i < 6; i++) { - led_state_all(0); - mp_hal_delay_ms(50); - uint8_t l = RESET_MODE_LED_STATES >> ((reset_mode - 1) * 4); - led_state_all(l); - mp_hal_delay_ms(50); - } - mp_hal_delay_ms(300); - } - return reset_mode; -} - -#endif - NORETURN static __attribute__((naked)) void branch_to_application(uint32_t r0, uint32_t bl_addr) { __asm volatile ( "ldr r2, [r1, #0]\n" // get address of stack pointer @@ -1566,11 +1402,13 @@ enter_bootloader: #endif if ((initial_r0 & 0xffffff80) == 0x70ad0080) { + mboot_state_change(MBOOT_STATE_FSLOAD_START, 0); + int ret = -1; #if MBOOT_FSLOAD // Application passed through elements, validate then process them const uint8_t *elem_end = elem_search(ELEM_DATA_START, ELEM_TYPE_END); if (elem_end != NULL && elem_end[-1] == 0) { - int ret = fsload_process(); + ret = fsload_process(); // If there is a valid ELEM_TYPE_STATUS element then store the status in the given location. const uint8_t *elem_status = elem_search(ELEM_DATA_START, ELEM_TYPE_STATUS); if (elem_status != NULL && elem_status[-1] == 4) { @@ -1580,8 +1418,8 @@ enter_bootloader: } } #endif + mboot_state_change(MBOOT_STATE_FSLOAD_END, ret); // Always reset because the application is expecting to resume - led_state_all(0); leave_bootloader(); } @@ -1598,8 +1436,7 @@ enter_bootloader: i2c_init(initial_r0); #endif - led_state_all(0); - led0_state(LED0_STATE_SLOW_FLASH); + mboot_state_change(MBOOT_STATE_DFU_START, 0); #if MBOOT_USB_RESET_ON_DISCONNECT bool has_connected = false; @@ -1633,8 +1470,9 @@ enter_bootloader: #endif } + mboot_state_change(MBOOT_STATE_DFU_END, 0); + // Shutdown and leave the bootloader. - led_state_all(0); mp_hal_delay_ms(50); pyb_usbdd_shutdown(); #if defined(MBOOT_I2C_SCL) diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h index 0e04f67f4a..0984fd9ac5 100644 --- a/ports/stm32/mboot/mboot.h +++ b/ports/stm32/mboot/mboot.h @@ -46,9 +46,31 @@ #define MBOOT_ADDRESS_SPACE_64BIT (0) #endif +// These enum values are passed as the first argument to mboot_state_change() to +// notify of a change in state of the bootloader activity. This function has a +// default implementation in ui.c but can be overridden by a board. Some states +// have an argument passed along as the second argument to mboot_state_change(). +// If this argument is unused then 0 is passed in. A result of an operation is +// 0 for success and <0 for failure, with a failure being either an MP_Exxx code +// or MBOOT_ERRNO_xxx code. +typedef enum { + MBOOT_STATE_DFU_START, // arg: unused + MBOOT_STATE_DFU_END, // arg: unused + MBOOT_STATE_FSLOAD_START, // arg: unused + MBOOT_STATE_FSLOAD_END, // arg: result of fsload operation + MBOOT_STATE_FSLOAD_PROGRESS, // arg: total bytes processed so far, high bit set when doing write pass + MBOOT_STATE_ERASE_START, // arg: address of erase + MBOOT_STATE_ERASE_END, // arg: result of erase + MBOOT_STATE_READ_START, // arg: address of read + MBOOT_STATE_READ_END, // arg: result of read + MBOOT_STATE_WRITE_START, // arg: address of write + MBOOT_STATE_WRITE_END, // arg: result of write +} mboot_state_t; + enum { MBOOT_ERRNO_FLASH_ERASE_DISALLOWED = 200, MBOOT_ERRNO_FLASH_ERASE_FAILED, + MBOOT_ERRNO_FLASH_READ_DISALLOWED, MBOOT_ERRNO_FLASH_WRITE_DISALLOWED, MBOOT_ERRNO_DFU_INVALID_HEADER = 210, @@ -98,10 +120,12 @@ typedef uint64_t mboot_addr_t; typedef uint32_t mboot_addr_t; #endif +extern volatile uint32_t systick_ms; extern uint8_t _estack[ELEM_DATA_SIZE]; void systick_init(void); void led_init(void); +void led0_update(void); void SystemClock_Config(void); uint32_t get_le32(const uint8_t *b); @@ -132,4 +156,20 @@ static inline void mboot_entry_init(uint32_t *initial_r0) { #endif } +#if defined(MBOOT_BOARD_GET_RESET_MODE) +static inline int mboot_get_reset_mode(void) { + return MBOOT_BOARD_GET_RESET_MODE(); +} +#else +int mboot_get_reset_mode(void); +#endif + +#if defined(MBOOT_BOARD_STATE_CHANGE) +static inline void mboot_state_change(mboot_state_t state, uint32_t arg) { + return MBOOT_BOARD_STATE_CHANGE(state, arg); +} +#else +void mboot_state_change(mboot_state_t state, uint32_t arg); +#endif + #endif // MICROPY_INCLUDED_STM32_MBOOT_MBOOT_H diff --git a/ports/stm32/mboot/ui.c b/ports/stm32/mboot/ui.c new file mode 100644 index 0000000000..36cba1cf50 --- /dev/null +++ b/ports/stm32/mboot/ui.c @@ -0,0 +1,255 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017-2022 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. + */ + +#include "mboot.h" +#include "ports/stm32/boardctrl.h" + +/******************************************************************************/ +// LED + +#if defined(MBOOT_LED1) +#define LED0 MBOOT_LED1 +#elif defined(MICROPY_HW_LED1) +#define LED0 MICROPY_HW_LED1 +#endif + +#if defined(MBOOT_LED2) +#define LED1 MBOOT_LED2 +#elif defined(MICROPY_HW_LED2) +#define LED1 MICROPY_HW_LED2 +#endif + +#if defined(MBOOT_LED3) +#define LED2 MBOOT_LED3 +#elif defined(MICROPY_HW_LED3) +#define LED2 MICROPY_HW_LED3 +#endif + +#if defined(MBOOT_LED4) +#define LED3 MBOOT_LED4 +#elif defined(MICROPY_HW_LED4) +#define LED3 MICROPY_HW_LED4 +#endif + +// For flashing states: bit 0 is "active", bit 1 is "inactive", bits 2-6 are flash rate. +typedef enum { + LED0_STATE_OFF = 0, + LED0_STATE_ON = 1, + LED0_STATE_SLOW_FLASH = (20 << 2) | 1, + LED0_STATE_FAST_FLASH = (2 << 2) | 1, + LED0_STATE_SLOW_INVERTED_FLASH = (20 << 2) | 2, +} led0_state_t; + +static led0_state_t led0_cur_state = LED0_STATE_OFF; +static uint32_t led0_ms_interval = 0; +static int led0_toggle_count = 0; + +MP_WEAK void led_init(void) { + #if defined(MBOOT_BOARD_LED_INIT) + // Custom LED init function provided by the board. + MBOOT_BOARD_LED_INIT(); + #else + // Init LEDs using GPIO calls. + mp_hal_pin_output(LED0); + #ifdef LED1 + mp_hal_pin_output(LED1); + #endif + #ifdef LED2 + mp_hal_pin_output(LED2); + #endif + #ifdef LED3 + mp_hal_pin_output(LED3); + #endif + #endif + + led0_cur_state = LED0_STATE_OFF; +} + +MP_WEAK void led_state(uint32_t led, int val) { + #if defined(MBOOT_BOARD_LED_STATE) + // Custom LED state function provided by the board. + return MBOOT_BOARD_LED_STATE(led, val); + #else + // Set LEDs using GPIO calls. + if (val) { + MICROPY_HW_LED_ON(led); + } else { + MICROPY_HW_LED_OFF(led); + } + #endif +} + +void led_state_all(unsigned int mask) { + led_state(LED0, mask & 1); + #ifdef LED1 + led_state(LED1, mask & 2); + #endif + #ifdef LED2 + led_state(LED2, mask & 4); + #endif + #ifdef LED3 + led_state(LED3, mask & 8); + #endif +} + +void led0_state(led0_state_t state) { + led0_cur_state = state; + if (state == LED0_STATE_OFF || state == LED0_STATE_ON) { + led_state(LED0, state); + } +} + +void led0_update(void) { + if (led0_cur_state != LED0_STATE_OFF && systick_ms - led0_ms_interval > 50) { + uint8_t rate = (led0_cur_state >> 2) & 0x1f; + led0_ms_interval += 50; + if (++led0_toggle_count >= rate) { + led0_toggle_count = 0; + } + led_state(LED0, (led0_cur_state & (led0_toggle_count == 0 ? 1 : 2))); + } +} + +/******************************************************************************/ +// User button + +#if !defined(MBOOT_BOARD_GET_RESET_MODE) + +#define RESET_MODE_NUM_STATES (4) +#define RESET_MODE_TIMEOUT_CYCLES (8) +#ifdef LED2 +#ifdef LED3 +#define RESET_MODE_LED_STATES 0x8421 +#else +#define RESET_MODE_LED_STATES 0x7421 +#endif +#else +#define RESET_MODE_LED_STATES 0x3210 +#endif + +static void usrbtn_init(void) { + mp_hal_pin_config(MICROPY_HW_USRSW_PIN, MP_HAL_PIN_MODE_INPUT, MICROPY_HW_USRSW_PULL, 0); +} + +static int usrbtn_state(void) { + return mp_hal_pin_read(MICROPY_HW_USRSW_PIN) == MICROPY_HW_USRSW_PRESSED; +} + +int mboot_get_reset_mode(void) { + usrbtn_init(); + int reset_mode = BOARDCTRL_RESET_MODE_NORMAL; + if (usrbtn_state()) { + // Cycle through reset modes while USR is held + // Timeout is roughly 20s, where reset_mode=1 + systick_init(); + led_init(); + reset_mode = 0; + for (int i = 0; i < (RESET_MODE_NUM_STATES * RESET_MODE_TIMEOUT_CYCLES + 1) * 32; i++) { + if (i % 32 == 0) { + if (++reset_mode > RESET_MODE_NUM_STATES) { + reset_mode = BOARDCTRL_RESET_MODE_NORMAL; + } + uint8_t l = RESET_MODE_LED_STATES >> ((reset_mode - 1) * 4); + led_state_all(l); + } + if (!usrbtn_state()) { + break; + } + mp_hal_delay_ms(19); + } + // Flash the selected reset mode + for (int i = 0; i < 6; i++) { + led_state_all(0); + mp_hal_delay_ms(50); + uint8_t l = RESET_MODE_LED_STATES >> ((reset_mode - 1) * 4); + led_state_all(l); + mp_hal_delay_ms(50); + } + mp_hal_delay_ms(300); + } + return reset_mode; +} + +#endif + +/******************************************************************************/ +// State change + +#if !defined(MBOOT_BOARD_STATE_CHANGE) + +void mboot_state_change(mboot_state_t state, uint32_t arg) { + switch (state) { + case MBOOT_STATE_DFU_START: + led_state_all(0); + led0_state(LED0_STATE_SLOW_FLASH); + break; + + case MBOOT_STATE_DFU_END: + led_state_all(0); + break; + + case MBOOT_STATE_FSLOAD_START: + break; + + case MBOOT_STATE_FSLOAD_END: + // Flash LEDs based on success/failure of update + for (int i = 0; i < 4; ++i) { + if (arg == 0) { + led_state_all(7); + } else { + led_state_all(1); + } + mp_hal_delay_ms(100); + led_state_all(0); + mp_hal_delay_ms(100); + } + break; + + case MBOOT_STATE_FSLOAD_PROGRESS: + break; + + case MBOOT_STATE_ERASE_START: + led0_state(LED0_STATE_ON); + break; + + case MBOOT_STATE_READ_START: + case MBOOT_STATE_WRITE_START: + led0_state(LED0_STATE_FAST_FLASH); + break; + + case MBOOT_STATE_ERASE_END: + case MBOOT_STATE_READ_END: + case MBOOT_STATE_WRITE_END: + if (arg == 0) { + led0_state(LED0_STATE_SLOW_FLASH); + } else { + led0_state(LED0_STATE_SLOW_INVERTED_FLASH); + } + break; + } +} + +#endif