From 3249f41dd965d366a71b769ab42aa8ee76ebd9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Jos=C3=A9=20Tagle?= Date: Tue, 27 Jan 2026 22:41:20 -0300 Subject: [PATCH] In case a RESET command is received in the middle of a OTA DFU, reboot to bootloader to allow the DFU app to recover previously interrupted transfers, instead of reverting to the last working firmware and launching it --- .../libraries/bootloader_dfu/bootloader.c | 14 ++++++++--- .../libraries/bootloader_dfu/bootloader.h | 7 ++++++ .../libraries/bootloader_dfu/dfu_dual_bank.c | 9 ++++++++ .../bootloader_dfu/dfu_single_bank.c | 8 +++++++ .../libraries/bootloader_dfu/dfu_types.h | 2 ++ src/main.c | 23 +++++++++++++++---- src/usb/msc_uf2.c | 4 +++- src/usb/uf2/uf2cfg.h | 2 +- 8 files changed, 60 insertions(+), 9 deletions(-) diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index 7955790..9a8e55e 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -44,7 +44,8 @@ typedef enum BOOTLOADER_SETTINGS_SAVING, /**< Bootloader status for indicating that saving of bootloader settings is in progress. */ BOOTLOADER_COMPLETE, /**< Bootloader status for indicating that all operations for the update procedure has completed and it is safe to reset the system. */ BOOTLOADER_TIMEOUT, /**< Bootloader status field for indicating that a timeout has occured and current update process should be aborted. */ - BOOTLOADER_RESET, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted. */ + BOOTLOADER_SYS_RESET, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted. */ + BOOTLOADER_RESET_TO_SELF, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted and the bootloader must be reentered. */ } bootloader_status_t; static pstorage_handle_t m_bootsettings_handle; /**< Pstorage handle to use for registration and identifying the bootloader module on subsequent calls to the pstorage module for load and store of bootloader setting in flash. */ @@ -136,7 +137,8 @@ static void wait_for_events(void) if ((m_update_status == BOOTLOADER_COMPLETE) || (m_update_status == BOOTLOADER_TIMEOUT) || - (m_update_status == BOOTLOADER_RESET) ) + (m_update_status == BOOTLOADER_SYS_RESET) || + (m_update_status == BOOTLOADER_RESET_TO_SELF)) { // When update has completed or a timeout/reset occured we will return. return; @@ -320,7 +322,9 @@ void bootloader_dfu_update_process(dfu_update_status_t update_status) } else if (update_status.status_code == DFU_RESET) { - m_update_status = BOOTLOADER_RESET; + m_update_status = update_status.restart_into_bootloader == false + ? BOOTLOADER_SYS_RESET + : BOOTLOADER_RESET_TO_SELF; } else { @@ -328,6 +332,10 @@ void bootloader_dfu_update_process(dfu_update_status_t update_status) } } +bool bootloader_must_reset_to_self(void) +{ + return m_update_status == BOOTLOADER_RESET_TO_SELF; +} uint32_t bootloader_init(void) { diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h index 5f94e7b..9b6f754 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h @@ -32,6 +32,13 @@ */ uint32_t bootloader_init(void); +/**@brief Function to check if bootloader must be reentered after a reset + * @details: This function can only be called after bootloader_dfu_start returns + * @retval true If device must reboot to the bootloader + * @retval false If device must reboot to the application, if possible + */ +bool bootloader_must_reset_to_self(void); + /**@brief Function for validating application region in flash. * @retval true If Application region is valid. * @retval false If Application region is not valid. diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c index cba833d..32cc71a 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_dual_bank.c @@ -82,6 +82,7 @@ static void pstorage_callback_handler(pstorage_handle_t * p_handle, APP_ERROR_CHECK(result); } + /**@brief Function for handling the DFU timeout. * * @param[in] p_context The timeout context. @@ -710,6 +711,14 @@ void dfu_reset(void) update_status.status_code = DFU_RESET; + // If we are still receiving packets, and we are expecting more of them, + // and we receive the RESET command, this means the DFU app is trying + // to interrupt and recover a failed previous attempt of FLASHing a new + // application. Make sure to reboot into DFU mode instead of starting + // a previously stored application (specially valid for double bank + // bootloaders + update_status.restart_into_bootloader = (m_dfu_state == DFU_STATE_RX_DATA_PKT && m_data_received != m_image_size); + bootloader_dfu_update_process(update_status); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c index af25970..b53bf95 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_single_bank.c @@ -607,6 +607,14 @@ void dfu_reset(void) update_status.status_code = DFU_RESET; + // If we are still receiving packets, and we are expecting more of them, + // and we receive the RESET command, this means the DFU app is trying + // to interrupt and recover a failed previous attempt of FLASHing a new + // application. Make sure to reboot into DFU mode instead of starting + // a previously stored application (specially valid for double bank + // bootloaders + update_status.restart_into_bootloader = (m_dfu_state == DFU_STATE_RX_DATA_PKT && m_data_received != m_image_size); + bootloader_dfu_update_process(update_status); } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h index f86a684..e186e05 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_types.h @@ -24,6 +24,7 @@ #define DFU_TYPES_H__ #include +#include #include "nrf_sdm.h" #include "nrf_mbr.h" #include "nrf.h" @@ -154,6 +155,7 @@ typedef struct uint32_t bl_size; /**< Size of the recieved BootLoader. */ uint32_t app_size; /**< Size of the recieved Application. */ uint32_t sd_image_start; /**< Location in flash where the received SoftDevice image is stored. */ + bool restart_into_bootloader; /**< If the chip must be reset and must reenter bootloader mode. */ } dfu_update_status_t; /**@brief Update complete handler type. */ diff --git a/src/main.c b/src/main.c index 2e2fc0c..819b9e2 100644 --- a/src/main.c +++ b/src/main.c @@ -134,6 +134,7 @@ uint32_t* dbl_reset_mem = ((uint32_t*) DFU_DBL_RESET_MEM); // true if ble, false if serial bool _ota_dfu = false; bool _ota_connected = false; +bool _ota_was_active = false; bool _sd_inited = false; bool is_ota(void) { @@ -192,6 +193,10 @@ int main(void) { // Return when DFU process is complete (or not entered at all) check_dfu_mode(); + // Check if we must reenter the bootloader after reset, instead of + // launching the user application + bool bootloader_must_be_reentered = bootloader_must_reset_to_self(); + // Reset peripherals board_teardown(); @@ -202,7 +207,9 @@ int main(void) { * - sd_softdevice_vector_table_base_set(APP_ADDR) * - jump to App reset */ - if (bootloader_app_is_valid() && !bootloader_dfu_sd_in_progress()) { + if (!bootloader_must_be_reentered && + bootloader_app_is_valid() && + !bootloader_dfu_sd_in_progress()) { PRINTF("App is valid\r\n"); if (is_sd_existed()) { // MBR forward IRQ to SD (if not already) @@ -220,9 +227,14 @@ int main(void) { bootloader_app_start(); } - // No application was loaded, reset the system with the OTA DFU update - NRF_POWER->GPREGRET = DFU_MAGIC_OTA_RESET; - + // No application was loaded or we need to reenter the bootloader + + // Reset the system with the OTA DFU update in case we were in it, + // to allow completion of FLASHING, otherwise, default to normal reset + if (_ota_was_active) { + NRF_POWER->GPREGRET = DFU_MAGIC_OTA_RESET; + } + NVIC_SystemReset(); } @@ -440,6 +452,9 @@ uint32_t proc_ble(void) { sd_ble_gap_phy_update(evt->evt.gap_evt.conn_handle, &phys); _ota_connected = true; + + // Remember someone connected to BLE + _ota_was_active = true; led_state(STATE_BLE_CONNECTED); break; } diff --git a/src/usb/msc_uf2.c b/src/usb/msc_uf2.c index 4b6817f..96d705b 100644 --- a/src/usb/msc_uf2.c +++ b/src/usb/msc_uf2.c @@ -171,6 +171,7 @@ void tud_msc_write10_complete_cb(uint8_t lun) dfu_update_status_t update_status; memset(&update_status, 0, sizeof(dfu_update_status_t )); update_status.status_code = DFU_RESET; + update_status.restart_into_bootloader = false; bootloader_dfu_update_process(update_status); @@ -195,9 +196,10 @@ void tud_msc_write10_complete_cb(uint8_t lun) { // update bootloader always end with reset update_status.status_code = DFU_RESET; + update_status.restart_into_bootloader = false; // Location of current stored new bootloader - uint32_t * new_bootloader = (uint32_t *) BOOTLOADER_ADDR_NEW_RECIEVED; + uint32_t * new_bootloader = (uint32_t *) BOOTLOADER_ADDR_NEW_RECEIVED; PRINT_HEX(new_bootloader); diff --git a/src/usb/uf2/uf2cfg.h b/src/usb/uf2/uf2cfg.h index e5a6398..c23a12e 100644 --- a/src/usb/uf2/uf2cfg.h +++ b/src/usb/uf2/uf2cfg.h @@ -32,4 +32,4 @@ #define BOOTLOADER_ADDR_END BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS // Address where new bootloader is written before activation (skip application data) -#define BOOTLOADER_ADDR_NEW_RECIEVED (USER_FLASH_END-DFU_BL_IMAGE_MAX_SIZE) +#define BOOTLOADER_ADDR_NEW_RECEIVED (USER_FLASH_END-DFU_BL_IMAGE_MAX_SIZE)