Skip to content
File

Blob: archive/fft-benchmark/firmware/docs/ffi.md

Markdown61 lines

Frozen FFT harness: native boundary

This contract describes the archived benchmark harness. The maintained application has its own native boundary contract.

The private ABI is declared in platform/include/radio_bridge.h and imported only by esp32-radio/src/platform/ffi.rs. The boundary carries fixed-width integers, C strings, opaque handles and pointer/length pairs. It carries no Rust structs, trait objects, owned strings, enums, references or allocator-owned containers. size_t corresponds to Rust usize; both are 32-bit on this target.

All callers must provide valid pointer/length pairs, initialized inputs and writable output storage of the declared capacity. Each Rust wrapper constructs these pairs from live slices/strings and checks returned lengths. C performs the copy within capacity. The C header is private, not a general-purpose public FFI library that accepts arbitrary hostile pointers.

Ownership and calls

Resource Owner and lifetime Synchronization
Board drivers One initialized board; C retains driver handles LED capability moves to the radio task; it is not Sync
Peer handle/configuration/labels/answer C allocation retained until reset, including failed initialization once vendor code has been entered Rust peer is neither Send nor Sync; all methods run on its creating task
Vendor command payload Valid only during the C callback; copied immediately FreeRTOS queue of 16 commands, each with up to 512 payload bytes; no Rust callback
Vendor offer payload Copied before callback returns C mutex protects the stored offer and its consumer
Peer state and dropped count C callback context retained until reset C atomics
Music bytes C allocates and transfers initialized PSRAM to a Rust owner Immutable borrow tied to owner lifetime; C frees after borrows end
Outgoing audio/data Rust borrows bytes only during the adapter call; C copies into its own bounded storage Sole radio task; native packet/cache copies complete before return
HTTPS client/configuration/response C allocates; Rust owns the opaque handle and calls C cleanup in Drop Client is neither Send nor Sync; blocking operations stay on signaling
HTTPS POST body Borrowed for one blocking call Native POST field is cleared before return; callbacks use only C-owned response storage
Recovery count in RTC memory C retains across software resets C critical section; Rust admits one terminal recovery caller

The HTTPS response is capped at 24,576 bytes, setup requests at 20,000 bytes, SDP at 16,000 bytes, outbound data at 1,024 bytes, and individual Opus packets at 1,275 bytes. Redirects are disabled. Time synchronization precedes verified TLS, using the ESP-IDF certificate bundle. Credentials are compiled into the private C configuration and never formatted into Rust errors.

Vendor lifetime requirements

The adapter keeps configuration, callback context, labels and remote SDP alive until reset. Audio/data sends copy into C-owned buffers, and the pinned native implementation copies packets into its transport storage before returning. Review that assumption when upgrading the vendor binary; do not infer a new lifetime guarantee merely from an unchanged function signature.

Failure and teardown

Release builds use panic = "abort". Rust panics cannot unwind through C. Expected input and transport failures use results/status codes; malformed commands are rejected and counted without restarting the device.

Peer configuration and callback memory are C-owned for the process lifetime; the Rust wrapper does not close or free them in Drop. Terminal recovery resets the complete device. See archive transport notes for the native teardown issue and the conditions for changing this policy.

HTTP cleanup is different: its calls are blocking and their callbacks have returned before the Rust method returns. C cleanup closes the client before freeing its configuration/response context.