Skip to content

S08.05: Store-and-forward on FreeRTOS-Plus-FAT #270

@DavidCozens

Description

@DavidCozens

Parent epic: #10 (E08: RTOS Examples)

Scope

Add persistent store-and-forward to the FreeRTOS demo by introducing a FatFs–backed SolidSyslogFile, so the existing SolidSyslogFileBlockDevice / SolidSyslogBlockStore ecosystem runs unchanged on the QEMU target with messages surviving an oracle outage and a power cycle.

Why FatFs (ChaN), not FreeRTOS-Plus-FAT: FatFs is the de facto embedded filesystem — used by Zephyr, NuttX, MicroPython, CircuitPython, STM32 HAL, ESP-IDF, the Arduino SD ecosystem. FreeRTOS-Plus-FAT is an AWS Lab project, untagged, AWS-tied. For a library aimed at the broader embedded market, FatFs is what integrators already have. FatFs's disk I/O port (diskio.c) is a minimal 4-function interface — disk_initialize / disk_read / disk_write / disk_ioctl — which makes the QEMU storage substrate simpler than Plus-FAT's media-driver vtable.

New public surface:

  • Platform/FatFs/Interface/SolidSyslogFatFsFile.h — FatFs-backed implementation of the SolidSyslogFile extension point. Mirrors SolidSyslogPosixFile.h / SolidSyslogWindowsFile.h.
  • Platform/FatFs/Source/SolidSyslogFatFsFile.c — implementation against the FatFs API (f_open / f_close / f_read / f_write / f_lseek / f_size / f_truncate / f_stat / f_unlink).

Why a new Platform/FatFs/ platform pack, not under Platform/FreeRtos/: FatFs is filesystem-agnostic to the RTOS — it runs on bare-metal, FreeRTOS, Zephyr, NuttX, anywhere. A new Platform/FatFs/ is the right home, peer to Platform/Posix/, Platform/Windows/, Platform/FreeRtos/, Platform/OpenSsl/. Reusable by any integrator who picks FatFs.

Prerequisite — out-of-channel container bump

Story #270 depends on FatFs sources being present in the cpputest-freertos and cpputest-freertos-cross images at $FATFS_PATH (proposed /opt/fatfs). That change lives in the CppUTestFreertosDocker repo and must land first as a separate chore: bump container image to <sha> PR per docs/containers.md. Pin FatFs to R0.15 (current stable) by tarball SHA-256.

QEMU storage substrate

mps2-an385 has no built-in mass-storage device. Proposal: write a small semihosting-backed FatFs diskio.c (host-file-as-disk-image) and mount FatFs on top of it. Each disk_read / disk_write routes through SYS_READ / SYS_WRITE to a host-resident image file, giving continuous byte-for-byte persistence across QEMU restart and surviving an unannounced kill — the prerequisite for power_cycle_replay.feature. The semihosting diskio.c lives in BDD-target glue (Bdd/Targets/FreeRtos/SemihostingDiskIo.c), not in Platform/FatFs/ — production integrators supply their own flash / SD / eMMC diskio.c.

FatFs also needs OS glue (ffsystem.c) when FF_FS_REENTRANT=1. On the BDD target this wraps FreeRTOS mutexes; on host TDD we set FF_FS_REENTRANT=0 and the OS glue is a no-op. ffconf.h is per-consumer per the header-configured platform pattern (see CLAUDE.md and the project_header_configured_platforms memory).

Final storage shape locked at the slice-5 review gate.

Slicing (8 slices, one PR, one commit per slice)

# Slice Commit Review gate
1 Plumb FatFs into the host TDD build + smoke test chore: S08.05 slice 1 plumb FatFs host TDD build CMake wiring, host-side diskio.c (RAM-disk in Tests/Support/FatFsFakes/)
2 SolidSyslogFatFsFile — Create/Destroy + Open/Close/IsOpen feat: S08.05 slice 2 SolidSyslogFatFsFile Create + Open/Close header surface, storage size, f_open mode flags
3 Read / Write / SeekTo / Size / Truncate feat: S08.05 slice 3 FatFsFile Read/Write/Seek/Size/Truncate byte-exact return semantics on partial reads / writes
4 Exists / Delete feat: S08.05 slice 4 FatFsFile Exists/Delete path semantics, f_stat / f_unlink error returns
5 QEMU semihosting diskio.c + FreeRTOS ffsystem.c + BDD target wires BlockStore feat: S08.05 slice 5 QEMU diskio + BlockStore wiring for FreeRTOS BDD target semihosting diskio.c design + FreeRTOS-mutex ffsystem.c + set translations for --store / --max-blocks / --max-block-size / --discard-policy / --message / --halt-exit
6 store_and_forward.feature green on QEMU feat: S08.05 slice 6 store_and_forward feature green on FreeRTOS QEMU sender-outage recovery walks via QEMU
7 power_cycle_replay.feature green on QEMU feat: S08.05 slice 7 power_cycle_replay feature green on FreeRTOS QEMU replay correctness across QEMU kill / restart
8 store_capacity.feature — all 4 scenarios green on QEMU feat: S08.05 slice 8 store_capacity feature green on FreeRTOS QEMU discard-oldest, discard-newest, halt-exit, halt-prevents-service

Slices 1-4 land via build-freertos-host-tdd CI. Slices 5-8 land via bdd-freertos-qemu. The PR opens after slice 1 and gets force-updated as slices land; CI runs the full matrix on every push.

The discard-policy trap (slice 8)

max-block-size 520 in store_capacity.feature is sized for Linux / Windows (MAX=2048 → clamped up to ~2057). On FreeRTOS with MAX=512 the clamp lands at ~521, essentially unchanged. Combined with the message-body sizing SOLIDSYSLOG_MAX_MESSAGE_SIZE // 5 - 50 in syslog_steps.py (≈52 chars on FreeRTOS vs ≈359 on Linux), per-block record packing shifts dramatically. First action in slice 8 is a diagnostic print of actual per-record bytes and per-block packing on FreeRTOS — assertions follow the measurement, not a guess.

Acceptance criteria — BDD scenarios pass against QEMU

  • store_and_forward.feature — messages delivered after sender outage.
  • power_cycle_replay.feature — stored messages replayed after restart.
  • store_capacity.feature — overflow / discard-oldest / discard-newest / halt policies.

Docs and header surface

  • Platform/FatFs/Interface/SolidSyslogFatFsFile.h added.
  • Platform/FatFs/ platform pack (peer to Posix / Windows / FreeRtos / OpenSsl) added.
  • CLAUDE.md "Public header audiences" — new row.
  • DEVLOG entry noting FatFs ChaN-license compatibility with PolyForm Noncommercial 1.0.0 (permissive component flowing into more-restrictive parent — only obligation is preserving ChaN's notice in ff.c, which is untouched).

Cross-cuts

  • E05 E05: Store and Forward #7 Store and Forward — this story adds a FatFs-backed SolidSyslogFile to the existing file / block-device / store stack rather than reinventing it.
  • Container bump in CppUTestFreertosDocker must land first (separate PR, separate repo).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    storyStory issue

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions