1#![doc = document_features::document_features!()]
20#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
21#![no_std]
22
23use core::cell::RefCell;
24
25use byte::{BytesExt, TryRead};
26use critical_section::Mutex;
27use esp_config::*;
28use esp_hal::{clock::PhyClockGuard, peripherals::IEEE802154};
29use heapless::Vec;
30use ieee802154::mac::{self, FooterMode, FrameSerDesContext};
31
32use self::{
33 frame::FRAME_SIZE,
34 pib::{CONFIG_IEEE802154_CCA_THRESHOLD, IEEE802154_FRAME_EXT_ADDR_SIZE},
35 raw::*,
36};
37pub use self::{
38 frame::{Frame, ReceivedFrame},
39 pib::{CcaMode, PendingMode},
40 raw::RawReceived,
41};
42
43mod fmt;
44mod frame;
45mod hal;
46mod pib;
47mod raw;
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum Error {
52 Incomplete,
55 BadInput,
57}
58
59impl From<byte::Error> for Error {
60 fn from(err: byte::Error) -> Self {
61 match err {
62 byte::Error::Incomplete | byte::Error::BadOffset(_) => Error::Incomplete,
63 byte::Error::BadInput { .. } => Error::BadInput,
64 }
65 }
66}
67
68struct QueueConfig {
69 rx_queue_size: usize,
70}
71
72pub(crate) const CONFIG: QueueConfig = QueueConfig {
73 rx_queue_size: esp_config_int!(usize, "ESP_IEEE802154_CONFIG_RX_QUEUE_SIZE"),
74};
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub struct Config {
79 pub auto_ack_tx: bool,
80 pub auto_ack_rx: bool,
81 pub enhance_ack_tx: bool,
82 pub promiscuous: bool,
83 pub coordinator: bool,
84 pub rx_when_idle: bool,
85 pub txpower: i8,
86 pub channel: u8,
87 pub cca_threshold: i8,
88 pub cca_mode: CcaMode,
89 pub pan_id: Option<u16>,
90 pub short_addr: Option<u16>,
91 pub ext_addr: Option<u64>,
92}
93
94impl Default for Config {
95 fn default() -> Self {
96 Self {
97 auto_ack_tx: Default::default(),
98 auto_ack_rx: Default::default(),
99 enhance_ack_tx: Default::default(),
100 promiscuous: Default::default(),
101 coordinator: Default::default(),
102 rx_when_idle: Default::default(),
103 txpower: 10,
104 channel: 15,
105 cca_threshold: CONFIG_IEEE802154_CCA_THRESHOLD,
106 cca_mode: CcaMode::Ed,
107 pan_id: None,
108 short_addr: None,
109 ext_addr: None,
110 }
111 }
112}
113
114#[derive(Debug)]
116pub struct Ieee802154<'a> {
117 _align: u32,
118 transmit_buffer: [u8; FRAME_SIZE],
119 _phy_clock_guard: PhyClockGuard<'a>,
120}
121
122impl<'a> Ieee802154<'a> {
123 pub fn new(radio: IEEE802154<'a>) -> Self {
128 Self {
129 _align: 0,
130 transmit_buffer: [0u8; FRAME_SIZE],
131 _phy_clock_guard: esp_ieee802154_enable(radio),
132 }
133 }
134
135 pub fn set_config(&mut self, cfg: Config) {
137 set_auto_ack_tx(cfg.auto_ack_tx);
138 set_auto_ack_rx(cfg.auto_ack_rx);
139 set_enhance_ack_tx(cfg.enhance_ack_tx);
140 set_promiscuous(cfg.promiscuous);
141 set_coordinator(cfg.coordinator);
142 set_rx_when_idle(cfg.rx_when_idle);
143 set_tx_power(cfg.txpower);
144 set_channel(cfg.channel);
145 set_cca_theshold(cfg.cca_threshold);
146 set_cca_mode(cfg.cca_mode);
147
148 if let Some(pan_id) = cfg.pan_id {
149 set_panid(0, pan_id);
150 }
151
152 if let Some(short_addr) = cfg.short_addr {
153 set_short_address(0, short_addr);
154 }
155
156 if let Some(ext_addr) = cfg.ext_addr {
157 let mut address = [0u8; IEEE802154_FRAME_EXT_ADDR_SIZE];
158 address.copy_from_slice(&ext_addr.to_be_bytes()); set_extended_address(0, address);
161 }
162 }
163
164 pub fn start_receive(&mut self) {
166 ieee802154_receive();
167 }
168
169 pub fn raw_received(&mut self) -> Option<RawReceived> {
171 ieee802154_poll()
172 }
173
174 pub fn received(&mut self) -> Option<Result<ReceivedFrame, Error>> {
176 if let Some(raw) = ieee802154_poll() {
177 let maybe_decoded = if raw.data[0] as usize > raw.data.len() {
178 mac::Frame::try_read(&raw.data[1..][..raw.data.len()], FooterMode::Explicit)
180 } else {
181 mac::Frame::try_read(&raw.data[1..][..raw.data[0] as usize], FooterMode::Explicit)
182 };
183
184 let result = match maybe_decoded {
185 Ok((decoded, _)) => {
186 let rssi = if (raw.data[0] as usize > raw.data.len()) || (raw.data[0] == 0) {
188 raw.data[raw.data.len() - 1] as i8
189 } else {
190 raw.data[raw.data[0] as usize - 1] as i8
191 };
192
193 Ok(ReceivedFrame {
194 frame: Frame {
195 header: decoded.header,
196 content: decoded.content,
197 payload: Vec::from_slice(decoded.payload).unwrap(),
198 footer: decoded.footer,
199 },
200 channel: raw.channel,
201 rssi,
202 lqi: rssi_to_lqi(rssi),
203 })
204 }
205 Err(err) => Err(err.into()),
206 };
207
208 Some(result)
209 } else {
210 None
211 }
212 }
213
214 pub fn transmit(&mut self, frame: &Frame) -> Result<(), Error> {
216 let frm = mac::Frame {
217 header: frame.header,
218 content: frame.content,
219 payload: &frame.payload,
220 footer: frame.footer,
221 };
222
223 let mut offset = 1usize;
224 self.transmit_buffer
225 .write_with(
226 &mut offset,
227 frm,
228 &mut FrameSerDesContext::no_security(FooterMode::Explicit),
229 )
230 .unwrap();
231 self.transmit_buffer[0] = (offset - 1) as u8;
232
233 ieee802154_transmit(self.transmit_buffer.as_ptr(), false); Ok(())
236 }
237
238 pub fn transmit_raw(&mut self, frame: &[u8]) -> Result<(), Error> {
240 self.transmit_buffer[1..][..frame.len()].copy_from_slice(frame);
241 self.transmit_buffer[0] = frame.len() as u8;
242
243 ieee802154_transmit(self.transmit_buffer.as_ptr(), false); Ok(())
246 }
247
248 pub fn set_tx_done_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
250 critical_section::with(|cs| {
251 let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
252 let cb: &'static mut (dyn FnMut() + Send) = unsafe { core::mem::transmute(callback) };
253 tx_done_callback.replace(cb);
254 });
255 }
256
257 pub fn clear_tx_done_callback(&mut self) {
259 critical_section::with(|cs| {
260 let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
261 tx_done_callback.take();
262 });
263 }
264
265 pub fn set_rx_available_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
267 critical_section::with(|cs| {
268 let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
269 let cb: &'static mut (dyn FnMut() + Send) = unsafe { core::mem::transmute(callback) };
270 rx_available_callback.replace(cb);
271 });
272 }
273
274 pub fn clear_rx_available_callback(&mut self) {
276 critical_section::with(|cs| {
277 let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
278 rx_available_callback.take();
279 });
280 }
281
282 pub fn set_tx_done_callback_fn(&mut self, callback: fn()) {
284 critical_section::with(|cs| {
285 let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
286 tx_done_callback_fn.replace(callback);
287 });
288 }
289
290 pub fn clear_tx_done_callback_fn(&mut self) {
292 critical_section::with(|cs| {
293 let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
294 tx_done_callback_fn.take();
295 });
296 }
297
298 pub fn set_rx_available_callback_fn(&mut self, callback: fn()) {
300 critical_section::with(|cs| {
301 let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
302 rx_available_callback_fn.replace(callback);
303 });
304 }
305
306 pub fn clear_rx_available_callback_fn(&mut self) {
308 critical_section::with(|cs| {
309 let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
310 rx_available_callback_fn.take();
311 });
312 }
313}
314
315impl Drop for Ieee802154<'_> {
316 fn drop(&mut self) {
317 self.clear_tx_done_callback();
318 self.clear_tx_done_callback_fn();
319 self.clear_rx_available_callback();
320 self.clear_rx_available_callback_fn();
321 }
322}
323
324pub fn rssi_to_lqi(rssi: i8) -> u8 {
330 if rssi < -80 {
331 0
332 } else if rssi > -30 {
333 0xff
334 } else {
335 let lqi_convert = ((rssi as u32).wrapping_add(80)) * 255;
336 (lqi_convert / 50) as u8
337 }
338}
339
340static TX_DONE_CALLBACK: Mutex<RefCell<Option<&'static mut (dyn FnMut() + Send)>>> =
341 Mutex::new(RefCell::new(None));
342
343static RX_AVAILABLE_CALLBACK: Mutex<RefCell<Option<&'static mut (dyn FnMut() + Send)>>> =
344 Mutex::new(RefCell::new(None));
345
346#[allow(clippy::type_complexity)]
347static TX_DONE_CALLBACK_FN: Mutex<RefCell<Option<fn()>>> = Mutex::new(RefCell::new(None));
348
349#[allow(clippy::type_complexity)]
350static RX_AVAILABLE_CALLBACK_FN: Mutex<RefCell<Option<fn()>>> = Mutex::new(RefCell::new(None));
351
352fn tx_done() {
353 trace!("tx_done callback");
354
355 critical_section::with(|cs| {
356 let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
357 let tx_done_callback = tx_done_callback.as_mut();
358
359 if let Some(tx_done_callback) = tx_done_callback {
360 tx_done_callback();
361 }
362
363 let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
364 let tx_done_callback_fn = tx_done_callback_fn.as_mut();
365
366 if let Some(tx_done_callback_fn) = tx_done_callback_fn {
367 tx_done_callback_fn();
368 }
369 });
370}
371
372fn rx_available() {
373 trace!("rx available callback");
374
375 critical_section::with(|cs| {
376 let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
377 let rx_available_callback = rx_available_callback.as_mut();
378
379 if let Some(rx_available_callback) = rx_available_callback {
380 rx_available_callback();
381 }
382
383 let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
384 let rx_available_callback_fn = rx_available_callback_fn.as_mut();
385
386 if let Some(rx_available_callback_fn) = rx_available_callback_fn {
387 rx_available_callback_fn();
388 }
389 });
390}