esp_ieee802154/
raw.rs

1use core::cell::RefCell;
2
3use critical_section::Mutex;
4use esp_hal::{
5    clock::{ModemClockController, PhyClockGuard, init_radio_clocks},
6    handler,
7    interrupt::Priority,
8    peripherals::IEEE802154,
9};
10use esp_wifi_sys::include::{
11    esp_phy_calibration_data_t,
12    esp_phy_calibration_mode_t_PHY_RF_CAL_FULL,
13    ieee802154_coex_event_t,
14    ieee802154_coex_event_t_IEEE802154_IDLE,
15    ieee802154_coex_event_t_IEEE802154_LOW,
16    ieee802154_coex_event_t_IEEE802154_MIDDLE,
17    register_chipv7_phy,
18};
19use heapless::spsc::Queue;
20
21use crate::{
22    frame::{
23        FRAME_SIZE,
24        FRAME_VERSION_1,
25        FRAME_VERSION_2,
26        frame_get_version,
27        frame_is_ack_required,
28    },
29    hal::*,
30    pib::*,
31};
32
33const PHY_ENABLE_VERSION_PRINT: u32 = 1;
34
35static mut RX_BUFFER: [u8; FRAME_SIZE] = [0u8; FRAME_SIZE];
36static RX_QUEUE: Mutex<RefCell<Queue<RawReceived, { crate::CONFIG.rx_queue_size }>>> =
37    Mutex::new(RefCell::new(Queue::new()));
38static STATE: Mutex<RefCell<Ieee802154State>> = Mutex::new(RefCell::new(Ieee802154State::Idle));
39
40unsafe extern "C" {
41    fn bt_bb_v2_init_cmplx(print_version: u32); // from libbtbb.a
42
43    fn bt_bb_set_zb_tx_on_delay(time: u16); // from libbtbb.a
44
45    fn esp_coex_ieee802154_ack_pti_set(event: ieee802154_coex_event_t); // from ???
46
47    fn esp_coex_ieee802154_txrx_pti_set(event: ieee802154_coex_event_t); // from ???
48
49    fn phy_version_print(); // from libphy.a
50}
51
52#[derive(Debug, Clone, Copy, PartialEq)]
53enum Ieee802154State {
54    Idle,
55    Receive,
56    Transmit,
57    TxAck,
58}
59
60#[allow(unused)]
61#[derive(Debug, Clone, Copy, PartialEq)]
62enum Ieee802154TxRxScene {
63    Idle,
64    Tx,
65    Rx,
66    TxAt,
67    RxAt,
68}
69
70/// A raw payload received on some channel
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub struct RawReceived {
73    /// Payload
74    pub data: [u8; FRAME_SIZE],
75    /// Receiver channel
76    pub channel: u8,
77}
78
79pub(crate) fn esp_ieee802154_enable(mut radio: IEEE802154<'_>) -> PhyClockGuard<'_> {
80    init_radio_clocks();
81    let phy_clock_guard = radio.enable_phy_clock();
82    radio.enable_modem_clock(true);
83
84    esp_phy_enable();
85    esp_btbb_enable();
86    ieee802154_mac_init();
87
88    unsafe { phy_version_print() }; // libphy.a
89    info!("date={:x}", mac_date());
90    phy_clock_guard
91}
92
93fn esp_phy_enable() {
94    unsafe {
95        let mut calibration_data = esp_phy_calibration_data_t {
96            version: [0u8; 4],
97            mac: [0u8; 6],
98            opaque: [0u8; 1894],
99        };
100
101        register_chipv7_phy(
102            core::ptr::null(),
103            &mut calibration_data as *mut esp_phy_calibration_data_t,
104            esp_phy_calibration_mode_t_PHY_RF_CAL_FULL,
105        );
106    }
107}
108
109fn esp_btbb_enable() {
110    unsafe { bt_bb_v2_init_cmplx(PHY_ENABLE_VERSION_PRINT) };
111}
112
113fn ieee802154_mac_init() {
114    #[cfg(feature = "esp32c6")]
115    unsafe {
116        unsafe extern "C" {
117            static mut coex_pti_tab_ptr: u32;
118            static coex_pti_tab: u8;
119        }
120
121        // Manually set `coex_pti_tab_ptr` pointing to `coex_pti_tab`
122        core::ptr::addr_of_mut!(coex_pti_tab_ptr).write_volatile(&coex_pti_tab as *const _ as u32);
123    }
124
125    ieee802154_pib_init();
126
127    enable_events(Event::mask());
128    disable_events(Event::Timer0Overflow | Event::Timer1Overflow);
129
130    enable_tx_abort_events(
131        TxAbortReason::RxAckTimeout
132            | TxAbortReason::TxCoexBreak
133            | TxAbortReason::TxSecurityError
134            | TxAbortReason::CcaFailed
135            | TxAbortReason::CcaBusy
136            | TxAbortReason::TxStop,
137    );
138    enable_rx_abort_events(
139        RxAbortReason::TxAckTimeout | RxAbortReason::TxAckCoexBreak | RxAbortReason::RxStop,
140    );
141
142    set_ed_sample_mode(EdSampleMode::Avg);
143
144    unsafe { esp_coex_ieee802154_ack_pti_set(ieee802154_coex_event_t_IEEE802154_MIDDLE) };
145    ieee802154_set_txrx_pti(Ieee802154TxRxScene::Idle);
146
147    unsafe {
148        bt_bb_set_zb_tx_on_delay(50); // set tx on delay for libbtbb.a
149    }
150    set_rx_on_delay(50);
151
152    // memset(s_rx_frame, 0, sizeof(s_rx_frame));
153    // s_ieee802154_state = IEEE802154_STATE_IDLE;
154
155    unsafe {
156        esp_hal::interrupt::bind_interrupt(
157            esp_hal::peripherals::Interrupt::ZB_MAC,
158            ZB_MAC.handler(),
159        );
160    }
161    esp_hal::interrupt::enable(esp_hal::peripherals::Interrupt::ZB_MAC, ZB_MAC.priority()).unwrap();
162}
163
164fn ieee802154_set_txrx_pti(txrx_scene: Ieee802154TxRxScene) {
165    match txrx_scene {
166        Ieee802154TxRxScene::Idle => {
167            unsafe { esp_coex_ieee802154_txrx_pti_set(ieee802154_coex_event_t_IEEE802154_IDLE) };
168        }
169        Ieee802154TxRxScene::Tx | Ieee802154TxRxScene::Rx => {
170            unsafe { esp_coex_ieee802154_txrx_pti_set(ieee802154_coex_event_t_IEEE802154_LOW) };
171        }
172        Ieee802154TxRxScene::TxAt | Ieee802154TxRxScene::RxAt => {
173            unsafe { esp_coex_ieee802154_txrx_pti_set(ieee802154_coex_event_t_IEEE802154_MIDDLE) };
174        }
175    }
176}
177
178pub fn tx_init(frame: *const u8) {
179    let tx_frame = frame;
180    stop_current_operation();
181    ieee802154_pib_update();
182    ieee802154_sec_update();
183
184    set_tx_addr(tx_frame);
185
186    if true
187    // ieee802154_frame_is_ack_required(frame)
188    {
189        // set rx pointer for ack frame
190        set_next_rx_buffer();
191    }
192}
193
194pub fn ieee802154_transmit(frame: *const u8, cca: bool) -> i32 {
195    critical_section::with(|cs| {
196        tx_init(frame);
197
198        ieee802154_set_txrx_pti(Ieee802154TxRxScene::Tx);
199
200        if cca {
201            // disable_events(IEEE802154_EVENT_ED_DONE);
202            // set_cmd(IEEE802154_CMD_CCA_TX_START);
203            // ieee802154_state = IEEE802154_STATE_TX_CCA;
204        } else {
205            set_cmd(Command::TxStart);
206            // if (ieee802154_frame_get_type(frame) == IEEE802154_FRAME_TYPE_ACK
207            //     && ieee802154_frame_get_version(frame) == IEEE802154_FRAME_VERSION_2)
208            // {
209            //     ieee802154_state = IEEE802154_STATE_TX_ENH_ACK;
210            // } else {
211            *STATE.borrow_ref_mut(cs) = Ieee802154State::Transmit;
212            // }
213        }
214    });
215
216    0 // ESP_OK
217}
218
219pub fn ieee802154_receive() -> i32 {
220    critical_section::with(|cs| {
221        if *STATE.borrow_ref(cs) == Ieee802154State::Receive {
222            return;
223        }
224
225        rx_init();
226        enable_rx();
227
228        *STATE.borrow_ref_mut(cs) = Ieee802154State::Receive;
229    });
230
231    0 // ESP-OK
232}
233
234pub fn ieee802154_poll() -> Option<RawReceived> {
235    critical_section::with(|cs| {
236        let mut queue = RX_QUEUE.borrow_ref_mut(cs);
237        queue.dequeue()
238    })
239}
240
241fn rx_init() {
242    stop_current_operation();
243    ieee802154_pib_update();
244}
245
246fn enable_rx() {
247    set_next_rx_buffer();
248    ieee802154_set_txrx_pti(Ieee802154TxRxScene::Rx);
249
250    set_cmd(Command::RxStart);
251
252    // ieee802154_state = IEEE802154_STATE_RX;
253}
254
255fn stop_current_operation() {
256    let events = events();
257    set_cmd(Command::Stop);
258    clear_events(events);
259}
260
261fn set_next_rx_buffer() {
262    #[allow(unused_unsafe)] // stable compiler needs unsafe, nightly complains about it
263    unsafe {
264        set_rx_addr(core::ptr::addr_of_mut!(RX_BUFFER).cast());
265    }
266}
267
268pub fn set_promiscuous(enable: bool) {
269    ieee802154_pib_set_promiscuous(enable);
270}
271
272pub fn set_auto_ack_tx(enable: bool) {
273    ieee802154_pib_set_auto_ack_tx(enable);
274}
275
276pub fn set_auto_ack_rx(enable: bool) {
277    ieee802154_pib_set_auto_ack_rx(enable);
278}
279
280pub fn set_enhance_ack_tx(enable: bool) {
281    ieee802154_pib_set_enhance_ack_tx(enable);
282}
283
284pub fn set_coordinator(enable: bool) {
285    ieee802154_pib_set_coordinator(enable);
286}
287
288pub fn set_rx_when_idle(enable: bool) {
289    ieee802154_pib_set_rx_when_idle(enable);
290}
291
292pub fn set_tx_power(power: i8) {
293    ieee802154_pib_set_tx_power(power);
294}
295
296pub fn set_channel(channel: u8) {
297    ieee802154_pib_set_channel(channel);
298}
299
300#[allow(unused)]
301pub fn set_pending_mode(mode: PendingMode) {
302    ieee802154_pib_set_pending_mode(mode);
303}
304
305#[allow(unused)]
306pub fn set_multipan_enable(mask: u8) {
307    set_multipan_enable_mask(mask);
308}
309
310pub fn set_short_address(index: u8, address: u16) {
311    ieee802154_pib_set_short_address(index, address);
312}
313
314pub fn set_extended_address(index: u8, address: [u8; IEEE802154_FRAME_EXT_ADDR_SIZE]) {
315    ieee802154_pib_set_extended_address(index, address);
316}
317
318pub fn set_cca_theshold(cca_threshold: i8) {
319    ieee802154_pib_set_cca_theshold(cca_threshold);
320}
321
322pub fn set_cca_mode(mode: CcaMode) {
323    ieee802154_pib_set_cca_mode(mode);
324}
325
326pub fn set_panid(index: u8, id: u16) {
327    ieee802154_pib_set_panid(index, id);
328}
329
330#[inline(always)]
331fn ieee802154_sec_update() {
332    let is_security = false;
333    set_transmit_security(is_security);
334    // ieee802154_sec_clr_transmit_security();
335}
336
337fn next_operation() {
338    let previous_operation = critical_section::with(|cs| {
339        let state = *STATE.borrow_ref(cs);
340
341        if ieee802154_pib_get_rx_when_idle() {
342            enable_rx();
343            *STATE.borrow_ref_mut(cs) = Ieee802154State::Receive;
344        } else {
345            *STATE.borrow_ref_mut(cs) = Ieee802154State::Idle;
346        }
347
348        state
349    });
350
351    match previous_operation {
352        Ieee802154State::Receive => crate::rx_available(),
353        Ieee802154State::Transmit => crate::tx_done(),
354        Ieee802154State::TxAck => crate::tx_done(),
355        _ => (),
356    }
357}
358
359#[handler(priority = Priority::Priority1)]
360fn ZB_MAC() {
361    trace!("ZB_MAC interrupt");
362
363    let events = events();
364    clear_events(events);
365
366    trace!("events = {:032b}", events);
367
368    if events & Event::RxSfdDone != 0 {
369        // IEEE802154_STATE_TX && IEEE802154_STATE_TX_CCA && IEEE802154_STATE_TX_ENH_ACK
370        // for isr processing delay
371        trace!("rx sfd done");
372    }
373
374    if events & Event::TxSfdDone != 0 {
375        // IEEE802154_STATE_RX for isr processing delay, only 821
376        // IEEE802154_STATE_TX_ACK for workaround jira ZB-81.
377        trace!("tx sfd done");
378    }
379
380    if events & Event::TxDone != 0 {
381        trace!("tx done");
382        next_operation();
383    }
384
385    if events & Event::RxDone != 0 {
386        trace!("rx done");
387        unsafe {
388            trace!(
389                "Received raw {:?}",
390                crate::fmt::Bytes(&*core::ptr::addr_of!(RX_BUFFER))
391            );
392            critical_section::with(|cs| {
393                let mut queue = RX_QUEUE.borrow_ref_mut(cs);
394                if !queue.is_full() {
395                    let item = RawReceived {
396                        data: RX_BUFFER,
397                        channel: freq_to_channel(freq()),
398                    };
399                    queue.enqueue(item).ok();
400                } else {
401                    warn!("Receive queue full");
402                }
403
404                let frm = if RX_BUFFER[0] >= FRAME_SIZE as u8 {
405                    warn!("RX_BUFFER[0] {} is larger than frame size", RX_BUFFER[0]);
406                    &RX_BUFFER[1..][..FRAME_SIZE - 1]
407                } else {
408                    &RX_BUFFER[1..][..RX_BUFFER[0] as usize]
409                };
410                if will_auto_send_ack(frm) {
411                    *STATE.borrow_ref_mut(cs) = Ieee802154State::TxAck;
412                } else if should_send_enhanced_ack(frm) {
413                    // TODO
414                } else {
415                    // esp_ieee802154_coex_pti_set(IEEE802154_IDLE_RX);
416                    next_operation();
417                }
418            });
419        }
420    }
421
422    if events & Event::AckRxDone != 0 {
423        info!("EventAckRxDone");
424    }
425
426    if events & Event::AckTxDone != 0 {
427        trace!("EventAckTxDone");
428        next_operation();
429    }
430
431    if events & Event::TxAbort != 0 {
432        trace!("TxAbort");
433        abort_tx();
434    }
435
436    if events & Event::RxAbort != 0 {
437        trace!("RxAbort");
438        abort_rx();
439    }
440}
441
442fn freq_to_channel(freq: u8) -> u8 {
443    (freq - 3) / 5 + 11
444}
445
446fn will_auto_send_ack(frame: &[u8]) -> bool {
447    frame_is_ack_required(frame) && frame_get_version(frame) <= FRAME_VERSION_1 && tx_auto_ack()
448}
449
450fn should_send_enhanced_ack(frame: &[u8]) -> bool {
451    frame_is_ack_required(frame) && frame_get_version(frame) <= FRAME_VERSION_2 && tx_enhance_ack()
452}