# 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.
    1. Reads Slot A and Slot B version blocks from `ota_meta`.
    1. Selects the slot with the newer version as the **active slot** and loads its `rtt_x` / `rtapp_x`.
    1. 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.
    1. Selects the **other** slot as the **target slot**.
    1. Writes the `rtt` / `rtapp` content from the kdimg into the target slot's partitions.
    1. Reads back the written content and computes SHA256, comparing it against the digest recorded in the kdimg.
    1. 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

```text
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

```c
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`

```c
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`

```c
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`

```c
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 `free`s `ctx`.
- **Usage**: Always call this when done, regardless of success or failure, to avoid file descriptor leaks.

### `k230_ota_write_file`

```c
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
  1. Creates an OTA session
  1. Reads the file in `chunk_size` loops, calling `k230_ota_update` each time
  1. 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:

```c
#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.
1. Copy the kdimg to the board (e.g. `/data/ota_test.kdimg`).
1. Run on the board:

   ```sh
   test_ota /data/your_image.kdimg
   ```

1. 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`.

1. 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`.

1. 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.

1. 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 supported** — `SYSCTL_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.

1. **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.

1. **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.

1. **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.
