Skip to main content

esp_radio/ieee802154/
mod.rs

1//! # Low-level [IEEE 802.15.4] driver
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 module is intended to be used to implement support for higher-level
7//! communication protocols, for example [openthread].
8//!
9//! Note that this module requires the `unstable` feature on both `esp-radio`
10//! and `esp-hal`.
11//!
12//! NOTE: Coexistence with Wi-Fi or Bluetooth is currently not possible. If you do it anyway,
13//! things will break.
14//!
15//! [IEEE 802.15.4]: https://en.wikipedia.org/wiki/IEEE_802.15.4
16//! [openthread]: https://github.com/esp-rs/openthread
17
18#![allow(missing_docs)]
19
20use byte::{BytesExt, TryRead};
21use docsplay::Display;
22use esp_hal::peripherals::IEEE802154;
23use esp_phy::{PhyClockGuard, PhyInitGuard};
24use esp_sync::NonReentrantMutex;
25use ieee802154::mac::{self, FooterMode, FrameSerDesContext};
26
27use self::{
28    frame::FRAME_SIZE,
29    pib::{CONFIG_IEEE802154_CCA_THRESHOLD, IEEE802154_FRAME_EXT_ADDR_SIZE},
30    raw::*,
31};
32pub use self::{
33    frame::{Frame, ReceivedFrame},
34    pib::{CcaMode, PendingMode},
35    raw::RawReceived,
36};
37mod frame;
38mod hal;
39mod pib;
40mod raw;
41
42/// IEEE 802.15.4 errors
43#[derive(Display, Debug, Clone, Copy, PartialEq, Eq)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45#[instability::unstable]
46pub enum Error {
47    /// The requested data is bigger than available range, and/or the offset is
48    /// invalid.
49    Incomplete,
50
51    /// The requested data content is invalid.
52    BadInput,
53}
54
55impl core::error::Error for Error {}
56
57impl From<byte::Error> for Error {
58    fn from(err: byte::Error) -> Self {
59        match err {
60            byte::Error::Incomplete | byte::Error::BadOffset(_) => Error::Incomplete,
61            byte::Error::BadInput { .. } => Error::BadInput,
62        }
63    }
64}
65
66/// IEEE 802.15.4 driver configuration
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68#[cfg_attr(feature = "defmt", derive(defmt::Format))]
69#[instability::unstable]
70pub struct Config {
71    pub auto_ack_tx: bool,
72    pub auto_ack_rx: bool,
73    pub enhance_ack_tx: bool,
74    pub promiscuous: bool,
75    pub coordinator: bool,
76    pub rx_when_idle: bool,
77    pub txpower: i8,
78    pub channel: u8,
79    pub cca_threshold: i8,
80    pub cca_mode: CcaMode,
81    pub pan_id: Option<u16>,
82    pub short_addr: Option<u16>,
83    pub ext_addr: Option<u64>,
84    pub rx_queue_size: usize,
85}
86
87impl Default for Config {
88    fn default() -> Self {
89        Self {
90            auto_ack_tx: Default::default(),
91            auto_ack_rx: Default::default(),
92            enhance_ack_tx: Default::default(),
93            promiscuous: Default::default(),
94            coordinator: Default::default(),
95            rx_when_idle: Default::default(),
96            txpower: 20,
97            channel: 15,
98            cca_threshold: CONFIG_IEEE802154_CCA_THRESHOLD,
99            cca_mode: CcaMode::Ed,
100            pan_id: None,
101            short_addr: None,
102            ext_addr: None,
103            rx_queue_size: 10,
104        }
105    }
106}
107
108/// IEEE 802.15.4 driver
109#[derive(Debug)]
110#[cfg_attr(feature = "defmt", derive(defmt::Format))]
111#[instability::unstable]
112pub struct Ieee802154<'a> {
113    _align: u32,
114    transmit_buffer: [u8; FRAME_SIZE],
115    _phy_clock_guard: PhyClockGuard<'a>,
116    _phy_init_guard: PhyInitGuard<'a>,
117}
118
119impl<'a> Ieee802154<'a> {
120    /// Construct a new driver, enabling the IEEE 802.15.4 radio in the process
121    ///
122    /// NOTE: Coexistence with Wi-Fi or Bluetooth is currently not possible. If you do it anyway,
123    /// things will break.
124    #[instability::unstable]
125    pub fn new(radio: IEEE802154<'a>) -> Self {
126        let (_phy_clock_guard, _phy_init_guard) = esp_ieee802154_enable(radio);
127        Self {
128            _align: 0,
129            transmit_buffer: [0u8; FRAME_SIZE],
130            _phy_clock_guard,
131            _phy_init_guard,
132        }
133    }
134
135    /// Set the configuration for the driver
136    #[instability::unstable]
137    pub fn set_config(&mut self, cfg: Config) {
138        set_auto_ack_tx(cfg.auto_ack_tx);
139        set_auto_ack_rx(cfg.auto_ack_rx);
140        set_enhance_ack_tx(cfg.enhance_ack_tx);
141        set_promiscuous(cfg.promiscuous);
142        set_coordinator(cfg.coordinator);
143        set_rx_when_idle(cfg.rx_when_idle);
144        set_tx_power(cfg.txpower);
145        set_channel(cfg.channel);
146        set_cca_theshold(cfg.cca_threshold);
147        set_cca_mode(cfg.cca_mode);
148
149        if let Some(pan_id) = cfg.pan_id {
150            set_panid(0, pan_id);
151        }
152
153        if let Some(short_addr) = cfg.short_addr {
154            set_short_address(0, short_addr);
155        }
156
157        if let Some(ext_addr) = cfg.ext_addr {
158            let mut address = [0u8; IEEE802154_FRAME_EXT_ADDR_SIZE];
159            address.copy_from_slice(&ext_addr.to_le_bytes());
160
161            set_extended_address(0, address);
162        }
163
164        raw::set_queue_size(cfg.rx_queue_size);
165    }
166
167    /// Start receiving frames
168    #[instability::unstable]
169    pub fn start_receive(&mut self) {
170        ieee802154_receive();
171    }
172
173    /// Return the raw data of a received frame
174    #[instability::unstable]
175    pub fn raw_received(&mut self) -> Option<RawReceived> {
176        raw::ensure_receive_enabled();
177        ieee802154_poll()
178    }
179
180    /// Get the ACK frame received in response to the last transmission.
181    ///
182    /// When a transmitted frame requires acknowledgment, the peer sends back
183    /// an ACK frame. This method returns that ACK frame data, which includes
184    /// the Frame Pending bit and other information needed by upper layers
185    /// like OpenThread.
186    ///
187    /// Returns `None` if no ACK was received (frame didn't require ACK,
188    /// ACK timed out, or no transmission has occurred).
189    ///
190    /// The ACK frame is cleared at the start of each new transmission.
191    #[instability::unstable]
192    pub fn get_ack_frame(&self) -> Option<RawReceived> {
193        raw::get_ack_frame()
194    }
195
196    /// Get a received frame, if available
197    #[instability::unstable]
198    pub fn received(&mut self) -> Option<Result<ReceivedFrame, Error>> {
199        raw::ensure_receive_enabled();
200        if let Some(raw) = ieee802154_poll() {
201            let maybe_decoded = if raw.data[0] as usize > raw.data.len() {
202                // try to decode up to data.len()
203                mac::Frame::try_read(&raw.data[1..][..raw.data.len()], FooterMode::Explicit)
204            } else {
205                mac::Frame::try_read(&raw.data[1..][..raw.data[0] as usize], FooterMode::Explicit)
206            };
207
208            let result = match maybe_decoded {
209                Ok((decoded, _)) => {
210                    // crc is not written to rx buffer
211                    let rssi = if (raw.data[0] as usize > raw.data.len()) || (raw.data[0] == 0) {
212                        raw.data[raw.data.len() - 1] as i8
213                    } else {
214                        raw.data[raw.data[0] as usize - 1] as i8
215                    };
216
217                    Ok(ReceivedFrame {
218                        frame: Frame {
219                            header: decoded.header,
220                            content: decoded.content,
221                            payload: decoded.payload.to_vec(),
222                            footer: decoded.footer,
223                        },
224                        channel: raw.channel,
225                        rssi,
226                        lqi: rssi_to_lqi(rssi),
227                    })
228                }
229                Err(err) => Err(err.into()),
230            };
231
232            Some(result)
233        } else {
234            None
235        }
236    }
237
238    /// Transmit a frame
239    ///
240    /// If `cca` is true, a Clear Channel Assessment is performed before
241    /// transmitting. The transmission is aborted if the channel is busy.
242    #[instability::unstable]
243    pub fn transmit(&mut self, frame: &Frame, cca: bool) -> Result<(), Error> {
244        let frm = mac::Frame {
245            header: frame.header,
246            content: frame.content,
247            payload: &frame.payload,
248            footer: frame.footer,
249        };
250
251        let mut offset = 1usize;
252        self.transmit_buffer
253            .write_with(
254                &mut offset,
255                frm,
256                &mut FrameSerDesContext::no_security(FooterMode::Explicit),
257            )
258            .unwrap();
259        self.transmit_buffer[0] = (offset - 1) as u8;
260
261        ieee802154_transmit(self.transmit_buffer.as_ptr(), cca);
262
263        Ok(())
264    }
265
266    /// Transmit a raw frame
267    ///
268    /// If `cca` is true, a Clear Channel Assessment is performed before
269    /// transmitting. The transmission is aborted if the channel is busy.
270    #[instability::unstable]
271    pub fn transmit_raw(&mut self, frame: &[u8], cca: bool) -> Result<(), Error> {
272        self.transmit_buffer[1..][..frame.len()].copy_from_slice(frame);
273        self.transmit_buffer[0] = frame.len() as u8;
274
275        ieee802154_transmit(self.transmit_buffer.as_ptr(), cca);
276
277        Ok(())
278    }
279
280    /// Set the transmit done callback function.
281    #[instability::unstable]
282    pub fn set_tx_done_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
283        CALLBACKS.with(|cbs| {
284            let cb: &'static mut (dyn FnMut() + Send) = unsafe { core::mem::transmute(callback) };
285            cbs.tx_done = Some(cb);
286        });
287    }
288
289    /// Clear the transmit done callback function.
290    #[instability::unstable]
291    pub fn clear_tx_done_callback(&mut self) {
292        CALLBACKS.with(|cbs| cbs.tx_done = None);
293    }
294
295    /// Set the receive available callback function.
296    #[instability::unstable]
297    pub fn set_rx_available_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
298        CALLBACKS.with(|cbs| {
299            let cb: &'static mut (dyn FnMut() + Send) = unsafe { core::mem::transmute(callback) };
300            cbs.rx_available = Some(cb);
301        });
302    }
303
304    /// Clear the receive available callback function.
305    #[instability::unstable]
306    pub fn clear_rx_available_callback(&mut self) {
307        CALLBACKS.with(|cbs| cbs.rx_available = None);
308    }
309
310    /// Set the transmit done callback function.
311    #[instability::unstable]
312    pub fn set_tx_done_callback_fn(&mut self, callback: fn()) {
313        CALLBACKS.with(|cbs| cbs.tx_done_fn = Some(callback));
314    }
315
316    /// Clear the transmit done callback function.
317    #[instability::unstable]
318    pub fn clear_tx_done_callback_fn(&mut self) {
319        CALLBACKS.with(|cbs| cbs.tx_done_fn = None);
320    }
321
322    /// Set the receive available callback function.
323    #[instability::unstable]
324    pub fn set_rx_available_callback_fn(&mut self, callback: fn()) {
325        CALLBACKS.with(|cbs| cbs.rx_available_fn = Some(callback));
326    }
327
328    /// Clear the receive available callback function.
329    #[instability::unstable]
330    pub fn clear_rx_available_callback_fn(&mut self) {
331        CALLBACKS.with(|cbs| cbs.rx_available_fn = None);
332    }
333
334    /// Set the transmit failed callback function.
335    #[instability::unstable]
336    pub fn set_tx_failed_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
337        CALLBACKS.with(|cbs| {
338            let cb: &'static mut (dyn FnMut() + Send) = unsafe { core::mem::transmute(callback) };
339            cbs.tx_failed = Some(cb);
340        });
341    }
342
343    /// Clear the transmit failed callback function.
344    #[instability::unstable]
345    pub fn clear_tx_failed_callback(&mut self) {
346        CALLBACKS.with(|cbs| cbs.tx_failed = None);
347    }
348
349    /// Set the transmit failed callback function pointer.
350    #[instability::unstable]
351    pub fn set_tx_failed_callback_fn(&mut self, callback: fn()) {
352        CALLBACKS.with(|cbs| cbs.tx_failed_fn = Some(callback));
353    }
354
355    /// Clear the transmit failed callback function.
356    #[instability::unstable]
357    pub fn clear_tx_failed_callback_fn(&mut self) {
358        CALLBACKS.with(|cbs| cbs.tx_failed_fn = None);
359    }
360}
361
362impl Drop for Ieee802154<'_> {
363    fn drop(&mut self) {
364        self.clear_tx_done_callback();
365        self.clear_tx_done_callback_fn();
366        self.clear_rx_available_callback();
367        self.clear_rx_available_callback_fn();
368        self.clear_tx_failed_callback();
369        self.clear_tx_failed_callback_fn();
370    }
371}
372
373/// Convert from RSSI (Received Signal Strength Indicator) to LQI (Link Quality
374/// Indication)
375///
376/// RSSI is a measure of incoherent (raw) RF power in a channel. LQI is a
377/// cumulative value used in multi-hop networks to assess the cost of a link.
378#[instability::unstable]
379pub fn rssi_to_lqi(rssi: i8) -> u8 {
380    if rssi < -80 {
381        0
382    } else if rssi > -30 {
383        0xff
384    } else {
385        let lqi_convert = ((rssi as u32).wrapping_add(80)) * 255;
386        (lqi_convert / 50) as u8
387    }
388}
389
390struct Callbacks {
391    tx_done: Option<&'static mut (dyn FnMut() + Send)>,
392    rx_available: Option<&'static mut (dyn FnMut() + Send)>,
393    tx_failed: Option<&'static mut (dyn FnMut() + Send)>,
394    // TODO: remove these - Box<dyn FnMut> should be good enough
395    tx_done_fn: Option<fn()>,
396    rx_available_fn: Option<fn()>,
397    tx_failed_fn: Option<fn()>,
398}
399
400impl Callbacks {
401    fn call_tx_done(&mut self) {
402        if let Some(cb) = self.tx_done.as_mut() {
403            cb();
404        }
405        if let Some(cb) = self.tx_done_fn.as_mut() {
406            cb();
407        }
408    }
409
410    fn call_rx_available(&mut self) {
411        if let Some(cb) = self.rx_available.as_mut() {
412            cb();
413        }
414        if let Some(cb) = self.rx_available_fn.as_mut() {
415            cb();
416        }
417    }
418
419    fn call_tx_failed(&mut self) {
420        if let Some(cb) = self.tx_failed.as_mut() {
421            cb();
422        }
423        if let Some(cb) = self.tx_failed_fn.as_mut() {
424            cb();
425        }
426    }
427}
428
429static CALLBACKS: NonReentrantMutex<Callbacks> = NonReentrantMutex::new(Callbacks {
430    tx_done: None,
431    rx_available: None,
432    tx_failed: None,
433    tx_done_fn: None,
434    rx_available_fn: None,
435    tx_failed_fn: None,
436});
437
438fn tx_done() {
439    trace!("tx_done callback");
440
441    CALLBACKS.with(|cbs| cbs.call_tx_done());
442}
443
444fn tx_failed() {
445    trace!("tx_failed callback");
446
447    CALLBACKS.with(|cbs| cbs.call_tx_failed());
448}
449
450fn rx_available() {
451    trace!("rx available callback");
452
453    CALLBACKS.with(|cbs| cbs.call_rx_available());
454}