449 lines
12 KiB
Rust
449 lines
12 KiB
Rust
use core::cell::RefCell;
|
|
|
|
use critical_section::Mutex;
|
|
use esp_hal::{
|
|
interrupt::Priority,
|
|
peripherals::RADIO_CLK,
|
|
prelude::handler,
|
|
system::{RadioClockController, RadioPeripherals},
|
|
};
|
|
use esp_wifi_sys::include::{
|
|
esp_phy_calibration_data_t,
|
|
esp_phy_calibration_mode_t_PHY_RF_CAL_FULL,
|
|
ieee802154_coex_event_t,
|
|
ieee802154_coex_event_t_IEEE802154_IDLE,
|
|
ieee802154_coex_event_t_IEEE802154_LOW,
|
|
ieee802154_coex_event_t_IEEE802154_MIDDLE,
|
|
register_chipv7_phy,
|
|
};
|
|
use heapless::spsc::Queue;
|
|
|
|
use crate::{
|
|
frame::{
|
|
frame_get_version,
|
|
frame_is_ack_required,
|
|
FRAME_SIZE,
|
|
FRAME_VERSION_1,
|
|
FRAME_VERSION_2,
|
|
},
|
|
hal::*,
|
|
pib::*,
|
|
};
|
|
|
|
const PHY_ENABLE_VERSION_PRINT: u32 = 1;
|
|
|
|
static mut RX_BUFFER: [u8; FRAME_SIZE] = [0u8; FRAME_SIZE];
|
|
static RX_QUEUE: Mutex<RefCell<Queue<RawReceived, 20>>> = Mutex::new(RefCell::new(Queue::new()));
|
|
static STATE: Mutex<RefCell<Ieee802154State>> = Mutex::new(RefCell::new(Ieee802154State::Idle));
|
|
|
|
extern "C" {
|
|
fn bt_bb_v2_init_cmplx(print_version: u32); // from libbtbb.a
|
|
|
|
fn bt_bb_set_zb_tx_on_delay(time: u16); // from libbtbb.a
|
|
|
|
fn esp_coex_ieee802154_ack_pti_set(event: ieee802154_coex_event_t); // from ???
|
|
|
|
fn esp_coex_ieee802154_txrx_pti_set(event: ieee802154_coex_event_t); // from ???
|
|
|
|
fn phy_version_print(); // from libphy.a
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
enum Ieee802154State {
|
|
Idle,
|
|
Receive,
|
|
Transmit,
|
|
TxAck,
|
|
}
|
|
|
|
#[allow(unused)]
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
enum Ieee802154TxRxScene {
|
|
Idle,
|
|
Tx,
|
|
Rx,
|
|
TxAt,
|
|
RxAt,
|
|
}
|
|
|
|
/// A raw payload received on some channel
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct RawReceived {
|
|
/// Payload
|
|
pub data: [u8; FRAME_SIZE],
|
|
/// Receiver channel
|
|
pub channel: u8,
|
|
}
|
|
|
|
pub(crate) fn esp_ieee802154_enable(radio_clock_control: &mut RADIO_CLK) {
|
|
radio_clock_control.init_clocks();
|
|
radio_clock_control.enable(RadioPeripherals::Phy);
|
|
radio_clock_control.enable(RadioPeripherals::Ieee802154);
|
|
|
|
esp_phy_enable();
|
|
esp_btbb_enable();
|
|
ieee802154_mac_init();
|
|
|
|
unsafe { phy_version_print() }; // libphy.a
|
|
log::info!("date={:x}", mac_date());
|
|
}
|
|
|
|
fn esp_phy_enable() {
|
|
unsafe {
|
|
let mut calibration_data = esp_phy_calibration_data_t {
|
|
version: [0u8; 4],
|
|
mac: [0u8; 6],
|
|
opaque: [0u8; 1894],
|
|
};
|
|
|
|
register_chipv7_phy(
|
|
core::ptr::null(),
|
|
&mut calibration_data as *mut esp_phy_calibration_data_t,
|
|
esp_phy_calibration_mode_t_PHY_RF_CAL_FULL,
|
|
);
|
|
}
|
|
}
|
|
|
|
fn esp_btbb_enable() {
|
|
unsafe { bt_bb_v2_init_cmplx(PHY_ENABLE_VERSION_PRINT) };
|
|
}
|
|
|
|
fn ieee802154_mac_init() {
|
|
#[cfg(feature = "esp32c6")]
|
|
unsafe {
|
|
extern "C" {
|
|
static mut coex_pti_tab_ptr: u32;
|
|
static coex_pti_tab: u8;
|
|
}
|
|
|
|
// Manually set `coex_pti_tab_ptr` pointing to `coex_pti_tab`
|
|
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
|
|
| TxAbortReason::TxStop,
|
|
);
|
|
enable_rx_abort_events(
|
|
RxAbortReason::TxAckTimeout | RxAbortReason::TxAckCoexBreak | RxAbortReason::RxStop,
|
|
);
|
|
|
|
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 tx on delay for libbtbb.a
|
|
}
|
|
set_rx_on_delay(50);
|
|
|
|
// memset(s_rx_frame, 0, sizeof(s_rx_frame));
|
|
// s_ieee802154_state = IEEE802154_STATE_IDLE;
|
|
|
|
unsafe {
|
|
esp_hal::interrupt::bind_interrupt(
|
|
esp_hal::peripherals::Interrupt::ZB_MAC,
|
|
ZB_MAC.handler(),
|
|
);
|
|
}
|
|
esp_hal::interrupt::enable(esp_hal::peripherals::Interrupt::ZB_MAC, ZB_MAC.priority()).unwrap();
|
|
}
|
|
|
|
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) };
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn tx_init(frame: *const u8) {
|
|
let tx_frame = frame;
|
|
stop_current_operation();
|
|
ieee802154_pib_update();
|
|
ieee802154_sec_update();
|
|
|
|
set_tx_addr(tx_frame);
|
|
|
|
if true
|
|
// ieee802154_frame_is_ack_required(frame)
|
|
{
|
|
// set rx pointer for ack frame
|
|
set_next_rx_buffer();
|
|
}
|
|
}
|
|
|
|
pub fn ieee802154_transmit(frame: *const u8, cca: bool) -> i32 {
|
|
critical_section::with(|cs| {
|
|
tx_init(frame);
|
|
|
|
ieee802154_set_txrx_pti(Ieee802154TxRxScene::Tx);
|
|
|
|
if cca {
|
|
// disable_events(IEEE802154_EVENT_ED_DONE);
|
|
// set_cmd(IEEE802154_CMD_CCA_TX_START);
|
|
// ieee802154_state = IEEE802154_STATE_TX_CCA;
|
|
} else {
|
|
set_cmd(Command::TxStart);
|
|
// if (ieee802154_frame_get_type(frame) == IEEE802154_FRAME_TYPE_ACK
|
|
// && ieee802154_frame_get_version(frame) == IEEE802154_FRAME_VERSION_2)
|
|
// {
|
|
// ieee802154_state = IEEE802154_STATE_TX_ENH_ACK;
|
|
// } else {
|
|
*STATE.borrow_ref_mut(cs) = Ieee802154State::Transmit;
|
|
// }
|
|
}
|
|
});
|
|
|
|
0 // ESP_OK
|
|
}
|
|
|
|
pub fn ieee802154_receive() -> i32 {
|
|
critical_section::with(|cs| {
|
|
if *STATE.borrow_ref(cs) == Ieee802154State::Receive {
|
|
return;
|
|
}
|
|
|
|
rx_init();
|
|
enable_rx();
|
|
|
|
*STATE.borrow_ref_mut(cs) = Ieee802154State::Receive;
|
|
});
|
|
|
|
0 // ESP-OK
|
|
}
|
|
|
|
pub fn ieee802154_poll() -> Option<RawReceived> {
|
|
critical_section::with(|cs| {
|
|
let mut queue = RX_QUEUE.borrow_ref_mut(cs);
|
|
queue.dequeue()
|
|
})
|
|
}
|
|
|
|
fn rx_init() {
|
|
stop_current_operation();
|
|
ieee802154_pib_update();
|
|
}
|
|
|
|
fn enable_rx() {
|
|
set_next_rx_buffer();
|
|
ieee802154_set_txrx_pti(Ieee802154TxRxScene::Rx);
|
|
|
|
set_cmd(Command::RxStart);
|
|
|
|
// ieee802154_state = IEEE802154_STATE_RX;
|
|
}
|
|
|
|
fn stop_current_operation() {
|
|
let events = get_events();
|
|
set_cmd(Command::Stop);
|
|
clear_events(events);
|
|
}
|
|
|
|
fn set_next_rx_buffer() {
|
|
unsafe {
|
|
set_rx_addr(RX_BUFFER.as_mut_ptr());
|
|
}
|
|
}
|
|
|
|
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 ieee802154_sec_update() {
|
|
let is_security = false;
|
|
set_transmit_security(is_security);
|
|
// ieee802154_sec_clr_transmit_security();
|
|
}
|
|
|
|
fn next_operation() {
|
|
let previous_operation = critical_section::with(|cs| {
|
|
let state = *STATE.borrow_ref(cs);
|
|
|
|
if ieee802154_pib_get_rx_when_idle() {
|
|
enable_rx();
|
|
*STATE.borrow_ref_mut(cs) = Ieee802154State::Receive;
|
|
} else {
|
|
*STATE.borrow_ref_mut(cs) = Ieee802154State::Idle;
|
|
}
|
|
|
|
state
|
|
});
|
|
|
|
match previous_operation {
|
|
Ieee802154State::Receive => crate::rx_available(),
|
|
Ieee802154State::Transmit => crate::tx_done(),
|
|
Ieee802154State::TxAck => crate::tx_done(),
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
#[handler(priority = "Priority::Priority1")]
|
|
fn ZB_MAC() {
|
|
log::trace!("ZB_MAC interrupt");
|
|
|
|
let events = get_events();
|
|
clear_events(events);
|
|
|
|
log::trace!("events = {:032b}", events);
|
|
|
|
if events & Event::RxSfdDone != 0 {
|
|
// IEEE802154_STATE_TX && IEEE802154_STATE_TX_CCA && IEEE802154_STATE_TX_ENH_ACK
|
|
// for isr processing delay
|
|
log::trace!("rx sfd done");
|
|
}
|
|
|
|
if events & Event::TxSfdDone != 0 {
|
|
// IEEE802154_STATE_RX for isr processing delay, only 821
|
|
// IEEE802154_STATE_TX_ACK for workaround jira ZB-81.
|
|
log::trace!("tx sfd done");
|
|
}
|
|
|
|
if events & Event::TxDone != 0 {
|
|
log::trace!("tx done");
|
|
next_operation();
|
|
}
|
|
|
|
if events & Event::RxDone != 0 {
|
|
log::trace!("rx done");
|
|
unsafe {
|
|
log::trace!("Received raw {:x?}", RX_BUFFER);
|
|
critical_section::with(|cs| {
|
|
let mut queue = RX_QUEUE.borrow_ref_mut(cs);
|
|
if !queue.is_full() {
|
|
let item = RawReceived {
|
|
data: RX_BUFFER,
|
|
channel: freq_to_channel(get_freq()),
|
|
};
|
|
queue.enqueue(item).ok();
|
|
} else {
|
|
log::warn!("Receive queue full");
|
|
}
|
|
|
|
let frm = if RX_BUFFER[0] >= FRAME_SIZE as u8 {
|
|
log::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]
|
|
};
|
|
if will_auto_send_ack(frm) {
|
|
*STATE.borrow_ref_mut(cs) = Ieee802154State::TxAck;
|
|
} else if should_send_enhanced_ack(frm) {
|
|
// TODO
|
|
} else {
|
|
// esp_ieee802154_coex_pti_set(IEEE802154_IDLE_RX);
|
|
next_operation();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
if events & Event::AckRxDone != 0 {
|
|
log::info!("EventAckRxDone");
|
|
}
|
|
|
|
if events & Event::AckTxDone != 0 {
|
|
log::trace!("EventAckTxDone");
|
|
next_operation();
|
|
}
|
|
|
|
if events & Event::TxAbort != 0 {
|
|
log::trace!("TxAbort");
|
|
abort_tx();
|
|
}
|
|
|
|
if events & Event::RxAbort != 0 {
|
|
log::trace!("RxAbort");
|
|
abort_rx();
|
|
}
|
|
}
|
|
|
|
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 && get_tx_auto_ack()
|
|
}
|
|
|
|
fn should_send_enhanced_ack(frame: &[u8]) -> bool {
|
|
frame_is_ack_required(frame)
|
|
&& frame_get_version(frame) <= FRAME_VERSION_2
|
|
&& get_tx_enhance_ack()
|
|
}
|