Note

This is the documentation for the latest development branch and may refer to features that are not available in released versions. If you are looking for the documentation for a specific release, use the drop-down menu on the left and select the desired version.

K230 OTA User Guide#

This document describes the partition layout, overall design, and application-layer API for OTA (Over-The-Air) upgrades on the K230 platform, to help you integrate and use OTA in your RT-Smart application.

Relevant source locations:

  • Application-layer wrapper: src/rtsmart/libs/rtsmart_hal/components/k230_ota/k230_ota.c / k230_ota.h

  • Test case: src/rtsmart/libs/testcases/rtsmart_hal/test_ota.c

  • Kernel OTA driver: src/rtsmart/rtsmart/kernel/bsp/maix3/components/ota/ota.c

  • SD card partition layout: boards/k230_canmv_xxxxx/genimage-sdcard.cfg


Partition Layout#

Using boards/k230_canmv_01studio/genimage-sdcard.cfg as an example, the core OTA-related partitions are:

Partition

Offset / Size

load/boot

Content

Notes

TOC

0xe0000

-

TOC table (up to 16 entries)

Shared by U-Boot and OTA

ota_meta

0xf0000, 0x800

-

Slot A/B version blocks

2×512 B, A first then B

spl

0x100000

-

U-Boot SPL

Boot code

uboot_env

0x1e0000

-

U-Boot environment

uboot

0x200000

-

U-Boot main image

rtt_a

10 MB, 20 MB

load=1, boot=0x3

opensbi_rtt_system.bin

Slot A RT-Smart system

rtapp_a

30 MB, 30 MB

load=1

rtapp.elf.gz

Slot A application

rtt_b

60 MB, 20 MB

load=1, boot=0x3

opensbi_rtt_system.bin

Slot B RT-Smart system

rtapp_b

80 MB, 30 MB

load=1

rtapp.elf.gz

Slot B application

bin/app

After 110 MB

MBR FAT

User bin/app partition

Not directly related to OTA

Notes:

  • The ota_meta partition is reserved in the TOC only; its initial content is empty.

  • Partition names must be consistent among the TOC, the RT-Smart OTA driver, and the packaging scripts:

    • TOC entry names: "ota_meta", "rtt_a", "rtapp_a", "rtt_b", "rtapp_b", etc.

    • The OTA driver locates target addresses using names such as ota_find_partition("rtt_a").


Design Overview#

The K230 OTA solution is based on an A/B dual-slot design combined with a TOC (Table of Contents) and a version metadata block (ota_meta) stored on the boot medium.

  • Dual system partitions

    • rtt_a / rtapp_a: Slot A kernel (RT-Smart) and application (RTAPP).

    • rtt_b / rtapp_b: Slot B kernel and application.

  • TOC (partition table)

    • Fixed at boot medium offset 0x000e0000 (K230_TOC_OFFSET).

    • Each entry is 64 bytes; maximum 16 entries (K230_TOC_MAX_ENTRIES).

    • Shared between U-Boot and the RT-Smart kernel.

  • OTA metadata (ota_meta)

    • A ota_meta partition is pre-allocated during image packaging. Its offset and size are defined in genimage-sdcard.cfg:

      • offset 0x000f0000, size 0x800 bytes.

    • Split evenly into two blocks:

      • First 512 B: Slot A version block

      • Last 512 B: Slot B version block

    • Version block structure (ota_slot_meta):

      • magic: fixed value 0x4f544156u (“OTAV”)

      • version: monotonically increasing slot version number

      • crc32: checksum covering the entire structure (computed with the crc32 field zeroed)

  • OTA image format (kdimg)

    • Uses a custom kdimg container:

      • 512-byte header (KDIMG_HDR_MAGIC + CRC32 + metadata)

      • Followed by one or more kd_img_part partition descriptors (rtt, rtapp, etc.)

      • Actual content starts at KDIMG_CONTENT_START_OFF (64 KB), stored per partition

    • The OTA driver streams the kdimg, writing each partition’s content to the corresponding rtt_x / rtapp_x physical region.

  • Boot flow and slot selection

    • At boot, U-Boot SPL:

      1. Parses partition information from the TOC.

      2. Reads Slot A and Slot B version blocks from ota_meta.

      3. Selects the slot with the newer version as the active slot and loads its rtt_x / rtapp_x.

      4. If both slots are invalid (bad magic or CRC), defaults to Slot A.

    • When writing a kdimg, the OTA driver:

      1. Reads the current A/B slot versions.

      2. Selects the other slot as the target slot.

      3. Writes the rtt / rtapp content from the kdimg into the target slot’s partitions.

      4. Reads back the written content and computes SHA256, comparing it against the digest recorded in the kdimg.

      5. On success, updates the target slot’s version block to max(ver_a, ver_b) + 1.

This design ensures:

  • Upgrades always write to the non-running slot.

  • Slot version numbers increase monotonically after each OTA.

  • In the event of a boot failure, the old version can still be recovered by clearing the meta or other rollback strategies.


Component Relationships and Data Flow#

Application / test program
        │
        │  calls k230_ota_xxx API
        ▼
   libk230_ota  (userspace wrapper)
        │
        │  open("/dev/ota"), write(...)
        ▼
   RT-Thread /dev/ota device (ota.c)
        │
        │  ota_dev_write -> ota_kd_stream_write
        ▼
  - Parse kdimg header and partition table
  - Select target slot (A or B)
  - Write to rtt_x / rtapp_x partition
  - Read back and verify SHA256
  - Update ota_meta version block
        │
        ▼
   On next power-on:
   U-Boot SPL -> read TOC / ota_meta -> select slot to boot

From the application side, you only need to care about the k230_ota.c API and the kdimg file path. Partition selection, writing, verification, and version management are all handled automatically by the kernel OTA driver.


k230_ota.c API Reference#

Header: src/rtsmart/libs/rtsmart_hal/components/k230_ota/k230_ota.h Implementation: src/rtsmart/libs/rtsmart_hal/components/k230_ota/k230_ota.c

Type Definition#

typedef struct k230_ota_ctx k230_ota_t;

k230_ota_t is an opaque context structure. The application does not need to access its internals — operate it entirely through the provided functions.

k230_ota_create#

k230_ota_t* k230_ota_create(void);
  • Purpose: Creates an OTA session. Internally:

    • malloc-allocates a struct k230_ota_ctx

    • Opens /dev/ota in write mode

    • Resets the file offset to 0

  • Returns: Valid pointer on success; NULL on failure (an error message is printed to the console).

  • Usage: One k230_ota_create / k230_ota_destroy pair per upgrade. Only one OTA session at a time is recommended (the driver uses a global g_kdctx).

k230_ota_update#

int k230_ota_update(k230_ota_t* ctx, const void* buf, size_t size);
  • Purpose: Writes a chunk of kdimg data to the current OTA session. Internally calls write(ctx->fd, ...) in a loop until all size bytes are written. The file offset is maintained by the /dev/ota kernel driver; sequential writes are required.

  • Parameters:

    • ctx: session pointer returned by k230_ota_create

    • buf: data buffer

    • size: buffer size in bytes

  • Returns: 0 on success; < 0 on failure.

  • Usage: May be called multiple times with any chunk size (e.g. 64 KB per call). Do not use lseek to change the /dev/ota offset; the driver only accepts sequential writes starting at offset 0.

k230_ota_destroy#

void k230_ota_destroy(k230_ota_t* ctx);
  • Purpose: Closes the OTA session and releases resources. Closes ctx->fd if valid, prints the total bytes written, then frees ctx.

  • Usage: Always call this when done, regardless of success or failure, to avoid file descriptor leaks.

k230_ota_write_file#

int k230_ota_write_file(const char* image_path, size_t chunk_size);
  • Purpose: A one-call OTA utility that:

    1. Opens the specified kdimg file

    2. Creates an OTA session

    3. Reads the file in chunk_size loops, calling k230_ota_update each time

    4. Closes the file and OTA session when done

  • Parameters:

    • image_path: path to the kdimg file

    • chunk_size: read block size (recommended: 64 * 1024)

  • Returns: 0 on success; < 0 on failure.

  • Use case: Suitable for simple CLI tools or test programs. For finer-grained progress control or timeout management, use k230_ota_create / k230_ota_update / k230_ota_destroy directly.


Test Program Example#

Test program: src/rtsmart/libs/testcases/rtsmart_hal/test_ota.c

Core logic:

#define OTA_DEFAULT_IMAGE "/data/ota_test.kdimg"
#define READ_CHUNK_SIZE (64 * 1024)

int main(int argc, char* argv[])
{
    const char* image_path = OTA_DEFAULT_IMAGE;
    ...

    if (argc >= 2 && argv && argv[1])
        image_path = argv[1];

    printf("[ota_test] Starting OTA with img=%s\n", image_path);

    fd_img = open(image_path, O_RDONLY, 0);
    ...

    ota_ctx = k230_ota_create();
    ...

    buf = malloc(READ_CHUNK_SIZE);
    ...

    while ((rd = read(fd_img, buf, READ_CHUNK_SIZE)) > 0) {
        if (k230_ota_update(ota_ctx, buf, rd) < 0) {
            ...
        }
    }

    ...
    k230_ota_destroy(ota_ctx);
    ...
}

Typical usage steps:

  1. Build a new kdimg on the PC (containing new rtt/rtapp) using the SDK/build system.

  2. Copy the kdimg to the board (e.g. /data/ota_test.kdimg).

  3. Run on the board:

    test_ota /data/your_image.kdimg
    
  4. Wait for the program to print OTA update successful!, then reboot the device and check whether it boots from the new slot.


OTA Flow Details#

A complete OTA process (performed by the /dev/ota driver) involves the following steps:

  1. Application calls open("/dev/ota", O_WRONLY), triggering:

    • ota_dev_init(): calls ota_storage_init() to open the underlying block device (EMMC/SD).

    • ota_kdctx_reset(): clears the internal state machine g_kdctx.

  2. Application makes multiple write("/dev/ota", buf, len) calls; the driver internally:

    • Accumulates the first 512 bytes into hdr_buf, parses the kdimg header (verifies KDIMG_HDR_MAGIC and header CRC, records the number of partition table entries, etc.).

    • Accumulates the partition table into tbl_buf, parses it (verifies each kd_img_part’s part_magic; finds part_name=="rtt" and part_name=="rtapp"; looks up rtt_a/rtt_b/rtapp_a/rtapp_b in the TOC).

    • Loads and checks OTA version information: reads A/B slot version blocks from ota_meta; computes ver_a/ver_b; selects the newer slot as active_slot; uses the other as target_slot; sets the target version to max(ver_a, ver_b) + 1.

    • Streams data from the kdimg content area into the target partition according to each part’s content_offset and content_size.

  3. After all content for rtt and rtapp in the target slot has been written:

    • Reads back the target partition and computes SHA256, comparing it against the digest in the kdimg.

    • On success, calls ota_meta_write_slot(target_slot, target_version) to update the slot’s version block.

  4. On next power-on:

    • U-Boot SPL reads A/B slot metadata from ota_meta.

    • Boots from the slot with the higher version number, seamlessly switching to the new firmware.


Notes and FAQ#

  • Only valid kdimg files are accepted — the OTA driver verifies the kdimg header and partition table CRC, and performs SHA256 read-back verification on rtt / rtapp. If any check fails, ota_meta is not updated and no slot switch occurs.

  • Writes must start at offset 0 and be sequential — the driver maintains file_pos internally. If an out-of-order write is detected (pos != file_pos), it marks g_kdctx.invalid=RT_TRUE and all subsequent writes fail.

  • The initial ota_meta content is zero — on a freshly flashed SD card the ota_meta partition is empty (magic ≠ OTA_META_MAGIC), so U-Boot treats both slots as invalid and defaults to Slot A. After the first OTA the target slot’s version block is written with version=0x1; subsequent OTAs increment the version.

  • Behavior when slot versions differ by more than 1 — the driver prints a warning but still sets the new version to max(ver_a, ver_b) + 1.

  • Only EMMC/SD card is supportedSYSCTL_BOOT_NANDFLASH / SYSCTL_BOOT_NORFLASH are not yet implemented in ota_storage_init() and return -RT_ENOSYS.

  • Specifying the rt_app path — run make menuconfig, go to Fast Boot Configuration, and modify Fast boot file path. For available variables, refer to the exported paths in tools/mkenv.mk.


Integration Recommendations#

When integrating OTA into your own application or service:

  1. Firmware build and distribution

    • Generate the kdimg file in your CI/build system (includes rtt/rtapp partitions with SHA256; output at output/k230_canmv_xxxx_defconfig/xxx_ota.kdimg).

    • Host it on a web server, USB drive, or SD card for the device to download.

  2. Device-side download

    • Implement your own HTTP/FTP/MQTT download logic; save the new kdimg to a local path on the device (e.g. /data/update.kdimg) or stream it directly into a user-space buffer.

  3. Call the OTA API

    • Simple case: call k230_ota_write_file("/data/update.kdimg", 64 * 1024).

    • Advanced case: use k230_ota_create / k230_ota_update / k230_ota_destroy directly for progress reporting, resumable transfers, etc.

  4. Reboot and verify — after OTA completes, reboot the board. Check the SPL’s slot selection in the serial log to confirm the new slot booted. Consider implementing an application-level check of ver_a/ver_b to support health-check + automatic rollback strategies.

Comments list
Comments
Log in