diff --git a/README.md b/README.md index b75bb398..4625a8e0 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,8 @@ In OTA DFU mode: The OTA fallback is intentional and prevents devices from getting stuck in UF2 mode after failed OTA updates. +A device with a valid application that sits in DFU for 6 minutes with no activity (no BLE connection or DFU traffic, no drive access) boots its application. A device with no valid application stays in DFU. + If the bootloader recorded a fault since the last UF2 drive session, `INFO_UF2.TXT` shows a `Last-Fault:` line with the fault id, program counter, and info word. It appears only if the next boot reaches the drive before an application boots, since the application's RAM use wipes the record. --- diff --git a/changelog.md b/changelog.md index 1033984b..c04c9424 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ it and is upstream Adafruit history, kept for provenance. ## OTAFIX 2.6 +- A board with a valid application that sits in DFU for 6 minutes with no activity boots its application. A BLE connection or DFU write, a UF2 drive read or write, USB enumeration, and serial DFU traffic each restart the 6 minutes; a board with no valid application keeps waiting. - A SoftDevice flash timeout during a BLE or serial transfer, or the activation settings save, is retried every 100 ms, up to 20 times, instead of resetting the bootloader. - `INFO_UF2.TXT` shows a `Last-Fault: id=0x.. pc=0x.. info=0x..` line for a SoftDevice or SDK fault the bootloader recorded since the last UF2 drive session. It appears only if the next boot reaches the drive before an application boots, since the application's RAM use wipes the record. The record lives in a 16-byte RAM region behind a magic word and is cleared once printed; for an SDK error `info` is the error code. - A device with no valid application that is plugged into a USB host comes up as a UF2 drive showing NO FIRMWARE instead of waiting for a phone. On battery (no VBUS) it goes straight to the BLE OTA wait, and with VBUS but no host enumerating within 3 s (a charger or power bank) it falls through to the same wait. A cabled board with no application returns to the drive after an aborted copy, and after an eject on boards built with `UF2_EXIT_ON_EJECT` (`mesh_tracker_x1`, `t1000_e`). When the cable is pulled while the drive is up (VBUS gone for 250 ms), it goes to the BLE OTA wait. Plugging a cable into a board with no application that is waiting for BLE, with no phone connected, brings the drive up (VBUS present for 250 ms); a cable already present when the wait began, or a phone connected, leaves the wait alone. diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c index d99b12fd..3a117c55 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.c @@ -54,6 +54,17 @@ static bool m_cancel_timeout_on_usb; /**< If set the timeout is cancelled when U APP_TIMER_DEF( _dfu_startup_timer ); volatile bool dfu_startup_packet_received = false; +#ifndef DFU_INACTIVITY_TIMEOUT_MS +#define DFU_INACTIVITY_TIMEOUT_MS (6 * 60 * 1000) +#endif +#define DFU_INACTIVITY_TICK_MS 1000 + +// dfu_activity() only sets a flag, so it is cheap from any context; a 1 s tick turns it into the idle count. +APP_TIMER_DEF( _dfu_inactivity_timer ); +static volatile bool m_dfu_activity; +static bool m_inactivity_armed; +static uint32_t m_inactivity_idle_ms; + /**@brief Function for handling callbacks from pstorage module. * * @details Handles pstorage results for clear and storage operation. For detailed description of @@ -99,6 +110,43 @@ static void dfu_startup_timer_handler(void * p_context) } } +void dfu_activity(void) +{ + m_dfu_activity = true; +} + +// A session that sat idle with a valid application ends like the startup timeout, so main() boots the application. +// A started transfer has invalidated bank 0, and with no application the wait stays. +static void dfu_inactivity_timer_handler(void * p_context) +{ + if (!m_inactivity_armed) return; + + if (m_dfu_activity) + { + m_dfu_activity = false; + m_inactivity_idle_ms = 0; + } + else + { + m_inactivity_idle_ms += DFU_INACTIVITY_TICK_MS; + } + + if (m_inactivity_idle_ms < DFU_INACTIVITY_TIMEOUT_MS) + { + app_timer_start(_dfu_inactivity_timer, APP_TIMER_TICKS(DFU_INACTIVITY_TICK_MS), NULL); + return; + } + + m_inactivity_armed = false; + if (m_update_status == BOOTLOADER_UPDATING && bootloader_app_is_valid()) + { + dfu_update_status_t update_status; + update_status.status_code = DFU_TIMEOUT; + + bootloader_dfu_update_process(update_status); + } +} + /**@brief Function for waiting for events. * * @details This function will place the chip in low power mode while waiting for events from @@ -355,8 +403,17 @@ uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms, bool cancel_timeout err_code = dfu_transport_serial_update_start(); } + m_dfu_activity = false; + m_inactivity_idle_ms = 0; + m_inactivity_armed = true; + app_timer_create(&_dfu_inactivity_timer, APP_TIMER_MODE_SINGLE_SHOT, dfu_inactivity_timer_handler); + app_timer_start(_dfu_inactivity_timer, APP_TIMER_TICKS(DFU_INACTIVITY_TICK_MS), NULL); + wait_for_events(); + m_inactivity_armed = false; + app_timer_stop(_dfu_inactivity_timer); + return err_code; } diff --git a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h index d861c6fc..a0b9a6a7 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h +++ b/lib/sdk11/components/libraries/bootloader_dfu/bootloader.h @@ -44,6 +44,12 @@ bool bootloader_app_is_valid(void); */ uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms, bool cancel_timeout_on_usb); +/**@brief Function for telling the DFU inactivity timeout that the session is not idle. + * + * @details Safe to call from any context, before or during a session. + */ +void dfu_activity(void); + /**@brief Function for exiting bootloader and booting into application. * * @details This function will disable SoftDevice and all interrupts before jumping to application. diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c index 4cc3459f..e640d99c 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_ble.c @@ -708,6 +708,8 @@ static void on_dfu_evt(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt) uint32_t err_code; ble_dfu_resp_val_t resp_val; + dfu_activity(); + switch (p_evt->ble_dfu_evt_type) { case BLE_DFU_VALIDATE: @@ -923,6 +925,7 @@ static void on_ble_evt(ble_evt_t * p_ble_evt) switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: + dfu_activity(); m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; m_is_advertising = false; diff --git a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c index 71f08526..99ecb2db 100644 --- a/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c +++ b/lib/sdk11/components/libraries/bootloader_dfu/dfu_transport_serial.c @@ -18,6 +18,7 @@ #include "app_util.h" #include "hci_transport.h" #include "app_timer.h" +#include "bootloader.h" #include "app_scheduler.h" #include "boards.h" @@ -200,6 +201,7 @@ static void process_dfu_packet(void * p_event_data, uint16_t event_size) // Adafruit modification for startup dfu extern bool dfu_startup_packet_received; dfu_startup_packet_received = true; + dfu_activity(); while (false == DATA_QUEUE_EMPTY()) { diff --git a/src/usb/msc_uf2.c b/src/usb/msc_uf2.c index 499bd583..d7261900 100644 --- a/src/usb/msc_uf2.c +++ b/src/usb/msc_uf2.c @@ -155,6 +155,7 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { (void) lun; + dfu_activity(); memset(buffer, 0, bufsize); // since we return block size each, offset should always be zero @@ -179,6 +180,7 @@ int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buf int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { (void) lun; + dfu_activity(); uint32_t count = 0; while ( count < bufsize ) diff --git a/src/usb/usb.c b/src/usb/usb.c index a600c742..7635a7cc 100644 --- a/src/usb/usb.c +++ b/src/usb/usb.c @@ -123,6 +123,7 @@ bool usb_host_mounted(void) { void tud_mount_cb(void) { _host_mounted = true; + dfu_activity(); led_state(STATE_USB_MOUNTED); }