esp_radio/ieee802154/
raw.rs

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