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.hTest case:
src/rtsmart/libs/testcases/rtsmart_hal/test_ota.cKernel OTA driver:
src/rtsmart/rtsmart/kernel/bsp/maix3/components/ota/ota.cSD 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 |
|
- |
TOC table (up to 16 entries) |
Shared by U-Boot and OTA |
ota_meta |
|
- |
Slot A/B version blocks |
2×512 B, A first then B |
spl |
|
- |
U-Boot SPL |
Boot code |
uboot_env |
|
- |
U-Boot environment |
|
uboot |
|
- |
U-Boot main image |
|
rtt_a |
10 MB, 20 MB |
|
opensbi_rtt_system.bin |
Slot A RT-Smart system |
rtapp_a |
30 MB, 30 MB |
|
rtapp.elf.gz |
Slot A application |
rtt_b |
60 MB, 20 MB |
|
opensbi_rtt_system.bin |
Slot B RT-Smart system |
rtapp_b |
80 MB, 30 MB |
|
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_metapartition 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_metapartition is pre-allocated during image packaging. Its offset and size are defined ingenimage-sdcard.cfg:offset
0x000f0000, size0x800bytes.
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 value0x4f544156u(“OTAV”)version: monotonically increasing slot version numbercrc32: checksum covering the entire structure (computed with thecrc32field zeroed)
OTA image format (kdimg)
Uses a custom
kdimgcontainer:512-byte header (
KDIMG_HDR_MAGIC+ CRC32 + metadata)Followed by one or more
kd_img_partpartition 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_xphysical region.
Boot flow and slot selection
At boot, U-Boot SPL:
Parses partition information from the TOC.
Reads Slot A and Slot B version blocks from
ota_meta.Selects the slot with the newer version as the active slot and loads its
rtt_x/rtapp_x.If both slots are invalid (bad magic or CRC), defaults to Slot A.
When writing a kdimg, the OTA driver:
Reads the current A/B slot versions.
Selects the other slot as the target slot.
Writes the
rtt/rtappcontent from the kdimg into the target slot’s partitions.Reads back the written content and computes SHA256, comparing it against the digest recorded in the kdimg.
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 astruct k230_ota_ctxOpens
/dev/otain write modeResets the file offset to 0
Returns: Valid pointer on success;
NULLon failure (an error message is printed to the console).Usage: One
k230_ota_create/k230_ota_destroypair per upgrade. Only one OTA session at a time is recommended (the driver uses a globalg_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 allsizebytes are written. The file offset is maintained by the/dev/otakernel driver; sequential writes are required.Parameters:
ctx: session pointer returned byk230_ota_createbuf: data buffersize: buffer size in bytes
Returns:
0on success;< 0on failure.Usage: May be called multiple times with any chunk size (e.g. 64 KB per call). Do not use
lseekto change the/dev/otaoffset; 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->fdif valid, prints the total bytes written, thenfreesctx.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:
Opens the specified kdimg file
Creates an OTA session
Reads the file in
chunk_sizeloops, callingk230_ota_updateeach timeCloses the file and OTA session when done
Parameters:
image_path: path to the kdimg filechunk_size: read block size (recommended:64 * 1024)
Returns:
0on success;< 0on 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_destroydirectly.
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:
Build a new kdimg on the PC (containing new
rtt/rtapp) using the SDK/build system.Copy the kdimg to the board (e.g.
/data/ota_test.kdimg).Run on the board:
test_ota /data/your_image.kdimgWait 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:
Application calls
open("/dev/ota", O_WRONLY), triggering:ota_dev_init(): callsota_storage_init()to open the underlying block device (EMMC/SD).ota_kdctx_reset(): clears the internal state machineg_kdctx.
Application makes multiple
write("/dev/ota", buf, len)calls; the driver internally:Accumulates the first 512 bytes into
hdr_buf, parses the kdimg header (verifiesKDIMG_HDR_MAGICand header CRC, records the number of partition table entries, etc.).Accumulates the partition table into
tbl_buf, parses it (verifies eachkd_img_part’spart_magic; findspart_name=="rtt"andpart_name=="rtapp"; looks uprtt_a/rtt_b/rtapp_a/rtapp_bin the TOC).Loads and checks OTA version information: reads A/B slot version blocks from
ota_meta; computesver_a/ver_b; selects the newer slot asactive_slot; uses the other astarget_slot; sets the target version tomax(ver_a, ver_b) + 1.Streams data from the kdimg content area into the target partition according to each part’s
content_offsetandcontent_size.
After all content for
rttandrtappin 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.
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_metais not updated and no slot switch occurs.Writes must start at offset 0 and be sequential — the driver maintains
file_posinternally. If an out-of-order write is detected (pos != file_pos), it marksg_kdctx.invalid=RT_TRUEand all subsequent writes fail.The initial ota_meta content is zero — on a freshly flashed SD card the
ota_metapartition 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 withversion=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 supported —
SYSCTL_BOOT_NANDFLASH/SYSCTL_BOOT_NORFLASHare not yet implemented inota_storage_init()and return-RT_ENOSYS.Specifying the rt_app path — run
make menuconfig, go toFast Boot Configuration, and modifyFast boot file path. For available variables, refer to the exported paths intools/mkenv.mk.
Integration Recommendations#
When integrating OTA into your own application or service:
Firmware build and distribution
Generate the kdimg file in your CI/build system (includes
rtt/rtapppartitions with SHA256; output atoutput/k230_canmv_xxxx_defconfig/xxx_ota.kdimg).Host it on a web server, USB drive, or SD card for the device to download.
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.
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_destroydirectly for progress reporting, resumable transfers, etc.
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_bto support health-check + automatic rollback strategies.
