esp-hal/esp-ieee802154/src/raw.rs

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()
}