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