use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower};
use crate::interrupt::typelevel::Interrupt;
use crate::interrupt::{self};
use crate::pac::radio::vals;
use crate::Peripheral;
pub const DEFAULT_SFD: u8 = 0xA7;
pub enum Cca {
CarrierSense,
EnergyDetection {
ed_threshold: u8,
},
}
pub struct Radio<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
needs_enable: bool,
}
impl<'d, T: Instance> Radio<'d, T> {
pub fn new(
radio: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self {
into_ref!(radio);
let r = T::regs();
r.power().write(|w| w.set_power(false));
r.power().write(|w| w.set_power(true));
r.mode().write(|w| w.set_mode(vals::Mode::IEEE802154_250KBIT));
r.crccnf().write(|w| {
w.set_len(vals::Len::TWO);
w.set_skipaddr(vals::Skipaddr::IEEE802154);
});
r.crcpoly().write(|w| w.set_crcpoly(0x0001_1021));
r.crcinit().write(|w| w.set_crcinit(0));
r.pcnf0().write(|w| {
w.set_lflen(8);
w.set_s0len(false);
w.set_s1len(0);
w.set_s1incl(vals::S1incl::AUTOMATIC);
w.set_cilen(0);
w.set_plen(vals::Plen::_32BIT_ZERO);
w.set_crcinc(vals::Crcinc::INCLUDE);
});
r.pcnf1().write(|w| {
w.set_maxlen(Packet::MAX_PSDU_LEN);
w.set_statlen(0);
w.set_balen(0);
w.set_endian(vals::Endian::LITTLE);
w.set_whiteen(false);
});
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
let mut radio = Self {
_p: radio,
needs_enable: false,
};
radio.set_sfd(DEFAULT_SFD);
radio.set_transmission_power(0);
radio.set_channel(11);
radio.set_cca(Cca::CarrierSense);
radio
}
pub fn set_channel(&mut self, channel: u8) {
let r = T::regs();
if channel < 11 || channel > 26 {
panic!("Bad 802.15.4 channel");
}
let frequency_offset = (channel - 10) * 5;
self.needs_enable = true;
r.frequency().write(|w| {
w.set_frequency(frequency_offset);
w.set_map(vals::Map::DEFAULT);
});
}
pub fn set_cca(&mut self, cca: Cca) {
let r = T::regs();
self.needs_enable = true;
match cca {
Cca::CarrierSense => r.ccactrl().write(|w| w.set_ccamode(vals::Ccamode::CARRIER_MODE)),
Cca::EnergyDetection { ed_threshold } => {
r.ccactrl().write(|w| {
w.set_ccamode(vals::Ccamode::ED_MODE);
w.set_ccaedthres(ed_threshold);
});
}
}
}
pub fn set_sfd(&mut self, sfd: u8) {
let r = T::regs();
r.sfd().write(|w| w.set_sfd(sfd));
}
pub fn clear_all_interrupts(&mut self) {
let r = T::regs();
r.intenclr().write(|w| w.0 = 0xffff_ffff);
}
pub fn set_transmission_power(&mut self, power: i8) {
let r = T::regs();
self.needs_enable = true;
let tx_power: TxPower = match power {
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
8 => TxPower::POS8_DBM,
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
7 => TxPower::POS7_DBM,
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
6 => TxPower::POS6_DBM,
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
5 => TxPower::POS5_DBM,
#[cfg(not(feature = "_nrf5340-net"))]
4 => TxPower::POS4_DBM,
#[cfg(not(feature = "_nrf5340-net"))]
3 => TxPower::POS3_DBM,
#[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))]
2 => TxPower::POS2_DBM,
0 => TxPower::_0_DBM,
#[cfg(feature = "_nrf5340-net")]
-1 => TxPower::NEG1_DBM,
#[cfg(feature = "_nrf5340-net")]
-2 => TxPower::NEG2_DBM,
#[cfg(feature = "_nrf5340-net")]
-3 => TxPower::NEG3_DBM,
-4 => TxPower::NEG4_DBM,
#[cfg(feature = "_nrf5340-net")]
-5 => TxPower::NEG5_DBM,
#[cfg(feature = "_nrf5340-net")]
-6 => TxPower::NEG6_DBM,
#[cfg(feature = "_nrf5340-net")]
-7 => TxPower::NEG7_DBM,
-8 => TxPower::NEG8_DBM,
-12 => TxPower::NEG12_DBM,
-16 => TxPower::NEG16_DBM,
-20 => TxPower::NEG20_DBM,
-30 => TxPower::NEG30_DBM,
-40 => TxPower::NEG40_DBM,
_ => panic!("Invalid transmission power value"),
};
r.txpower().write(|w| w.set_txpower(tx_power));
}
fn wait_for_radio_state(&self, state: RadioState) {
while self.state() != state {}
}
fn state(&self) -> RadioState {
state(T::regs())
}
fn disable(&mut self) {
let r = T::regs();
loop {
match self.state() {
RadioState::DISABLED => return,
RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => {
r.tasks_disable().write_value(1);
self.wait_for_radio_state(RadioState::DISABLED);
return;
}
RadioState::RX_DISABLE | RadioState::TX_DISABLE => {
self.wait_for_radio_state(RadioState::DISABLED);
return;
}
RadioState::RX => {
r.tasks_ccastop().write_value(1);
r.tasks_stop().write_value(1);
self.wait_for_radio_state(RadioState::RX_IDLE);
}
RadioState::TX => {
r.tasks_stop().write_value(1);
self.wait_for_radio_state(RadioState::TX_IDLE);
}
_ => unreachable!(),
}
}
}
fn set_buffer(&mut self, buffer: &[u8]) {
let r = T::regs();
r.packetptr().write_value(buffer.as_ptr() as u32);
}
fn receive_prepare(&mut self) {
T::regs().events_ccabusy().write_value(0);
T::regs().events_phyend().write_value(0);
let disable = match self.state() {
RadioState::DISABLED => false,
RadioState::RX_IDLE => self.needs_enable,
_ => true,
};
if disable {
self.disable();
}
self.needs_enable = false;
}
fn receive_start(&mut self, packet: &mut Packet) {
let r = T::regs();
self.receive_prepare();
r.shorts().write(|w| w.set_rxready_start(true));
self.set_buffer(packet.buffer.as_mut());
dma_start_fence();
match self.state() {
RadioState::RX_IDLE => r.tasks_start().write_value(1),
_ => r.tasks_rxen().write_value(1),
}
}
fn receive_cancel() {
let r = T::regs();
r.shorts().write(|_| {});
r.tasks_stop().write_value(1);
loop {
match state(r) {
RadioState::DISABLED | RadioState::RX_IDLE => break,
_ => (),
}
}
dma_end_fence();
}
pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), Error> {
let s = T::state();
let r = T::regs();
self.receive_start(packet);
let dropper = OnDrop::new(|| Self::receive_cancel());
self.clear_all_interrupts();
core::future::poll_fn(|cx| {
s.event_waker.register(cx.waker());
if r.events_phyend().read() != 0 {
r.events_phyend().write_value(0);
trace!("RX done poll");
return Poll::Ready(());
} else {
r.intenset().write(|w| w.set_phyend(true));
};
Poll::Pending
})
.await;
dma_end_fence();
dropper.defuse();
let crc = r.rxcrc().read().rxcrc() as u16;
if r.crcstatus().read().crcstatus() == vals::Crcstatus::CRCOK {
Ok(())
} else {
Err(Error::CrcFailed(crc))
}
}
pub async fn try_send(&mut self, packet: &mut Packet) -> Result<(), Error> {
let s = T::state();
let r = T::regs();
self.receive_prepare();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TransmitResult {
Success,
ChannelInUse,
}
r.shorts().write(|w| {
w.set_rxready_ccastart(true);
w.set_ccaidle_txen(true);
w.set_txready_start(true);
w.set_ccabusy_disable(true);
w.set_phyend_disable(true);
});
self.set_buffer(packet.buffer.as_mut());
dma_start_fence();
match self.state() {
RadioState::RX_IDLE => r.tasks_ccastart().write_value(1),
_ => r.tasks_rxen().write_value(1),
}
self.clear_all_interrupts();
let result = core::future::poll_fn(|cx| {
s.event_waker.register(cx.waker());
if r.events_phyend().read() != 0 {
r.events_phyend().write_value(0);
r.events_ccabusy().write_value(0);
trace!("TX done poll");
return Poll::Ready(TransmitResult::Success);
} else if r.events_ccabusy().read() != 0 {
r.events_ccabusy().write_value(0);
trace!("TX no CCA");
return Poll::Ready(TransmitResult::ChannelInUse);
}
r.intenset().write(|w| {
w.set_phyend(true);
w.set_ccabusy(true);
});
Poll::Pending
})
.await;
match result {
TransmitResult::Success => Ok(()),
TransmitResult::ChannelInUse => Err(Error::ChannelInUse),
}
}
}
pub struct Packet {
buffer: [u8; Self::SIZE],
}
impl Packet {
const PHY_HDR: usize = 0;
const DATA: core::ops::RangeFrom<usize> = 1..;
pub const CAPACITY: u8 = 125;
const CRC: u8 = 2; const MAX_PSDU_LEN: u8 = Self::CAPACITY + Self::CRC;
const SIZE: usize = 1 + Self::MAX_PSDU_LEN as usize;
pub fn new() -> Self {
let mut packet = Self {
buffer: [0; Self::SIZE],
};
packet.set_len(0);
packet
}
pub fn copy_from_slice(&mut self, src: &[u8]) {
assert!(src.len() <= Self::CAPACITY as usize);
let len = src.len() as u8;
self.buffer[Self::DATA][..len as usize].copy_from_slice(&src[..len.into()]);
self.set_len(len);
}
pub fn len(&self) -> u8 {
self.buffer[Self::PHY_HDR] - Self::CRC
}
pub fn set_len(&mut self, len: u8) {
assert!(len <= Self::CAPACITY);
self.buffer[Self::PHY_HDR] = len + Self::CRC;
}
pub fn lqi(&self) -> u8 {
self.buffer[1 + self.len() as usize ]
}
}
impl core::ops::Deref for Packet {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.buffer[Self::DATA][..self.len() as usize]
}
}
impl core::ops::DerefMut for Packet {
fn deref_mut(&mut self) -> &mut [u8] {
let len = self.len();
&mut self.buffer[Self::DATA][..len as usize]
}
}
fn dma_start_fence() {
compiler_fence(Ordering::Release);
}
fn dma_end_fence() {
compiler_fence(Ordering::Acquire);
}