Skip to content
File

Blob: archive/fft-benchmark/firmware/crates/esp32-radio/src/platform/mod.rs

rust88 lines
1//! Safe application-facing boundary around ESP-IDF. Native handles never escape.
2//! There are no Rust callbacks from C and no unsafe Send/Sync implementations.
3mod ffi;
4mod http;
5mod music;
6mod peer;
7 
8use crate::error::{Error, Result, check};
9pub(crate) use http::{Http, RESPONSE_LIMIT};
10pub(crate) use music::MusicBytes;
11pub(crate) use peer::{Peer, PeerState};
12use radio_core::protocol::Color;
13use std::{
14 cell::Cell,
15 ffi::CString,
16 marker::PhantomData,
17 sync::atomic::{AtomicBool, Ordering},
18};
19 
20static BOARD_TAKEN: AtomicBool = AtomicBool::new(false);
21 
22/// Unique LED owner; moved to the radio thread after initialization, never shared.
23#[derive(Debug)]
24pub(crate) struct Board {
25 _not_sync: PhantomData<Cell<()>>,
26}
27 
28impl Board {
29 pub(crate) fn init() -> Result<Self> {
30 if BOARD_TAKEN.swap(true, Ordering::AcqRel) {
31 return Err(Error::new("board already initialized"));
32 }
33 // SAFETY: the atomic guard permits exactly one initialization; C owns all
34 // driver state. It registers only C callbacks and borrows no Rust memory.
35 let code = unsafe { ffi::radio_board_init() };
36 check(code, "board initialization failed")?;
37 Ok(Self {
38 _not_sync: PhantomData,
39 })
40 }
41 pub(crate) fn set_led(&mut self, Color([r, g, b]): Color) -> Result<()> {
42 // SAFETY: a Board exists only after initialization; &mut self serializes
43 // LED calls and this sole owner is not Sync. Components fit the C ABI.
44 check(unsafe { ffi::radio_led(r, g, b) }, "LED write failed")
45 }
46}
47 
48pub(crate) fn now_us() -> u64 {
49 // SAFETY: thread-safe IDF monotonic timer; no borrowed state or pointers.
50 unsafe { ffi::radio_now_us() }
51}
52pub(crate) fn random() -> u32 {
53 // SAFETY: thread-safe IDF entropy API; Wi-Fi is active for the application.
54 unsafe { ffi::radio_random() }
55}
56pub(crate) fn heap_free() -> u32 {
57 // SAFETY: IDF serializes heap inspection internally; scalar result only.
58 unsafe { ffi::radio_heap_free() }
59}
60pub(crate) fn stack_free() -> u32 {
61 // SAFETY: query of the calling FreeRTOS task, with no external pointers.
62 unsafe { ffi::radio_stack_free() }
63}
64pub(crate) fn rssi() -> i32 {
65 // SAFETY: IDF Wi-Fi API synchronizes access and C initializes its output.
66 unsafe { ffi::radio_rssi() }
67}
68pub(crate) fn console_byte() -> i32 {
69 // SAFETY: called only by app_main's console loop; C stdio owns its buffer.
70 unsafe { ffi::radio_console_byte() }
71}
72pub(crate) fn log(message: &str) {
73 if let Ok(message) = CString::new(message) {
74 // SAFETY: valid terminated string remains live until synchronous logging
75 // returns. C uses "%s", so text cannot become a format string.
76 unsafe { ffi::radio_log(message.as_ptr()) };
77 }
78}
79pub(crate) fn recovery_attempt(reset: bool) -> u32 {
80 // SAFETY: called only by signaling (or fatal recovery which never returns).
81 // C retains the bounded scalar counter across software resets.
82 unsafe { ffi::radio_recovery_attempt(i32::from(reset)) }
83}
84pub(crate) fn restart() -> ! {
85 // SAFETY: IDF reset does not return or unwind and takes no borrowed memory.
86 unsafe { ffi::radio_restart() }
87}