use alloc::collections::VecDeque as Queue;
use esp_hal::{handler, interrupt::Priority, peripherals::IEEE802154};
use esp_phy::{PhyClockGuard, PhyInitGuard};
use esp_sync::NonReentrantMutex;
use super::{
frame::{
FRAME_SIZE,
FRAME_VERSION_1,
FRAME_VERSION_2,
frame_get_version,
frame_is_ack_required,
},
hal::*,
pib::*,
};
use crate::{
radio_clocks::{clocks_ll::enable_ieee802154, init_radio_clocks},
sys::include::{
ieee802154_coex_event_t,
ieee802154_coex_event_t_IEEE802154_IDLE,
ieee802154_coex_event_t_IEEE802154_LOW,
ieee802154_coex_event_t_IEEE802154_MIDDLE,
},
};
const PHY_ENABLE_VERSION_PRINT: u8 = 1;
const ACK_TIMEOUT_US: u32 = 200_000;
static mut RX_BUFFER: [u8; FRAME_SIZE] = [0u8; FRAME_SIZE];
struct PendingTx {
frame: *const u8,
cca: bool,
}
unsafe impl Send for PendingTx {}
struct IeeeState {
state: Ieee802154State,
rx_queue: Queue<RawReceived>,
rx_queue_size: usize,
pending_tx: Option<PendingTx>,
ack_frame: Option<RawReceived>,
}
static STATE: NonReentrantMutex<IeeeState> = NonReentrantMutex::new(IeeeState {
state: Ieee802154State::Idle,
rx_queue: Queue::new(),
rx_queue_size: 10,
pending_tx: None,
ack_frame: None,
});
unsafe extern "C" {
fn bt_bb_v2_init_cmplx(print_version: u8);
fn bt_bb_set_zb_tx_on_delay(time: u16);
fn esp_coex_ieee802154_ack_pti_set(event: ieee802154_coex_event_t);
fn esp_coex_ieee802154_txrx_pti_set(event: ieee802154_coex_event_t);
fn esp_coex_ieee802154_coex_break_notify(); }
#[derive(Debug, Clone, Copy, PartialEq)]
enum Ieee802154State {
Idle,
Receive,
Transmit,
TxAck,
RxAck,
TxEnhAck,
}
#[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq)]
enum Ieee802154TxRxScene {
Idle,
Tx,
Rx,
TxAt,
RxAt,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub struct RawReceived {
pub data: [u8; FRAME_SIZE],
pub channel: u8,
}
pub(crate) fn esp_ieee802154_enable(
radio: IEEE802154<'_>,
) -> (PhyClockGuard<'_>, PhyInitGuard<'_>) {
init_radio_clocks();
let phy_clock_guard = esp_phy::enable_phy_clock();
enable_ieee802154(true);
let phy_init_guard = esp_phy::enable_phy();
esp_btbb_enable();
ieee802154_mac_init(radio);
info!("date={:x}", mac_date());
(phy_clock_guard, phy_init_guard)
}
fn esp_btbb_enable() {
unsafe { bt_bb_v2_init_cmplx(PHY_ENABLE_VERSION_PRINT) };
}
fn ieee802154_mac_init(radio: IEEE802154<'_>) {
#[cfg(any(esp32c6, esp32c5))]
unsafe {
unsafe extern "C" {
static mut coex_pti_tab_ptr: u32;
static coex_pti_tab: u8;
}
core::ptr::addr_of_mut!(coex_pti_tab_ptr).write_volatile(&coex_pti_tab as *const _ as u32);
}
ieee802154_pib_init();
enable_events(Event::mask());
disable_events(Event::Timer0Overflow | Event::Timer1Overflow);
enable_tx_abort_events(
TxAbortReason::RxAckTimeout
| TxAbortReason::TxCoexBreak
| TxAbortReason::TxSecurityError
| TxAbortReason::CcaFailed
| TxAbortReason::CcaBusy,
);
enable_rx_abort_events(RxAbortReason::TxAckTimeout | RxAbortReason::TxAckCoexBreak);
set_ed_sample_mode(EdSampleMode::Avg);
unsafe { esp_coex_ieee802154_ack_pti_set(ieee802154_coex_event_t_IEEE802154_MIDDLE) };
ieee802154_set_txrx_pti(Ieee802154TxRxScene::Idle);
unsafe {
bt_bb_set_zb_tx_on_delay(50); }
set_rx_on_delay(50);
radio.bind_mac_interrupt(zb_mac_handler);
}
fn ieee802154_set_txrx_pti(txrx_scene: Ieee802154TxRxScene) {
match txrx_scene {
Ieee802154TxRxScene::Idle => {
unsafe { esp_coex_ieee802154_txrx_pti_set(ieee802154_coex_event_t_IEEE802154_IDLE) };
}
Ieee802154TxRxScene::Tx | Ieee802154TxRxScene::Rx => {
unsafe { esp_coex_ieee802154_txrx_pti_set(ieee802154_coex_event_t_IEEE802154_LOW) };
}
Ieee802154TxRxScene::TxAt | Ieee802154TxRxScene::RxAt => {
unsafe { esp_coex_ieee802154_txrx_pti_set(ieee802154_coex_event_t_IEEE802154_MIDDLE) };
}
}
}
fn tx_init(state: &mut IeeeState, frame: *const u8) {
stop_current_operation_inner(state);
ieee802154_pib_update();
set_transmit_security(false);
state.ack_frame = None;
set_tx_addr(frame);
if frame_is_ack_required(unsafe { core::slice::from_raw_parts(frame.add(1), *frame as usize) })
{
set_next_rx_buffer();
}
}
pub(crate) fn set_queue_size(rx_queue_size: usize) {
STATE.with(|state| {
state.rx_queue_size = rx_queue_size;
});
}
static mut TX_FRAME: *const u8 = core::ptr::null();
pub fn ieee802154_transmit(frame: *const u8, cca: bool) -> i32 {
STATE.with(|state| {
if state.state == Ieee802154State::TxAck
|| state.state == Ieee802154State::TxEnhAck
|| (state.state == Ieee802154State::Receive && is_current_rx_frame())
{
state.pending_tx = Some(PendingTx { frame, cca });
enable_rx_abort_events(RxAbortReason::all());
return;
}
state.pending_tx = None;
transmit_internal(state, frame, cca);
});
0 }
fn transmit_internal(state: &mut IeeeState, frame: *const u8, cca: bool) {
unsafe { TX_FRAME = frame };
tx_init(state, frame);
ieee802154_set_txrx_pti(Ieee802154TxRxScene::Tx);
if cca {
set_cmd(Command::CcaTxStart);
} else {
set_cmd(Command::TxStart);
}
state.state = Ieee802154State::Transmit;
}
pub fn ieee802154_receive() -> i32 {
STATE.with(|state| {
if state.state == Ieee802154State::Receive || state.state == Ieee802154State::TxAck {
return;
}
rx_init(state);
enable_rx();
state.state = Ieee802154State::Receive;
});
0 }
pub fn ieee802154_poll() -> Option<RawReceived> {
STATE.with(|state| state.rx_queue.pop_front())
}
pub fn get_ack_frame() -> Option<RawReceived> {
STATE.with(|state| state.ack_frame)
}
fn rx_init(state: &mut IeeeState) {
stop_current_operation_inner(state);
ieee802154_pib_update();
}
fn enable_rx() {
set_next_rx_buffer();
ieee802154_set_txrx_pti(Ieee802154TxRxScene::Rx);
set_cmd(Command::RxStart);
}
fn stop_current_operation_inner(state: &mut IeeeState) {
event_end_process();
match state.state {
Ieee802154State::Idle => {
set_cmd(Command::Stop);
}
Ieee802154State::Receive => {
stop_rx(state);
}
Ieee802154State::TxAck => {
stop_tx_ack();
}
Ieee802154State::Transmit | Ieee802154State::TxEnhAck => {
stop_tx(state);
}
Ieee802154State::RxAck => {
stop_rx_ack(state);
}
}
}
fn stop_rx(state: &mut IeeeState) {
set_cmd(Command::Stop);
let evts = events();
if evts & Event::RxDone != 0 {
receive_done(state);
}
clear_events(Event::RxDone | Event::RxAbort | Event::RxSfdDone);
}
fn stop_tx_ack() {
set_cmd(Command::Stop);
clear_events(Event::AckTxDone | Event::RxAbort | Event::TxSfdDone);
}
fn stop_tx(state: &mut IeeeState) {
set_cmd(Command::Stop);
let evts = events();
if state.state == Ieee802154State::TxEnhAck {
clear_events(Event::AckTxDone as u16);
} else if (evts & Event::TxDone != 0)
&& (!frame_is_ack_required(unsafe {
core::slice::from_raw_parts(TX_FRAME.add(1), *TX_FRAME as usize)
}) || !rx_auto_ack())
{
super::tx_done();
} else {
super::tx_failed();
}
clear_events(Event::TxDone | Event::TxAbort | Event::TxSfdDone);
}
fn stop_rx_ack(state: &mut IeeeState) {
set_cmd(Command::Stop);
let evts = events();
timer0_stop();
disable_events(Event::Timer0Overflow as u16);
if evts & Event::AckRxDone != 0 {
state.ack_frame = Some(RawReceived {
data: unsafe { RX_BUFFER },
channel: freq_to_channel(freq()),
});
super::tx_done();
} else {
super::tx_failed();
}
clear_events(Event::AckRxDone | Event::RxSfdDone | Event::TxAbort);
}
fn receive_done(state: &mut IeeeState) {
unsafe {
if state.rx_queue.len() < state.rx_queue_size {
let item = RawReceived {
data: RX_BUFFER,
channel: freq_to_channel(freq()),
};
state.rx_queue.push_back(item);
} else {
warn!("Receive queue full");
}
}
}
fn set_next_rx_buffer() {
set_rx_addr(core::ptr::addr_of_mut!(RX_BUFFER).cast());
}
pub fn set_promiscuous(enable: bool) {
ieee802154_pib_set_promiscuous(enable);
}
pub fn set_auto_ack_tx(enable: bool) {
ieee802154_pib_set_auto_ack_tx(enable);
}
pub fn set_auto_ack_rx(enable: bool) {
ieee802154_pib_set_auto_ack_rx(enable);
}
pub fn set_enhance_ack_tx(enable: bool) {
ieee802154_pib_set_enhance_ack_tx(enable);
}
pub fn set_coordinator(enable: bool) {
ieee802154_pib_set_coordinator(enable);
}
pub fn set_rx_when_idle(enable: bool) {
ieee802154_pib_set_rx_when_idle(enable);
}
pub fn set_tx_power(power: i8) {
ieee802154_pib_set_tx_power(power);
}
pub fn set_channel(channel: u8) {
ieee802154_pib_set_channel(channel);
}
#[allow(unused)]
pub fn set_pending_mode(mode: PendingMode) {
ieee802154_pib_set_pending_mode(mode);
}
#[allow(unused)]
pub fn set_multipan_enable(mask: u8) {
set_multipan_enable_mask(mask);
}
pub fn set_short_address(index: u8, address: u16) {
ieee802154_pib_set_short_address(index, address);
}
pub fn set_extended_address(index: u8, address: [u8; IEEE802154_FRAME_EXT_ADDR_SIZE]) {
ieee802154_pib_set_extended_address(index, address);
}
pub fn set_cca_theshold(cca_threshold: i8) {
ieee802154_pib_set_cca_theshold(cca_threshold);
}
pub fn set_cca_mode(mode: CcaMode) {
ieee802154_pib_set_cca_mode(mode);
}
pub fn set_panid(index: u8, id: u16) {
ieee802154_pib_set_panid(index, id);
}
#[inline(always)]
fn event_end_process() {
set_transmit_security(false);
timer0_stop();
}
fn next_operation_inner(state: &mut IeeeState) {
state.state = Ieee802154State::Idle;
if let Some(pending) = state.pending_tx.take() {
disable_rx_abort_events(RxAbortReason::all());
enable_rx_abort_events(RxAbortReason::TxAckTimeout | RxAbortReason::TxAckCoexBreak);
clear_events(Event::RxAbort as u16);
transmit_internal(state, pending.frame, pending.cca);
} else if ieee802154_pib_get_rx_when_idle() {
enable_rx();
state.state = Ieee802154State::Receive;
}
}
fn next_operation() {
STATE.with(next_operation_inner);
}
pub(crate) fn ensure_receive_enabled() {
STATE.with(|state| {
if state.state == Ieee802154State::Receive {
set_cmd(Command::RxStart);
}
});
}
#[handler(priority = Priority::Priority1)]
fn zb_mac_handler() {
trace!("ZB_MAC interrupt");
let events = events();
let rx_abort_reason = get_rx_abort_reason();
let tx_abort_reason = get_tx_abort_reason();
clear_events(events);
trace!("events = {:032b}", events);
let mut needs_next_operation = false;
if events & Event::RxAbort != 0 {
trace!("RxAbort phase 1");
isr_handle_rx_phase_rx_abort(rx_abort_reason, &mut needs_next_operation);
}
if events & Event::RxSfdDone != 0 {
trace!("rx sfd done");
}
if events & Event::TxSfdDone != 0 {
trace!("tx sfd done");
}
if events & Event::TxDone != 0 {
trace!("tx done");
isr_handle_tx_done(&mut needs_next_operation);
}
if events & Event::RxDone != 0 {
trace!("rx done");
isr_handle_rx_done(&mut needs_next_operation);
}
if events & Event::AckTxDone != 0 {
trace!("AckTxDone");
isr_handle_ack_tx_done(&mut needs_next_operation);
}
if events & Event::AckRxDone != 0 {
trace!("AckRxDone");
isr_handle_ack_rx_done(&mut needs_next_operation);
}
if events & Event::RxAbort != 0 {
trace!("RxAbort phase 2");
isr_handle_tx_ack_phase_rx_abort(rx_abort_reason, &mut needs_next_operation);
}
if events & Event::TxAbort != 0 {
trace!("TxAbort");
isr_handle_tx_abort(tx_abort_reason, &mut needs_next_operation);
}
if events & Event::Timer0Overflow != 0 {
trace!("Timer0Overflow");
isr_handle_timer0_done(&mut needs_next_operation);
}
if needs_next_operation {
next_operation();
}
}
fn isr_handle_tx_done(needs_next_op: &mut bool) {
event_end_process();
STATE.with(|state| {
if state.state == Ieee802154State::Transmit {
let tx_frame = unsafe { TX_FRAME };
let frame_data =
unsafe { core::slice::from_raw_parts(tx_frame.add(1), *tx_frame as usize) };
if frame_is_ack_required(frame_data) && rx_auto_ack() {
state.state = Ieee802154State::RxAck;
receive_ack_timeout_timer_start(ACK_TIMEOUT_US);
*needs_next_op = false;
} else {
super::tx_done();
*needs_next_op = true;
}
}
});
}
fn isr_handle_rx_done(needs_next_op: &mut bool) {
event_end_process();
unsafe {
trace!(
"Received raw {:?}",
crate::fmt::Bytes(&*core::ptr::addr_of!(RX_BUFFER))
);
STATE.with(|state| {
let frm = if RX_BUFFER[0] >= FRAME_SIZE as u8 {
warn!("RX_BUFFER[0] {} is larger than frame size", RX_BUFFER[0]);
&RX_BUFFER[1..][..FRAME_SIZE - 1]
} else {
&RX_BUFFER[1..][..RX_BUFFER[0] as usize]
};
receive_done(state);
if will_auto_send_ack(frm) {
state.state = Ieee802154State::TxAck;
*needs_next_op = false;
} else if should_send_enhanced_ack(frm) {
state.state = Ieee802154State::TxEnhAck;
*needs_next_op = false;
} else {
super::rx_available();
*needs_next_op = true;
}
});
}
}
fn isr_handle_ack_tx_done(needs_next_op: &mut bool) {
super::rx_available();
*needs_next_op = true;
}
fn isr_handle_ack_rx_done(needs_next_op: &mut bool) {
timer0_stop();
disable_events(Event::Timer0Overflow as u16);
STATE.with(|state| {
state.ack_frame = Some(RawReceived {
data: unsafe { RX_BUFFER },
channel: freq_to_channel(freq()),
});
});
super::tx_done();
*needs_next_op = true;
}
fn isr_handle_rx_phase_rx_abort(rx_abort_reason: u32, needs_next_op: &mut bool) {
event_end_process();
match rx_abort_reason {
r if r == RxAbortReason::RxStop as u32
|| r == RxAbortReason::TxAckStop as u32
|| r == RxAbortReason::EdStop as u32 => {}
r if r == RxAbortReason::SfdTimeout as u32
|| r == RxAbortReason::CrcError as u32
|| r == RxAbortReason::InvalidLen as u32
|| r == RxAbortReason::FilterFail as u32
|| r == RxAbortReason::NoRss as u32
|| r == RxAbortReason::UnexpectedAck as u32
|| r == RxAbortReason::RxRestart as u32
|| r == RxAbortReason::CoexBreak as u32 =>
{
*needs_next_op = true;
}
r if r == RxAbortReason::TxAckTimeout as u32
|| r == RxAbortReason::TxAckCoexBreak as u32
|| r == RxAbortReason::EnhackSecurityError as u32 => {}
_ => {
warn!("Unexpected rx abort reason: {}", rx_abort_reason);
*needs_next_op = true;
}
}
}
fn isr_handle_tx_ack_phase_rx_abort(rx_abort_reason: u32, needs_next_op: &mut bool) {
event_end_process();
match rx_abort_reason {
r if r == RxAbortReason::TxAckStop as u32
|| r == RxAbortReason::RxStop as u32
|| r == RxAbortReason::EdStop as u32
|| r == RxAbortReason::SfdTimeout as u32
|| r == RxAbortReason::CrcError as u32
|| r == RxAbortReason::InvalidLen as u32
|| r == RxAbortReason::FilterFail as u32
|| r == RxAbortReason::NoRss as u32
|| r == RxAbortReason::UnexpectedAck as u32
|| r == RxAbortReason::RxRestart as u32
|| r == RxAbortReason::CoexBreak as u32
|| r == RxAbortReason::EdAbort as u32
|| r == RxAbortReason::EdCoexReject as u32 => {}
r if r == RxAbortReason::TxAckTimeout as u32
|| r == RxAbortReason::TxAckCoexBreak as u32 =>
{
super::rx_available();
*needs_next_op = true;
}
r if r == RxAbortReason::EnhackSecurityError as u32 => {
super::rx_available();
*needs_next_op = true;
}
_ => {
warn!(
"Unexpected rx abort reason in tx_ack phase: {}",
rx_abort_reason
);
}
}
}
fn isr_handle_timer0_done(needs_next_op: &mut bool) {
timer0_stop();
disable_events(Event::Timer0Overflow as u16);
super::tx_failed();
*needs_next_op = true;
}
fn isr_handle_tx_abort(tx_abort_reason: u32, needs_next_op: &mut bool) {
event_end_process();
match tx_abort_reason {
r if r == TxAbortReason::RxAckStop as u32 || r == TxAbortReason::TxStop as u32 => {
}
r if r == TxAbortReason::RxAckSfdTimeout as u32
|| r == TxAbortReason::RxAckCrcError as u32
|| r == TxAbortReason::RxAckInvalidLen as u32
|| r == TxAbortReason::RxAckFilterFail as u32
|| r == TxAbortReason::RxAckNoRss as u32
|| r == TxAbortReason::RxAckCoexBreak as u32
|| r == TxAbortReason::RxAckTypeNotAck as u32
|| r == TxAbortReason::RxAckRestart as u32 =>
{
super::tx_failed();
*needs_next_op = true;
}
r if r == TxAbortReason::RxAckTimeout as u32 => {
timer0_stop();
disable_events(Event::Timer0Overflow as u16);
super::tx_failed();
*needs_next_op = true;
}
r if r == TxAbortReason::TxCoexBreak as u32 => {
unsafe { esp_coex_ieee802154_coex_break_notify() };
super::tx_failed();
*needs_next_op = true;
}
r if r == TxAbortReason::TxSecurityError as u32 => {
super::tx_failed();
*needs_next_op = true;
}
r if r == TxAbortReason::CcaFailed as u32 => {
super::tx_failed();
*needs_next_op = true;
}
r if r == TxAbortReason::CcaBusy as u32 => {
super::tx_failed();
*needs_next_op = true;
}
_ => {
warn!("Unexpected tx abort reason: {}", tx_abort_reason);
}
}
}
fn freq_to_channel(freq: u8) -> u8 {
(freq - 3) / 5 + 11
}
fn will_auto_send_ack(frame: &[u8]) -> bool {
frame_is_ack_required(frame) && frame_get_version(frame) <= FRAME_VERSION_1 && tx_auto_ack()
}
fn should_send_enhanced_ack(frame: &[u8]) -> bool {
frame_is_ack_required(frame) && frame_get_version(frame) <= FRAME_VERSION_2 && tx_enhance_ack()
}
fn receive_ack_timeout_timer_start(duration: u32) {
enable_events(Event::Timer0Overflow as u16);
timer0_set_threshold(duration);
timer0_start();
}