1#![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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum Error {
49 Incomplete,
52 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#[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#[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 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 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()); set_extended_address(0, address);
157 }
158 }
159
160 pub fn start_receive(&mut self) {
162 ieee802154_receive();
163 }
164
165 pub fn raw_received(&mut self) -> Option<RawReceived> {
167 ieee802154_poll()
168 }
169
170 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 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 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 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); Ok(())
232 }
233
234 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); Ok(())
242 }
243
244 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 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 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 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 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 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 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 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
320pub 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}