-
Notifications
You must be signed in to change notification settings - Fork 898
Description
Objective
I would like to introduce status & progress notification callback hooks so that I can provide feedback during long running MCUBoot operations such as Serial Recovery uploads, flash swaps between slots, and DFU uploads.
High Level Design
Like many MCU devices, my device has a set of status LEDs that I use to help my customer understand what is going on in the device, which I use in different combinations of lighting and blinking to signal different states. MCUBoot has native support to turn on one of these LEDs solidly. I would like to expose callback hooks, gated behind CONFIG_MCUBOOT_PROGRESS_HOOKS, so that mcuboot extensions can do more than just turn on one LED solidly, especially during long running operations that may take 30 seconds or 5 minutes.
I do not propose to implement logic around signaling or blinking, as this can be very device dependent. Just the hooks by which this logic can be easily added to the MCUBoot workflows.
Proposal Draft
Header: boot/bootutil/include/bootutil/mcuboot_status.h
Status Change Hook (CONFIG_MCUBOOT_ACTION_HOOKS)
typedef enum {
MCUBOOT_STATUS_STARTUP = 0,
MCUBOOT_STATUS_UPGRADING,
MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND,
MCUBOOT_STATUS_NO_BOOTABLE_IMAGE_FOUND,
MCUBOOT_STATUS_BOOT_FAILED,
MCUBOOT_STATUS_USB_DFU_WAITING,
MCUBOOT_STATUS_USB_DFU_ENTERED,
MCUBOOT_STATUS_USB_DFU_TIMED_OUT,
MCUBOOT_STATUS_SERIAL_DFU_ENTERED,
} mcuboot_status_type_t;
void mcuboot_status_change(mcuboot_status_type_t status);Call sites (boot/zephyr/main.c unless noted):
| Line | Status | When |
|---|---|---|
| 563 | STARTUP |
Beginning of main() |
| 523 | SERIAL_DFU_ENTERED |
Entering serial recovery |
| 599 | USB_DFU_ENTERED |
USB DFU starts (GPIO-triggered) |
| 615 | USB_DFU_WAITING |
Polling for USB host |
| 618 | USB_DFU_TIMED_OUT |
USB wait expires |
| 674 | NO_BOOTABLE_IMAGE_FOUND |
Image validation fails |
| 712 | BOOTABLE_IMAGE_FOUND |
Before jumping to app |
| 733 | BOOT_FAILED |
Fatal error |
loader.c:1783 |
UPGRADING |
has_upgrade is true, before swap loop |
Progress Hook (CONFIG_MCUBOOT_PROGRESS_HOOKS)
typedef enum {
MCUBOOT_PROGRESS_SWAP = 0,
MCUBOOT_PROGRESS_SERIAL,
MCUBOOT_PROGRESS_USB_DFU,
} mcuboot_progress_type_t;
void mcuboot_progress(mcuboot_progress_type_t type, uint32_t offset, uint32_t total);MCUBOOT_PROGRESS_SWAP
Offset/total are sector indices (not bytes).
Call sites in boot/bootutil/src/swap_offset.c:
:686— Revert swap, once per sector afterboot_swap_sectors_revert():708— Forward swap, once per sector afterboot_swap_sectors()
Both inside boot_swap_image() (~line 583). idx starts at 0 (or resumes from bs->idx - BOOT_STATUS_IDX_0) and increments to last_idx. Percentage = idx * 100 / last_idx.
MCUBOOT_PROGRESS_SERIAL
Offset/total are byte offsets. Total is the exact image size (MCUmgr protocol sends it in the first upload chunk). Covers both UART and CDC-ACM serial transports.
Call sites in boot/boot_serial/src/boot_serial.c, inside bs_upload() (~line 897):
:1168 — During upload, fires on 4KB sector boundary crossings:
uint32_t prev_sector = curr_off >> 12;
curr_off += img_chunk_len + rem_bytes;
if ((curr_off >> 12) != prev_sector) {
mcuboot_progress(MCUBOOT_PROGRESS_SERIAL, curr_off, img_size);
}:1171 — Upload completion, guarantees final 100% callback:
if (curr_off == img_size) {
mcuboot_progress(MCUBOOT_PROGRESS_SERIAL, img_size, img_size);
}MCUBOOT_PROGRESS_USB_DFU
Offset is bytes written, total is flash area capacity (not actual image size — USB DFU 1.1 protocol doesn't communicate image size upfront; host sends data until a zero-length DFU_DNLOAD signals completion).
Target: Zephyr's new USB stack flash backend at zephyr/subsys/usb/device_next/class/usbd_dfu_flash.c.
Inside dfu_flash_write() (line 89), after successful flash_img_buffered_write() at line 116. Same 4KB sector-boundary pattern as serial:
data->last_block = block;
data->downloaded += size;
#if defined(CONFIG_MCUBOOT_PROGRESS_HOOKS)
{
uint32_t prev_sector = (data->downloaded - size) >> 12;
if ((data->downloaded >> 12) != prev_sector || flush) {
mcuboot_progress(MCUBOOT_PROGRESS_USB_DFU,
data->downloaded, data->flash_size);
}
}
#endifRequires adding a uint32_t flash_size field to struct usbd_dfu_flash_data and caching fa->fa_size during the block == 0 init path.
Available variables at call site:
| Variable | Source | Meaning |
|---|---|---|
data->downloaded |
Struct field, incremented per block | Cumulative bytes received |
flash_img_bytes_written(&data->fi_ctx) |
Zephyr API | Cumulative bytes flushed to flash |
data->flash_size |
Cached from fa->fa_size at init |
Full flash area capacity |
size |
Function parameter | Current chunk size; 0 = download complete (flush) |
Thread context: Called from USB class control handler context. Callback must be fast and non-blocking.
Summary
| Type | offset | total | Granularity | Source file |
|---|---|---|---|---|
SWAP |
sector index | last sector index | every sector | boot/bootutil/src/swap_offset.c |
SERIAL |
bytes written | image size (exact) | 4KB boundaries | boot/boot_serial/src/boot_serial.c |
USB_DFU |
bytes written | flash area size (approx) | 4KB boundaries | zephyr/.../usbd_dfu_flash.c |