openaliro
Aliro reader: UWB/CCC core and ESP32-S3/C5/C6 port
Loading...
Searching...
No Matches
flight_recorder.h
Go to the documentation of this file.
1
7/*
8 * Copyright (c) 2026 asxeem
9 * SPDX-License-Identifier: ISC
10 *
11 * flight_recorder — record a live UWB walk-up on the device, replay it into the
12 * host build deterministically.
13 *
14 * WHAT IS RECORDED. The DS-TWR responder's *inputs*: the session config (incl.
15 * the ephemeral URSK the Pre-POLL decode needs) and, in dispatch order, every
16 * call into the shared ccc_shim_rx.c protocol engine — one record per call,
17 * carrying the DW3000 register snapshot that entry point will read (received
18 * frame bytes, Ipatov RX timestamp, TX timestamp, system time, STS quality).
19 *
20 * WHY INPUTS ONLY. The engine's *outputs* (the radio-action sequence: RX arms,
21 * Response TX, the latched range) are a pure function of those inputs — there is
22 * no RNG on the UWB responder path and every clock read the logic branches on is
23 * a captured DW3000 timestamp. So a host replay that feeds the recorded inputs
24 * through the real ccc_shim_rx.c re-derives the outputs; a firmware change that
25 * alters them shows up as a divergence at a specific event. A field failure
26 * becomes a deterministic regression test, and the recorded frames seed the
27 * fuzzers with genuine RF sessions.
28 *
29 * The trace is a flat little-endian record stream (fr_write_* / fr_read_next),
30 * allocation-free over a caller-owned buffer so the device recorder writes it
31 * into a static RAM ring with no heap on the walk-up path. tools/flight_recorder.py
32 * carries it off the device (hex-encoded `[FREC]` serial lines -> .frc file) and
33 * extracts the frames into a fuzz corpus. The replay engine lives host-side
34 * (tests/host/fr_replay.c) because it drives the DW3000 shim doubles.
35 *
36 * This header compiles unchanged in the firmware and the host builds; it pulls
37 * in no platform surface of its own.
38 */
39#ifndef WOZ_FLIGHT_RECORDER_H
40#define WOZ_FLIGHT_RECORDER_H
41
42#include <stdbool.h>
43#include <stddef.h>
44#include <stdint.h>
45
46#ifdef __cplusplus
47extern "C" {
48#endif
49
50/* Format identity. Bump FR_VERSION on any incompatible record-layout change;
51 * fr_read_next() rejects a mismatch so a stale trace fails loud, not silent. */
52#define FR_MAGIC 0x31435246u /* "FRC1" little-endian */
53#define FR_VERSION 1u
54
55/* Fixed caps sized to the CCC responder: a frame fits woz_host_rx.rxdata[128],
56 * the URSK is 32 B, a RangingConfiguration is ~17 B, a short SHA ~12 chars. */
57#define FR_FRAME_MAX 128u
58#define FR_URSK_LEN 32u
59#define FR_RC_MAX 64u
60#define FR_SHA_MAX 16u
61
62/* Which port produced the trace (META). Host replay always runs the generic
63 * (non-ESP, non-Zephyr) code path, so an ESP capture replays through it too —
64 * the frames and outcomes are identical; the ESP sync-arm is a timing-only
65 * optimisation that compiles out on the host. */
66enum fr_port {
67 FR_PORT_HOST = 0,
68 FR_PORT_NRF = 1,
69 FR_PORT_ESP32 = 2,
70};
71
72/* Record types. */
73enum fr_rec_type {
74 FR_REC_META = 1, /* format version + port + firmware SHA */
75 FR_REC_CONFIG = 2, /* the woz_uwb_aliro_cfg that opened the session */
76 FR_REC_EV = 3, /* one call into ccc_shim_rx.c + its register snapshot */
77 FR_REC_END = 4, /* event count + truncation flag (last record) */
78};
79
80/* The ccc_shim_rx.c entry point an event drove, in device dispatch order. */
81enum fr_ep {
82 FR_EP_TRY_PREPOLL = 1, /* ccc_shim_rx_try_prepoll(datalength) */
83 FR_EP_RX_REARM = 2, /* prepoll_rx_rearm(cb) — RxOk/RxTo/RxErr */
84 FR_EP_TX_DONE = 3, /* resp_tx_done(cb) — TXFRS */
85};
86
87/* ─ Plain-data record mirrors (no pointers; safe to memcpy/serialize) ─ */
88
93struct fr_meta {
94 uint16_t version;
95 uint16_t port;
96 char sha[FR_SHA_MAX + 1]; /* NUL-terminated */
97};
98
104struct fr_config {
105 uint32_t session_id;
106 uint8_t channel;
107 uint8_t sync_code_index;
108 uint16_t slot_duration_rstu;
109 uint32_t block_duration_ms;
110 uint8_t slot_per_round;
111 uint32_t sts_index0;
112 uint64_t uwb_time_us;
113 uint8_t ursk[FR_URSK_LEN];
114 uint16_t rc_len;
115 uint8_t rc[FR_RC_MAX];
116};
117
123struct fr_ev {
124 uint8_t ep; /* enum fr_ep */
125 uint32_t status; /* dwt_cb_data_t.status (0 for TRY_PREPOLL) */
126 uint16_t datalength; /* frame length the entry sees */
127 uint64_t rx_ts40; /* Ipatov RX timestamp (40-bit) */
128 uint64_t tx_ts40; /* TX timestamp (40-bit) */
129 uint32_t systime; /* dwt_readsystimestamphi32 */
130 uint8_t stsq_valid; /* 1 if stsq_ret/stsq_val captured */
131 int16_t stsq_val; /* dwt_readstsquality out-param */
132 int32_t stsq_ret; /* dwt_readstsquality return */
133 uint16_t frame_len; /* bytes captured in frame[] (<= datalength, FR_FRAME_MAX) */
134 uint8_t frame[FR_FRAME_MAX];
135};
136
141struct fr_end {
142 uint32_t n_events;
143 uint8_t truncated; /* 1 if the ring filled and later events were dropped */
144};
145
150struct fr_record {
151 uint8_t type; /* enum fr_rec_type */
152 union {
153 struct fr_meta meta;
154 struct fr_config config;
155 struct fr_ev ev;
156 struct fr_end end;
157 } u;
158};
159
160/* ─ Writer: append records to a fixed caller-owned buffer ─────────────────
161 * Every fr_write_* returns 0 on success or -1 if the record did not fit; on the
162 * first non-fit the writer latches `overflow` and all further writes are no-ops,
163 * so the buffer always holds a valid record prefix (never a half-written tail).
164 */
165typedef struct {
166 uint8_t *buf;
167 size_t cap;
168 size_t len;
169 bool overflow;
170} fr_writer_t;
171
172void fr_writer_init(fr_writer_t *w, uint8_t *buf, size_t cap);
173int fr_write_meta(fr_writer_t *w, uint16_t port, const char *sha);
174int fr_write_config(fr_writer_t *w, const struct fr_config *cfg);
175int fr_write_ev(fr_writer_t *w, const struct fr_ev *ev);
176int fr_write_end(fr_writer_t *w, uint32_t n_events, bool truncated);
177
178/* ─ Reader: iterate a trace buffer ────────────────────────────────────────
179 * fr_read_next fills *out and returns the record type (>0), 0 at clean end, or
180 * -1 on a malformed/short/oversized/version-mismatched stream. The first record
181 * of a well-formed trace is FR_REC_META and must carry FR_VERSION.
182 */
183typedef struct {
184 const uint8_t *buf;
185 size_t len;
186 size_t pos;
187 bool checked_magic;
188} fr_reader_t;
189
190void fr_reader_init(fr_reader_t *r, const uint8_t *buf, size_t len);
191int fr_read_next(fr_reader_t *r, struct fr_record *out);
192
193/* ─ On-device capture API (CONFIG_WOZ_FLIGHT_RECORDER) ─────────────────────
194 * No-op inlines when the feature is compiled out, so the capture call sites in
195 * ccc_shim_rx.c / woz_uwb_facade.c cost nothing in a hardened build. */
196#if defined(ESP_PLATFORM)
197#include "sdkconfig.h" /* CONFIG_WOZ_FLIGHT_RECORDER (Zephyr injects autoconf.h itself) */
198#endif
199
200struct woz_uwb_aliro_cfg; /* forward decl; the real def is in woz_uwb_facade.h */
201
202#if defined(CONFIG_WOZ_FLIGHT_RECORDER)
203
204void fr_set_enabled(bool on); /* arm/disarm (OFF at boot; `fr on` before a walk-up) */
205bool fr_enabled(void);
206void fr_capture_config(const struct woz_uwb_aliro_cfg *cfg); /* session-start hook */
207void fr_capture_ev(uint8_t ep, uint32_t status, uint16_t datalength); /* per-frame hook */
208size_t fr_finalize(const uint8_t **buf); /* append END once; return trace length */
209void fr_dump(void); /* hex-encode the ring as `[FREC]` lines */
210void fr_clear(void);
211void fr_set_dump_sink(void (*sink)(const char *line)); /* test hook; NULL => woz_printf */
212
213#else
214
219static inline void fr_set_enabled(bool on)
220{
221 (void)on;
222}
227static inline bool fr_enabled(void)
228{
229 return false;
230}
235static inline void fr_capture_config(const struct woz_uwb_aliro_cfg *cfg)
236{
237 (void)cfg;
238}
243static inline void fr_capture_ev(uint8_t ep, uint32_t status, uint16_t datalength)
244{
245 (void)ep;
246 (void)status;
247 (void)datalength;
248}
253static inline void fr_dump(void)
254{
255}
260static inline void fr_clear(void)
261{
262}
263
264#endif /* CONFIG_WOZ_FLIGHT_RECORDER */
265
266#ifdef __cplusplus
267}
268#endif
269
270#endif /* WOZ_FLIGHT_RECORDER_H */
static void fr_capture_ev(uint8_t ep, uint32_t status, uint16_t datalength)
Stub callback invoked when the flight recorder captures a UWB event (endpoint fire,...
Definition flight_recorder.h:243
static void fr_dump(void)
Stub callback that dumps the flight recorder ring buffer to the host interface.
Definition flight_recorder.h:253
int fr_write_ev(fr_writer_t *w, const struct fr_ev *ev)
Emit an EV record containing DW3000 register snapshot and received frame data; truncate frame to maxi...
Definition flight_recorder.c:197
static void fr_clear(void)
Stub callback that clears the flight recorder ring buffer.
Definition flight_recorder.h:260
static void fr_set_enabled(bool on)
Stub callback that enables or disables flight recording.
Definition flight_recorder.h:219
void fr_writer_init(fr_writer_t *w, uint8_t *buf, size_t cap)
Initialize a flight-recorder writer with an output buffer; write the magic prefix if capacity permits...
Definition flight_recorder.c:132
int fr_write_meta(fr_writer_t *w, uint16_t port, const char *sha)
Emit a META record containing flight-recorder version, host port, and optional commit SHA; return 0 o...
Definition flight_recorder.c:152
static bool fr_enabled(void)
Returns true if the flight recorder is enabled and capturing events; false otherwise.
Definition flight_recorder.h:227
void fr_reader_init(fr_reader_t *r, const uint8_t *buf, size_t len)
Initialize a flight-recorder reader to parse a binary buffer; do not validate the magic prefix until ...
Definition flight_recorder.c:237
static void fr_capture_config(const struct woz_uwb_aliro_cfg *cfg)
Stub callback invoked when the flight recorder captures the Aliro session configuration.
Definition flight_recorder.h:235
int fr_write_end(fr_writer_t *w, uint32_t n_events, bool truncated)
Emit an END record with event count and truncation flag; return 0 on success or -1 on buffer overflow...
Definition flight_recorder.c:221
int fr_write_config(fr_writer_t *w, const struct fr_config *cfg)
Emit a CONFIG record containing Aliro session parameters and UWB radio configuration; truncate URSK a...
Definition flight_recorder.c:173
int fr_read_next(fr_reader_t *r, struct fr_record *out)
Parse one flight-recorder record from the reader buffer; check magic prefix on first call,...
Definition flight_recorder.c:250
Captured Aliro session configuration snapshot: channel, timing parameters, STS index,...
Definition flight_recorder.h:104
Trailer record marking the end of a flight recorder session: the count of captured events and a trunc...
Definition flight_recorder.h:141
Single UWB event captured during a walk-up: endpoint identity, status register, frame length,...
Definition flight_recorder.h:123
Metadata header for a flight recorder stream: protocol version, port identifier (target),...
Definition flight_recorder.h:93
One record in a flight recorder stream: a discriminated union holding either a metadata header,...
Definition flight_recorder.h:150
Aliro UWB ranging parameters negotiated during M1-M4 handshake.
Definition woz_uwb_facade.h:35