|
openaliro
Aliro reader: UWB/CCC core and ESP32-S3/C5/C6 port
|
— responder-RX CCC STS substitution: woz_uwb_arm_rx() programs the CCC STS on each RX-arm; target only. More...
#include <stddef.h>#include <stdint.h>#include <errno.h>#include "woz_port.h"#include "woz_bytes.h"#include <deca_device_api.h>#include "ccc_shim.h"#include "aliro_round_config.h"#include "ccc_kdf.h"#include "ccc_mac.h"#include "fira_session.h"#include "uwb_min.h"#include "uwb_seam.h"#include "woz_diag.h"#include "uwb_rxdiag.h"#include "flight_recorder.h"Macros | |
| #define | CCC_RX_LOG_ARMS 16 |
| Log the first N intercepted RX-arms — confirms slot advance + register load. | |
| #define | STS_KEY0_REG 0x2000CUL |
| STS_KEY/STS_IV registers (dw3000_deca_regs.h) — read back to prove the load lands. | |
| #define | CHAN_CTRL_REG 0x10014UL |
| CHAN_CTRL (dw3000_deca_regs.h): RX preamble code [12:8], SFD type [2:1]; read at the POLL arm to prove the active PHY. | |
| #define | SYS_CFG_REG 0x10UL |
| SYS_CFG [13:12] = CP_SPC (STS packet config / SP mode: 3 = SP3/ND). | |
| #define | STS_CFG0_REG 0x20000UL |
| STS_CFG0 [7:0] = CPS_LEN (STS length code: 7 = STS64 = 4096 DRBG bits). | |
| #define | CCC_RX_PIN_ROUND0_POLL 1 |
| ISOLATION PROBE (bench) — pin the RX STS index to the deterministic round-0 POLL (STS_Index0 + 1) instead of the local-time slot. | |
| #define | CCC_RX_FORCE_SP3 1 /* 1 for the seeded Δ sweep: engage the STS engine on the SP3 POLL. */ |
| ISOLATION PROBE (bench) — force SP3/ND at each RX arm so the STS engine is engaged when Apple's SP3 POLL lands. | |
| #define | CCC_RX_LOCK_SWEEP |
| EMPIRICAL STS-INDEX LOCK (bench) — track Apple's ranging clock on-air by searching the constant slot offset Δ between our time0 and Apple's UWB_Time0. | |
| #define | CCC_RX_SLOTS_PER_BLOCK 96u |
| RESIDUE-COMPLETE alignment-offset sweep (slot = current_slot() + Δ): the STS index is linear at one per slot, so sweep the constant offset Δ. | |
| #define | CCC_RX_DELTA_SEED 1299 |
| Sweep CENTER = Apple's UWB_Time0 / slot-duration; the offset is the constant slot gap between our time0 and Apple's UWB_Time0, searched not derived. | |
| #define | CCC_RX_DELTA_HALF 768u |
| ± window around the seed: covers offsets [531,2067] (two prior estimates \ ≈1250–1300). | |
| #define | CCC_RX_N_CAND |
| 1536 one-slot Δ candidates, alternating outward from the seed. | |
| #define | CCC_RX_DWELL 1u |
| catches held per Δ candidate (1 = fast full-range sweep; bump if it cycles past). | |
| #define | CCC_RX_PREPOLL_LOG 16u |
| BENCH: decode a live SP0 Pre-POLL (CCM*-decrypt with mUPSK1) to read Apple's exact POLL STS index, learn the block stride, and warm the next block's STS. | |
| #define | CCC_RX_PACK_SELFTEST 0 /* answered 2026-07-09: pack_iv needed the reverse (applied). */ |
| BENCH PROBE (CCC_RX_PACK_SELFTEST) — dump the STS register lanes a known V lands in, to pin the pack_iv byte order. | |
| #define | CCC_RX_DIAG_N 110u |
| Re-arm the SP0 receiver after each RX event; the woz_uwb_set_callbacks shim runs ccc_shim_rx_try_prepoll first, so here we keep the plain SP0 receive listening. | |
| #define | CCC_RX_SLEEP_HI32 45900000u |
| Sleep between wakes, hi32 (~4 ns) units: one 192 ms block MINUS ~5 slots, waking just ahead of the Pre-POLL so it is caught fresh each block. | |
| #define | CCC_RX_BLOCK_HI32 47920000u |
| One 192 ms ranging block, hi32 (~4 ns) units (measured on air, dsys ≈ 47.92 M). | |
| #define | CCC_RX_SLOT_HI32 499000u |
| One 2 ms slot, hi32 units (measured, dsys ≈ 499 000). | |
| #define | CCC_RX_WIN_TO 1950u |
| Anchored SP0 RX window length, dwt_setrxtimeout units (1.0256 µs): ~1 slot, so a miss times out before the POLL slot. | |
| #define | CCC_RX_SWEEP_N 5u |
| Sweep the armed offset ±2 slots (0..4 -> -2..+2), so an off-by-one gap does not read as "no Pre-POLL". | |
| #define | CCC_RX_POLL_LEAD 40000u |
| Open the SP3 POLL window this many hi32 units (~160 µs) BEFORE the POLL RMARKER (Pre-POLL. | |
| #define | CCC_RX_POLL_WIN_TO 1350u |
| SP3 POLL RX window (dwt_setrxtimeout units, 1.0256 µs): ~1.4 ms, delayed to the POLL slot, opening ~600 µs early. | |
| #define | CCC_RX_PREPOLL_CODE 9u |
| Sync (preamble) code to listen on for the SP0 Pre-POLL: 9, read from the phone's plaintext M4 (07 01 09 = SYNC_Code_Index=9). | |
Functions | |
| void | ccc_shim_rx_log_reset (void) |
| Reset all Per-POLL state: arm count, index tracking, STS warm cache, and optional lock-sweep diagnostic counters; called on entry to a new Pre-POLL listen. | |
| bool | ccc_shim_rx_awaiting_poll (void) |
| Returns true if the responder is awaiting the POLL frame after a successful Pre-POLL decode. | |
| bool | ccc_shim_rx_deadline_pending (void) |
| Return true if CCC UWB reception is awaiting either POLL or FINAL frame from the responder, false otherwise. | |
| bool | ccc_shim_rx_awaiting_final (void) |
| Return true if CCC UWB reception is awaiting a FINAL frame from the responder, false otherwise. | |
| void | ccc_shim_rx_notify_rx (uint32_t status) |
| Log one RX event for the optional lock-sweep diagnostic (CONFIG_CCC_RX_LOCK_SWEEP); tracks CPER (STS correlation fail flag) and dwells candidate indices until lock achieved or full cycle exhausted. | |
| static void | prepoll_decode (const uint8_t *frame, uint16_t datalength) |
| Decode a received Pre-POLL frame: verify MHR and message ID, decrypt SP0 payload, cache UAD-derived keys, extract Poll_STS_Index and stride, and pre-warm the next block's STS triplet (POLL, Response_0, Final) to eliminate KDF latency from the critical path. | |
| static uint64_t | ts5_to_u64 (const uint8_t t[5]) |
| Assemble a 5-byte DW3000 (40-bit) timestamp into a uint64 (DTU ticks). | |
| static void | final_data_decode (const uint8_t *frame, uint16_t datalength) |
| Decode a received Final_Data (SP0, msg_id=02): dUDSK-decrypt and parse the initiator's ranging timestamps; not time-critical. | |
| void | ccc_shim_rx_try_prepoll (uint16_t datalength) |
| Pre-POLL RX entry (from the RX-good shim): stash the frame and DEFER its ~2 ms decrypt+derive off the Pre-POLL->POLL critical path. | |
| static void | pack_key (dwt_sts_cp_key_t *out, const uint8_t dursk[CCC_DURSK_LEN]) |
Pack a 16-byte dURSK into the DW3000 STS-key image (whole-16 reverse). | |
| static void | pack_iv (dwt_sts_cp_iv_t *out, const uint8_t sts_v[CCC_STS_V_LEN]) |
| Pack a 16-byte STS-V into the DW3000 STS-IV image (whole-16 reverse then per-word LE, same as pack_key). | |
| int32_t | woz_uwb_arm_rx (int32_t mode) |
| Program the CCC STS for the current ranging slot, then arm RX. | |
| static int32_t | gated_rxenable (int32_t mode) |
| Gate-checked RX arm for every self-rearm site below; refuses once the listen-gate is closed. | |
| static int | arm_poll_sp3 (uint32_t prepoll_ip) |
| Flip to SP3/ND, load the pre-warmed CCC STS (g_warm_index), and arm a delayed RX to catch the POLL that follows the Pre-POLL. | |
| static void | revert_to_sp0_listen (void) |
| Revert SP3/ND -> SP0 and re-arm the permanent Pre-POLL listen (no timeout). | |
| static int | tx_response_sp3 (uint32_t poll_ip, uint32_t resp_idx) |
| Delayed-TX the responder's Response_0 (SP3-ND) one slot after the POLL, at STS index Poll_STS_Index + 1 (same dURSK, STS-V advances). | |
| static int | arm_final_sp3 (uint32_t poll_ip) |
| Arm the delayed SP3-ND RX for the phone's Final at STS index Poll_STS_Index+ALIRO_FINAL_SLOT_OFFSET, packing the g_armed_final_* STS (no KDF). | |
| static void | resp_tx_done (const dwt_cb_data_t *cb) |
| TX-done (TXFRS) callback: our Response_0 left the antenna, so arm the Final RX one slot later, then run the block's deferred Pre-POLL decode in the idle. | |
| static void | prepoll_rx_rearm (const dwt_cb_data_t *cb) |
| RX callback for Pre-POLL listen and POLL/Final results. | |
| int | ccc_prepoll_prewarm (uint8_t channel, uint8_t preamble_code) |
| Pre-apply the expected session PHY (radio configured, TRX off, RX not armed) so a following ccc_prepoll_listen() with the same params skips the dwt_configure long pole. | |
| int | ccc_prepoll_listen (uint8_t channel, uint8_t preamble_code) |
| BENCH: bring up a raw continuous SP0 receiver for the CCC Pre-POLL (target only). | |
| void | ccc_prepoll_stop (void) |
| Stop the Pre-POLL listener: close the self-rearm listen-gate, then force the radio off (target only). | |
Variables | |
| static uint32_t | g_rx_arms |
| Count of intercepted RX-arms; the first CCC_RX_LOG_ARMS are logged. | |
| static uint32_t | g_poll_sts_index |
| Apple's Poll_STS_Index from the most recent decoded Pre-POLL. | |
| static bool | g_have_poll_index |
| True once a Pre-POLL has handed us a fresh g_poll_sts_index. | |
| static bool | g_await_poll |
| True while the SP3 RX is armed for the POLL (next RX event = its result). | |
| static uint32_t | g_armed_index |
| Poll_STS_Index already armed once — a re-detected Pre-POLL (same index) must not re-arm late in the block. | |
| static uint32_t | g_prepoll_ip |
Pre-POLL Ipatov timestamp of the in-flight POLL arm (for the gap d= log). | |
| static uint32_t | g_poll_stride |
| Poll_STS_Index delta between consecutive Pre-POLLs (the block stride, ~96); primes the next block's dURSK. | |
| static bool | g_warm_valid |
| STS pre-derived in the idle for the predicted next POLL index, so the SP3 arm packs it directly with no KDF on the 2 ms path. | |
| static uint32_t | g_pp_logged |
| Verbose Pre-POLL trace budget; file-scope so a session start can reset it (below), else the cap latches after the first ranging attempt and later sessions log nothing. | |
| static uint8_t | g_warm_resp_dursk [CCC_DURSK_LEN] |
| Response_0 STS (Poll_STS_Index+1): same-round dURSK plus index+1 STS-V, pre-derived in the idle so the TX path runs no KDF. | |
| static uint8_t | g_warm_final_dursk [CCC_DURSK_LEN] |
| Final STS (Poll_STS_Index+2): the phone's Final RFRAME one slot after our Response, pre-derived and snapshotted at the POLL arm so no KDF runs. | |
| static bool | g_await_final |
| True while the SP3 RX is armed for the Final (next RX event = its result). | |
| static uint32_t | g_poll_ip_for_final |
| POLL RMARKER of the in-flight round, so the TXDONE can anchor the Final window. | |
| static uint64_t | g_t_poll_rx |
| The three responder DS-TWR timestamps, full 40-bit DTU: POLL RX (t2), Response TX (t3), Final RX (t6). | |
| static int32_t | g_final_sts_verdict = -1 |
| Responder-side DS-TWR intervals (reply1 = t3-t2, round2 = t6-t3) SNAPSHOTTED at the Final RX capture, when t2/t3/t6 are all from the same round. | |
| static bool | g_uad_cached |
| Per-session Pre-POLL decrypt constants (mUPSK1 + UAD-derived src/dest/keysource): depend only on URSK + STS_Index0, derived once and reused. | |
| static uint8_t | g_pp_stash [64] |
| Pre-POLL frame stashed at RX for a DEFERRED decode: the ~2 ms decrypt+derive must not run between the Pre-POLL and the POLL. | |
| uint32_t | g_ccc_dbg_decode |
| try_prepoll() decode duration (hi32 ~4 ns units), reported on the ARM-FAIL line to attribute the pre-arm latency. | |
| static volatile bool | g_listen_gate |
| Listen-gate: true only while the Pre-POLL listener is up. | |
| static uint8_t | g_resp_payload [4] |
| Dummy Response_0 body — NOT radiated (SP3/ND sends STS only), but the TX sequence writes a frame body before dwt_starttx. | |
— responder-RX CCC STS substitution: woz_uwb_arm_rx() programs the CCC STS on each RX-arm; target only.
| #define CCC_RX_DELTA_SEED 1299 |
Sweep CENTER = Apple's UWB_Time0 / slot-duration; the offset is the constant slot gap between our time0 and Apple's UWB_Time0, searched not derived.
center: 0x0027a4d4 µs / 2 ms (heuristic; retunable).
| #define CCC_RX_DIAG_N 110u |
Re-arm the SP0 receiver after each RX event; the woz_uwb_set_callbacks shim runs ccc_shim_rx_try_prepoll first, so here we keep the plain SP0 receive listening.
per-catch lines to emit (~1 catch/block now — spans a full sweep).
| #define CCC_RX_DWELL 1u |
catches held per Δ candidate (1 = fast full-range sweep; bump if it cycles past).
\
| #define CCC_RX_PACK_SELFTEST 0 /* answered 2026-07-09: pack_iv needed the reverse (applied). */ |
BENCH PROBE (CCC_RX_PACK_SELFTEST) — dump the STS register lanes a known V lands in, to pin the pack_iv byte order.
Not shippable.
| #define CCC_RX_POLL_LEAD 40000u |
Open the SP3 POLL window this many hi32 units (~160 µs) BEFORE the POLL RMARKER (Pre-POLL.
| #define CCC_RX_SLOTS_PER_BLOCK 96u |
RESIDUE-COMPLETE alignment-offset sweep (slot = current_slot() + Δ): the STS index is linear at one per slot, so sweep the constant offset Δ.
192 ms / 2 ms — phase-log modulus only, NOT the index stride.
|
static |
Arm the delayed SP3-ND RX for the phone's Final at STS index Poll_STS_Index+ALIRO_FINAL_SLOT_OFFSET, packing the g_armed_final_* STS (no KDF).
EXPERIMENT-2RESP: the 1:1 baseline uses POLL + 2 slots / index+2; a 2-responder round puts the Final at POLL + 3 (responder 1's silent slot sits at POLL + 2).
| bool ccc_shim_rx_awaiting_final | ( | void | ) |
Return true if CCC UWB reception is awaiting a FINAL frame from the responder, false otherwise.
True while an SP3 Final RX is armed.
| bool ccc_shim_rx_awaiting_poll | ( | void | ) |
Returns true if the responder is awaiting the POLL frame after a successful Pre-POLL decode.
True while an SP3 POLL RX is armed (the next RX-good event is its result).
| bool ccc_shim_rx_deadline_pending | ( | void | ) |
Return true if CCC UWB reception is awaiting either POLL or FINAL frame from the responder, false otherwise.
True while the responder still owes the current ranging block a radio event: an armed POLL RX (a few slots out) or an armed Final RX (~2 ms out).
| void ccc_shim_rx_log_reset | ( | void | ) |
Reset all Per-POLL state: arm count, index tracking, STS warm cache, and optional lock-sweep diagnostic counters; called on entry to a new Pre-POLL listen.
Reset the responder-RX wrap's first-N-arm log budget (target only).
| void ccc_shim_rx_notify_rx | ( | uint32_t | status | ) |
Log one RX event for the optional lock-sweep diagnostic (CONFIG_CCC_RX_LOCK_SWEEP); tracks CPER (STS correlation fail flag) and dwells candidate indices until lock achieved or full cycle exhausted.
Feed each RX event's status word to the empirical STS-index tracker (target only).
| status | DW3000 status register value. |
| void ccc_shim_rx_try_prepoll | ( | uint16_t | datalength | ) |
Pre-POLL RX entry (from the RX-good shim): stash the frame and DEFER its ~2 ms decrypt+derive off the Pre-POLL->POLL critical path.
Decode a received SP0 Pre-POLL frame to read Apple's exact POLL STS index (target only).
The decode only feeds the NEXT block's warm (the SP3 arm keys on the pre-warmed prediction, not this block's fresh index), so it has the ~190 ms idle to run in. Running it inline — on the single DW3000 workqueue, ahead of the queued POLL RX-OK callback — pushed the Response TX arm past its slot (dx-now < 0 => HPDWARN). So read the bytes now (cheap SPI), then:
|
static |
Decode a received Final_Data (SP0, msg_id=02): dUDSK-decrypt and parse the initiator's ranging timestamps; not time-critical.
| frame | Pointer to the received frame buffer. |
| datalength | Length of the frame in bytes. |
|
static |
Gate-checked RX arm for every self-rearm site below; refuses once the listen-gate is closed.
Arms the radio directly: each caller has just programmed the STS for the window it is opening, so re-running woz_uwb_arm_rx would overwrite it with the current slot's.
|
static |
Pack a 16-byte STS-V into the DW3000 STS-IV image (whole-16 reverse then per-word LE, same as pack_key).
| out | Pointer to the DW3000 STS-IV register image. |
| sts_v | 16-byte STS vector to pack. |
|
static |
Decode a received Pre-POLL frame: verify MHR and message ID, decrypt SP0 payload, cache UAD-derived keys, extract Poll_STS_Index and stride, and pre-warm the next block's STS triplet (POLL, Response_0, Final) to eliminate KDF latency from the critical path.
| frame | Pointer to the received frame buffer. |
| datalength | Length of the frame in bytes. |
|
static |
RX callback for Pre-POLL listen and POLL/Final results.
Re-arms SP0 by default, or arms SP3/ND for POLL if a warmed index is ready, or fires the delayed-TX Response_0 and Final RX arm on valid POLL CPER; logs free-running timing and optionally defers Pre-POLL decode to warm the next block.
|
extern |
try_prepoll() decode duration (hi32 ~4 ns units), reported on the ARM-FAIL line to attribute the pre-arm latency.
try_prepoll() decode duration (hi32 ~4 ns units), reported on the ARM-FAIL line to attribute the pre-arm latency.
Decode-cost probe: last try_prepoll() duration, hi32 (~4 ns) units.
|
static |
Responder-side DS-TWR intervals (reply1 = t3-t2, round2 = t6-t3) SNAPSHOTTED at the Final RX capture, when t2/t3/t6 are all from the same round.
final_data_decode consumes these instead of recomputing from the live globals: the Final_Data lands only after the NEXT round's POLL/ Response have overwritten t2/t3, so recomputing there mixes this round's t6 with the next round's t3 and corrupts round2 (observed as km-scale distances). g_final_round_valid gates the compute (consume-once): a Final_Data with no fresh Final capture is dropped, not turned into garbage.
Cache of the STS key (dURSK) currently loaded in the STS_KEY registers, so the per-arm sts_key_load() skips the ~258 us of redundant key writes when the per-cycle-constant dURSK is unchanged. Defined here (ahead of ccc_shim_rx_log_reset) so a session start can invalidate it.
|
static |
Listen-gate: true only while the Pre-POLL listener is up.
ccc_prepoll_stop() closes it so no callback rearm can re-enable RX after a session stop; ccc_prepoll_listen() reopens it before its arm.