Skip to content

Enhancement: Status/Progress Notification #2636

@JayToltTech

Description

@JayToltTech

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:

  1. :686 — Revert swap, once per sector after boot_swap_sectors_revert()
  2. :708 — Forward swap, once per sector after boot_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);
    }
}
#endif

Requires 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions