Skip to content
File

Blob: firmware/crates/esp32-radio/src/platform/metrics.rs

rust75 lines
1use super::ffi;
2use crate::error::{Error, Result, check};
3use radio_core::metrics::Memory;
4use std::{ffi::c_void, marker::PhantomData, ptr::NonNull, rc::Rc};
5 
6pub(crate) struct Sample {
7 pub now_us: u64,
8 pub idle_us: [u64; 2],
9 pub temperature_mc: Option<i32>,
10 pub rssi: Option<i32>,
11 pub internal: Memory,
12 pub psram: Memory,
13}
14 
15/// Single-task sensor owner that keeps temperature conversion on core 1.
16pub(crate) struct Metrics {
17 handle: NonNull<c_void>,
18 _task: PhantomData<Rc<()>>,
19}
20 
21impl Metrics {
22 pub(crate) fn open() -> Result<Self> {
23 // SAFETY: scalar query of this task before any sensor floating-point work.
24 if unsafe { ffi::radio_current_core() } != 1 {
25 return Err(Error::new("metrics must run on core 1"));
26 }
27 // SAFETY: C owns the driver and context. It borrows no Rust memory and
28 // registers no callbacks; a missing temperature sensor remains optional.
29 let handle = NonNull::new(unsafe { ffi::radio_metrics_open() })
30 .ok_or(Error::new("metrics initialization failed"))?;
31 Ok(Self {
32 handle,
33 _task: PhantomData,
34 })
35 }
36 
37 pub(crate) fn sample(&mut self, detailed: bool) -> Result<Sample> {
38 let mut clocks = [0; 3];
39 let mut memory = [0; 6];
40 let mut sensors = [i32::MIN; 2];
41 // SAFETY: live same-task handle and initialized output arrays of exactly
42 // the ABI's 3/6/2 elements. C writes synchronously and retains no pointers.
43 let status = unsafe {
44 ffi::radio_metrics_sample(
45 self.handle.as_ptr(),
46 clocks.as_mut_ptr(),
47 memory.as_mut_ptr(),
48 sensors.as_mut_ptr(),
49 i32::from(detailed),
50 )
51 };
52 check(status, "metrics sampling failed")?;
53 let pool = |offset| Memory {
54 free: memory[offset],
55 minimum_free: memory[offset + 1],
56 largest_block: memory[offset + 2],
57 };
58 Ok(Sample {
59 now_us: clocks[0],
60 idle_us: [clocks[1], clocks[2]],
61 temperature_mc: (sensors[0] != i32::MIN).then_some(sensors[0]),
62 rssi: (sensors[1] != i32::MIN).then_some(sensors[1]),
63 internal: pool(0),
64 psram: pool(3),
65 })
66 }
67}
68 
69impl Drop for Metrics {
70 fn drop(&mut self) {
71 // SAFETY: sole task-affine owner; native sensor APIs have no callbacks.
72 unsafe { ffi::radio_metrics_free(self.handle.as_ptr()) };
73 }
74}