esp_ieee802154/
lib.rs

1//! Low-level [IEEE 802.15.4] driver for the ESP32-C6 and ESP32-H2.
2//!
3//! Implements the PHY/MAC layers of the IEEE 802.15.4 protocol stack, and
4//! supports sending and receiving of raw frames.
5//!
6//! This library is intended to be used to implement support for higher-level
7//! communication protocols, for example [esp-openthread].
8//!
9//! Note that this crate currently requires you to enable the `unstable` feature
10//! on `esp-hal`.
11//!
12//! [IEEE 802.15.4]: https://en.wikipedia.org/wiki/IEEE_802.15.4
13//! [esp-openthread]: https://github.com/esp-rs/esp-openthread
14//!
15//! ## Feature Flags
16#![doc = document_features::document_features!()]
17#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
18#![no_std]
19
20use core::{cell::RefCell, marker::PhantomData};
21
22use byte::{BytesExt, TryRead};
23use critical_section::Mutex;
24use esp_config::*;
25use esp_hal::peripherals::{IEEE802154, RADIO_CLK};
26use heapless::Vec;
27use ieee802154::mac::{self, FooterMode, FrameSerDesContext};
28
29use self::{
30    frame::FRAME_SIZE,
31    pib::{CONFIG_IEEE802154_CCA_THRESHOLD, IEEE802154_FRAME_EXT_ADDR_SIZE},
32    raw::*,
33};
34pub use self::{
35    frame::{Frame, ReceivedFrame},
36    pib::{CcaMode, PendingMode},
37    raw::RawReceived,
38};
39
40mod fmt;
41mod frame;
42mod hal;
43mod pib;
44mod raw;
45
46/// IEEE 802.15.4 errors
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum Error {
49    /// The requested data is bigger than available range, and/or the offset is
50    /// invalid
51    Incomplete,
52    /// The requested data content is invalid
53    BadInput,
54}
55
56impl From<byte::Error> for Error {
57    fn from(err: byte::Error) -> Self {
58        match err {
59            byte::Error::Incomplete | byte::Error::BadOffset(_) => Error::Incomplete,
60            byte::Error::BadInput { .. } => Error::BadInput,
61        }
62    }
63}
64
65struct QueueConfig {
66    rx_queue_size: usize,
67}
68
69pub(crate) const CONFIG: QueueConfig = QueueConfig {
70    rx_queue_size: esp_config_int!(usize, "ESP_IEEE802154_CONFIG_RX_QUEUE_SIZE"),
71};
72
73/// IEEE 802.15.4 driver configuration
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct Config {
76    pub auto_ack_tx: bool,
77    pub auto_ack_rx: bool,
78    pub enhance_ack_tx: bool,
79    pub promiscuous: bool,
80    pub coordinator: bool,
81    pub rx_when_idle: bool,
82    pub txpower: i8,
83    pub channel: u8,
84    pub cca_threshold: i8,
85    pub cca_mode: CcaMode,
86    pub pan_id: Option<u16>,
87    pub short_addr: Option<u16>,
88    pub ext_addr: Option<u64>,
89}
90
91impl Default for Config {
92    fn default() -> Self {
93        Self {
94            auto_ack_tx: Default::default(),
95            auto_ack_rx: Default::default(),
96            enhance_ack_tx: Default::default(),
97            promiscuous: Default::default(),
98            coordinator: Default::default(),
99            rx_when_idle: Default::default(),
100            txpower: 10,
101            channel: 15,
102            cca_threshold: CONFIG_IEEE802154_CCA_THRESHOLD,
103            cca_mode: CcaMode::Ed,
104            pan_id: None,
105            short_addr: None,
106            ext_addr: None,
107        }
108    }
109}
110
111/// IEEE 802.15.4 driver
112#[derive(Debug)]
113pub struct Ieee802154<'a> {
114    _align: u32,
115    transmit_buffer: [u8; FRAME_SIZE],
116    _phantom1: PhantomData<&'a ()>,
117}
118
119impl<'a> Ieee802154<'a> {
120    /// Construct a new driver, enabling the IEEE 802.15.4 radio in the process
121    pub fn new(_radio: IEEE802154, mut radio_clocks: RADIO_CLK) -> Self {
122        esp_ieee802154_enable(&mut radio_clocks);
123
124        Self {
125            _align: 0,
126            transmit_buffer: [0u8; FRAME_SIZE],
127            _phantom1: PhantomData,
128        }
129    }
130
131    /// Set the configuration for the driver
132    pub fn set_config(&mut self, cfg: Config) {
133        set_auto_ack_tx(cfg.auto_ack_tx);
134        set_auto_ack_rx(cfg.auto_ack_rx);
135        set_enhance_ack_tx(cfg.enhance_ack_tx);
136        set_promiscuous(cfg.promiscuous);
137        set_coordinator(cfg.coordinator);
138        set_rx_when_idle(cfg.rx_when_idle);
139        set_tx_power(cfg.txpower);
140        set_channel(cfg.channel);
141        set_cca_theshold(cfg.cca_threshold);
142        set_cca_mode(cfg.cca_mode);
143
144        if let Some(pan_id) = cfg.pan_id {
145            set_panid(0, pan_id);
146        }
147
148        if let Some(short_addr) = cfg.short_addr {
149            set_short_address(0, short_addr);
150        }
151
152        if let Some(ext_addr) = cfg.ext_addr {
153            let mut address = [0u8; IEEE802154_FRAME_EXT_ADDR_SIZE];
154            address.copy_from_slice(&ext_addr.to_be_bytes()); // LE or BE?
155
156            set_extended_address(0, address);
157        }
158    }
159
160    /// Start receiving frames
161    pub fn start_receive(&mut self) {
162        ieee802154_receive();
163    }
164
165    /// Return the raw data of a received frame
166    pub fn raw_received(&mut self) -> Option<RawReceived> {
167        ieee802154_poll()
168    }
169
170    /// Get a received frame, if available
171    pub fn received(&mut self) -> Option<Result<ReceivedFrame, Error>> {
172        if let Some(raw) = ieee802154_poll() {
173            let maybe_decoded = if raw.data[0] as usize > raw.data.len() {
174                // try to decode up to data.len()
175                mac::Frame::try_read(&raw.data[1..][..raw.data.len()], FooterMode::Explicit)
176            } else {
177                mac::Frame::try_read(&raw.data[1..][..raw.data[0] as usize], FooterMode::Explicit)
178            };
179
180            let result = match maybe_decoded {
181                Ok((decoded, _)) => {
182                    // crc is not written to rx buffer
183                    let rssi = if (raw.data[0] as usize > raw.data.len()) || (raw.data[0] == 0) {
184                        raw.data[raw.data.len() - 1] as i8
185                    } else {
186                        raw.data[raw.data[0] as usize - 1] as i8
187                    };
188
189                    Ok(ReceivedFrame {
190                        frame: Frame {
191                            header: decoded.header,
192                            content: decoded.content,
193                            payload: Vec::from_slice(decoded.payload).unwrap(),
194                            footer: decoded.footer,
195                        },
196                        channel: raw.channel,
197                        rssi,
198                        lqi: rssi_to_lqi(rssi),
199                    })
200                }
201                Err(err) => Err(err.into()),
202            };
203
204            Some(result)
205        } else {
206            None
207        }
208    }
209
210    /// Transmit a frame
211    pub fn transmit(&mut self, frame: &Frame) -> Result<(), Error> {
212        let frm = mac::Frame {
213            header: frame.header,
214            content: frame.content,
215            payload: &frame.payload,
216            footer: frame.footer,
217        };
218
219        let mut offset = 1usize;
220        self.transmit_buffer
221            .write_with(
222                &mut offset,
223                frm,
224                &mut FrameSerDesContext::no_security(FooterMode::Explicit),
225            )
226            .unwrap();
227        self.transmit_buffer[0] = (offset - 1) as u8;
228
229        ieee802154_transmit(self.transmit_buffer.as_ptr(), false); // what about CCA?
230
231        Ok(())
232    }
233
234    /// Transmit a raw frame
235    pub fn transmit_raw(&mut self, frame: &[u8]) -> Result<(), Error> {
236        self.transmit_buffer[1..][..frame.len()].copy_from_slice(frame);
237        self.transmit_buffer[0] = frame.len() as u8;
238
239        ieee802154_transmit(self.transmit_buffer.as_ptr(), false); // what about CCA?
240
241        Ok(())
242    }
243
244    /// Set the transmit done callback function.
245    pub fn set_tx_done_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
246        critical_section::with(|cs| {
247            let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
248            let cb: &'static mut (dyn FnMut() + Send) = unsafe { core::mem::transmute(callback) };
249            tx_done_callback.replace(cb);
250        });
251    }
252
253    /// Clear the transmit done callback function.
254    pub fn clear_tx_done_callback(&mut self) {
255        critical_section::with(|cs| {
256            let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
257            tx_done_callback.take();
258        });
259    }
260
261    /// Set the receive available callback function.
262    pub fn set_rx_available_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
263        critical_section::with(|cs| {
264            let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
265            let cb: &'static mut (dyn FnMut() + Send) = unsafe { core::mem::transmute(callback) };
266            rx_available_callback.replace(cb);
267        });
268    }
269
270    /// Clear the receive available callback function.
271    pub fn clear_rx_available_callback(&mut self) {
272        critical_section::with(|cs| {
273            let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
274            rx_available_callback.take();
275        });
276    }
277
278    /// Set the transmit done callback function.
279    pub fn set_tx_done_callback_fn(&mut self, callback: fn()) {
280        critical_section::with(|cs| {
281            let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
282            tx_done_callback_fn.replace(callback);
283        });
284    }
285
286    /// Clear the transmit done callback function.
287    pub fn clear_tx_done_callback_fn(&mut self) {
288        critical_section::with(|cs| {
289            let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
290            tx_done_callback_fn.take();
291        });
292    }
293
294    /// Set the receive available callback function.
295    pub fn set_rx_available_callback_fn(&mut self, callback: fn()) {
296        critical_section::with(|cs| {
297            let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
298            rx_available_callback_fn.replace(callback);
299        });
300    }
301
302    /// Clear the receive available callback function.
303    pub fn clear_rx_available_callback_fn(&mut self) {
304        critical_section::with(|cs| {
305            let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
306            rx_available_callback_fn.take();
307        });
308    }
309}
310
311impl Drop for Ieee802154<'_> {
312    fn drop(&mut self) {
313        self.clear_tx_done_callback();
314        self.clear_tx_done_callback_fn();
315        self.clear_rx_available_callback();
316        self.clear_rx_available_callback_fn();
317    }
318}
319
320/// Convert from RSSI (Received Signal Strength Indicator) to LQI (Link Quality
321/// Indication)
322///
323/// RSSI is a measure of incoherent (raw) RF power in a channel. LQI is a
324/// cumulative value used in multi-hop networks to assess the cost of a link.
325pub fn rssi_to_lqi(rssi: i8) -> u8 {
326    if rssi < -80 {
327        0
328    } else if rssi > -30 {
329        0xff
330    } else {
331        let lqi_convert = ((rssi as u32).wrapping_add(80)) * 255;
332        (lqi_convert / 50) as u8
333    }
334}
335
336static TX_DONE_CALLBACK: Mutex<RefCell<Option<&'static mut (dyn FnMut() + Send)>>> =
337    Mutex::new(RefCell::new(None));
338
339static RX_AVAILABLE_CALLBACK: Mutex<RefCell<Option<&'static mut (dyn FnMut() + Send)>>> =
340    Mutex::new(RefCell::new(None));
341
342#[allow(clippy::type_complexity)]
343static TX_DONE_CALLBACK_FN: Mutex<RefCell<Option<fn()>>> = Mutex::new(RefCell::new(None));
344
345#[allow(clippy::type_complexity)]
346static RX_AVAILABLE_CALLBACK_FN: Mutex<RefCell<Option<fn()>>> = Mutex::new(RefCell::new(None));
347
348fn tx_done() {
349    trace!("tx_done callback");
350
351    critical_section::with(|cs| {
352        let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
353        let tx_done_callback = tx_done_callback.as_mut();
354
355        if let Some(tx_done_callback) = tx_done_callback {
356            tx_done_callback();
357        }
358
359        let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
360        let tx_done_callback_fn = tx_done_callback_fn.as_mut();
361
362        if let Some(tx_done_callback_fn) = tx_done_callback_fn {
363            tx_done_callback_fn();
364        }
365    });
366}
367
368fn rx_available() {
369    trace!("rx available callback");
370
371    critical_section::with(|cs| {
372        let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
373        let rx_available_callback = rx_available_callback.as_mut();
374
375        if let Some(rx_available_callback) = rx_available_callback {
376            rx_available_callback();
377        }
378
379        let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
380        let rx_available_callback_fn = rx_available_callback_fn.as_mut();
381
382        if let Some(rx_available_callback_fn) = rx_available_callback_fn {
383            rx_available_callback_fn();
384        }
385    });
386}