use core::{cell::RefCell, future::Future, ops::RangeInclusive, pin::Pin};
use portable_atomic::{AtomicU8, AtomicU16, Ordering};
use crate::{
async_driver::private::{AsyncTransmitExt, HasDmaList, NonExhaustive},
borrowed_buffer::BorrowedBuffer,
crypto::CipherParameters,
dma_list::DmaBufferSlab,
ll::{
ChannelAccessError, ControlFrameFilterConfig, HardwareTxQueue, HardwareTxQueueStatus,
INTERFACE_COUNT, KEY_SLOT_COUNT, KeySlotParameters, LowLevelDriver, MacProtocolError,
RxFilterBank, WiFiInterrupt,
},
rates::TxPhyRate,
};
use embassy_sync::blocking_mutex;
use esp_hal::{dma::DmaDescriptor, handler, peripherals::WIFI};
use macro_bits::{bit, check_bit};
use crate::{
DefaultRawMutex,
dma_list::DmaList,
sync::{HardwareTxResultSignal, SignalQueue},
};
#[handler]
fn mac_handler() {
let cause = unsafe { LowLevelDriver::get_and_clear_mac_interrupt_cause() };
if cause.is_empty() {
return;
}
if cause.rx() {
WIFI_RX_SIGNAL_QUEUE.put();
}
if cause.tx_success() {
let tx_queue_status = Ok(());
unsafe {
LowLevelDriver::process_tx_status(tx_queue_status, |queue| {
HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()].signal(tx_queue_status);
})
};
}
if cause.tx_timeout() {
let tx_queue_status = Err(ChannelAccessError::Timeout);
unsafe {
LowLevelDriver::process_tx_status(tx_queue_status, |queue| {
HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()].signal(tx_queue_status);
LowLevelDriver::set_tx_queue_status(queue, HardwareTxQueueStatus::Disabled);
})
};
}
if cause.tx_collision() {
let tx_queue_status = Err(ChannelAccessError::Collision);
unsafe {
LowLevelDriver::process_tx_status(tx_queue_status, |queue| {
HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()].signal(tx_queue_status);
LowLevelDriver::set_tx_queue_status(queue, HardwareTxQueueStatus::Disabled);
})
};
}
}
#[cfg(pwr_interrupt_present)]
#[handler]
fn pwr_handler() {
let _cause = unsafe { LowLevelDriver::get_and_clear_pwr_interrupt_cause() };
}
static WIFI_RX_SIGNAL_QUEUE: SignalQueue = SignalQueue::new();
static HARDWARE_TX_RESULT_SIGNALS: [HardwareTxResultSignal; 5] =
[const { HardwareTxResultSignal::new() }; 5];
static FRAMES_SINCE_LAST_TXPWR_CTRL: AtomicU8 = AtomicU8::new(0);
const RX_BUFFER_SIZE: usize = 1600;
pub struct WiFiResources<const BUFFER_COUNT: usize> {
buffer_slab: DmaBufferSlab<BUFFER_COUNT, RX_BUFFER_SIZE>,
dma_list: Option<blocking_mutex::Mutex<DefaultRawMutex, RefCell<DmaList>>>,
ll_driver: Option<LowLevelDriver>,
tx_dma_descriptors: Option<[DmaDescriptor; 5]>,
}
impl<const BUFFER_COUNT: usize> WiFiResources<BUFFER_COUNT> {
pub const fn new() -> Self {
assert!(BUFFER_COUNT >= 2, "BUFFER_COUNT has to be larger than 2.");
Self {
buffer_slab: DmaBufferSlab::new(),
dma_list: None,
ll_driver: None,
tx_dma_descriptors: None,
}
}
pub(crate) unsafe fn init(
&mut self,
ll_driver: LowLevelDriver,
) -> (
&blocking_mutex::Mutex<DefaultRawMutex, RefCell<DmaList>>,
&LowLevelDriver,
&mut [DmaDescriptor; 5],
) {
let (base_ptr, last_ptr) = unsafe { self.buffer_slab.init() };
let ll_driver = self.ll_driver.insert(ll_driver);
let dma_list = self
.dma_list
.insert(blocking_mutex::Mutex::new(RefCell::new(DmaList::new(
base_ptr,
last_ptr,
unsafe {
core::mem::transmute::<&LowLevelDriver, &'static LowLevelDriver>(ll_driver)
},
))));
let tx_dma_descriptors = self.tx_dma_descriptors.insert([DmaDescriptor::EMPTY; 5]);
(dma_list, ll_driver, tx_dma_descriptors)
}
}
impl<const BUFFER_COUNT: usize> Default for WiFiResources<BUFFER_COUNT> {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum ScanningMode {
#[default]
Disabled,
BeaconsOnly,
ManagementAndData,
}
pub trait HasLowLevelDriver {
unsafe fn ll_driver_ref(&self) -> &LowLevelDriver;
fn mac_time(&self) -> esp_hal::time::Instant {
unsafe { LowLevelDriver::mac_time() }
}
fn get_channel(&self) -> u8 {
CURRENT_CHANNEL.load(Ordering::Relaxed)
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum CryptoError {
OutOfBounds,
MulticastBitSet,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct InvalidChannelError;
pub trait ChannelControl: HasLowLevelDriver {
fn set_channel(&mut self, channel_number: u8) -> Result<(), InvalidChannelError> {
if !(1..=14).contains(&channel_number) {
return Err(InvalidChannelError);
}
trace!("Changing channel to {}.", channel_number);
unsafe { self.ll_driver_ref() }.set_channel(channel_number);
CURRENT_CHANNEL.store(channel_number, Ordering::Relaxed);
Ok(())
}
}
pub trait CryptoControl: HasLowLevelDriver {
fn key_slot_in_use(&self, key_slot: usize) -> Result<bool, OutOfBounds> {
WiFi::validate_key_slot(key_slot)
.map(|_| unsafe { self.ll_driver_ref() }.key_slot_enabled(key_slot))
}
fn set_key(
&mut self,
key_slot: usize,
interface: usize,
key_id: u8,
address: [u8; 6],
cipher_parameters: CipherParameters<'_>,
) -> Result<(), CryptoError> {
let key = cipher_parameters.key();
let ll_driver = unsafe { self.ll_driver_ref() };
WiFi::validate_key_slot(key_slot).map_err(|_| CryptoError::OutOfBounds)?;
WiFi::validate_interface(interface).map_err(|_| CryptoError::OutOfBounds)?;
if key_id >= 4 {
return Err(CryptoError::OutOfBounds);
}
if check_bit!(address[0], bit!(0)) {
return Err(CryptoError::MulticastBitSet);
}
ll_driver.set_key_slot_parameters(
key_slot,
&KeySlotParameters {
address,
key_id,
interface: interface as u8,
pairwise: cipher_parameters.is_pairwise(),
group: cipher_parameters.is_group(),
algorithm: cipher_parameters.algorithm(),
wep_104: cipher_parameters.is_wep_104(),
},
);
ll_driver.set_key(key_slot, key);
ll_driver.set_key_slot_enable(key_slot, true);
let (protect_management_frames, protect_signaling_and_payload) =
if let Some(aes_cipher_parameters) = cipher_parameters.aes_cipher_parameters() {
(
aes_cipher_parameters.mfp_enabled,
aes_cipher_parameters.spp_enabled,
)
} else {
(false, false)
};
ll_driver.set_interface_crypto_parameters(
interface,
protect_management_frames,
protect_signaling_and_payload,
cipher_parameters.is_aead(),
);
ll_driver.set_sms4_status(false);
Ok(())
}
fn delete_key(&mut self, key_slot: usize) -> Result<(), OutOfBounds> {
WiFi::validate_key_slot(key_slot).map(|_| {
let ll_driver = unsafe { self.ll_driver_ref() };
ll_driver.set_key_slot_enable(key_slot, false);
ll_driver.clear_key_slot(key_slot);
})
}
fn dump_key_slot(&self, key_slot: usize) -> Result<(), OutOfBounds> {
WiFi::validate_key_slot(key_slot)?;
let wifi = WIFI::regs();
let crypto_key_slot = wifi.crypto_key_slot(key_slot);
let mut key_bytes = [0x00u8; 32];
for (buffer_chunk, key_word) in key_bytes
.chunks_mut(4)
.zip(crypto_key_slot.key_value_iter())
{
buffer_chunk.copy_from_slice(key_word.read().bits().to_le_bytes().as_slice());
}
let mut address = [0x00u8; 6];
address[..4].copy_from_slice(
crypto_key_slot
.addr_low()
.read()
.bits()
.to_le_bytes()
.as_slice(),
);
address[4..].copy_from_slice(
crypto_key_slot
.addr_high()
.read()
.addr()
.bits()
.to_le_bytes()
.as_slice(),
);
let control = crypto_key_slot.addr_high().read().bits() >> 16;
cfg_select! {
feature = "defmt" => {
info!(
"Key Slot: {} Address: {=[u8]:02x} Key: {=[u8]:02x} Control Reg: {:04x}",
key_slot, address, key_bytes, control
);
}
_ => {
info!(
"Key Slot: {} Address: {:02x?} Key: {:02x?} Control Reg: {:04x}",
key_slot, address, key_bytes, control
);
}
}
Ok(())
}
fn dump_crypto_config(&self) {
let wifi = WIFI::regs();
for (i, interface_crypto_control) in wifi
.crypto_control()
.interface_crypto_control_iter()
.enumerate()
{
info!(
"Interface: {} Crypto Control Reg: {:08x}",
i,
interface_crypto_control.read().bits()
);
}
info!(
"General Crypto Control Reg: {:08x}",
wifi.crypto_control().general_crypto_control().read().bits()
);
let mut enabled_key_slots = [0x00u8; 32];
let enabled_slot_count = wifi
.crypto_control()
.crypto_key_slot_state()
.read()
.key_slot_enable_iter()
.enumerate()
.filter_map(|(i, enabled)| enabled.bit().then_some(i))
.zip(enabled_key_slots.iter_mut())
.map(|(key_slot, k)| *k = key_slot as u8)
.count();
info!(
"Enabled Key Slots: {:?}",
&enabled_key_slots[..enabled_slot_count]
);
}
}
pub struct CryptoController<'res> {
ll_driver: &'res LowLevelDriver,
}
impl<'res> HasLowLevelDriver for CryptoController<'res> {
unsafe fn ll_driver_ref(&self) -> &LowLevelDriver {
self.ll_driver
}
}
impl CryptoControl for CryptoController<'_> {}
pub struct RxInterfaceController<'res> {
ll_driver: &'res LowLevelDriver,
interface: usize,
}
impl RxInterfaceController<'_> {
pub fn set_filter_bssid_check(&self, enabled: bool) {
self.ll_driver
.set_bssid_check_enable(self.interface, enabled);
}
pub fn set_filter(&self, filter_bank: RxFilterBank, address: [u8; 6]) {
self.ll_driver
.set_filter_address(self.interface, filter_bank, &address);
self.ll_driver
.set_filter_mask(self.interface, filter_bank, &[0xff; 6]);
self.ll_driver
.set_filter_enable(self.interface, filter_bank, true);
}
pub fn override_filter_mask(&self, filter_bank: RxFilterBank, mask: [u8; 6]) {
self.ll_driver
.set_filter_mask(self.interface, filter_bank, &mask)
}
pub fn clear_filter(&self, filter_bank: RxFilterBank) {
self.ll_driver.clear_filter(self.interface, filter_bank);
}
pub fn set_scanning_mode(&self, scanning_mode: ScanningMode) {
self.ll_driver.set_scanning_mode_parameters(
self.interface,
scanning_mode == ScanningMode::BeaconsOnly,
scanning_mode == ScanningMode::ManagementAndData,
);
}
pub fn set_filtered_address_types(&self, unicast: bool, multicast: bool) {
self.ll_driver
.set_filtered_address_types(self.interface, unicast, multicast);
}
pub fn set_control_frame_filter(&self, config: &ControlFrameFilterConfig) {
self.ll_driver
.set_control_frame_filter(self.interface, config);
}
pub const fn interface(&self) -> usize {
self.interface
}
}
impl HasLowLevelDriver for RxInterfaceController<'_> {
unsafe fn ll_driver_ref(&self) -> &LowLevelDriver {
self.ll_driver
}
}
static CURRENT_CHANNEL: AtomicU8 = AtomicU8::new(1);
pub struct ChannelController<'res> {
ll_driver: &'res LowLevelDriver,
}
impl HasLowLevelDriver for ChannelController<'_> {
unsafe fn ll_driver_ref(&self) -> &LowLevelDriver {
self.ll_driver
}
}
impl ChannelControl for ChannelController<'_> {}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum TxErrorBehaviour<'a> {
RetryUntil(u8),
MultiRateRetry(&'a [TxPhyRate]),
#[default]
Drop,
}
impl<'a> TxErrorBehaviour<'a> {
#[inline]
pub fn tx_attempt_rate_iter<'b>(
&'b self,
initial_rate: TxPhyRate,
) -> impl Iterator<Item = TxPhyRate> + Send + Sync + use<'b>
where
'b: 'a,
{
let tx_attempts = match self {
Self::RetryUntil(retries) => *retries as usize + 1,
Self::MultiRateRetry(rates) => rates.len(),
Self::Drop => 1,
};
(0..tx_attempts).map(move |i| match self {
Self::RetryUntil(_) => initial_rate,
Self::MultiRateRetry(rates) => {
if i == 0 {
initial_rate
} else {
rates[i]
}
}
Self::Drop => initial_rate,
})
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TxPlcpParameters {
pub rate: TxPhyRate,
pub _ne: NonExhaustive,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct EdcaParameters {
pub contention_window_exponent_range: Option<RangeInclusive<u8>>,
pub aifsn: Option<u8>,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub enum RtsStrategy {
#[default]
DriverControlled,
Forced(bool),
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct TxMacParameters {
pub key_slot_index: Option<u8>,
pub wait_for_ack: bool,
pub rts_strategy: RtsStrategy,
pub override_seq_num: bool,
pub edca_parameters: EdcaParameters,
pub _ne: NonExhaustive,
}
pub struct TxQueueEndpoint<'res> {
ll_driver: &'res LowLevelDriver,
queue: HardwareTxQueue,
dma_descriptor: &'res mut DmaDescriptor,
}
impl TxQueueEndpoint<'_> {
pub fn transmit<'a>(
&'a mut self,
interface: usize,
plcp_parameters: &'a TxPlcpParameters,
mac_parameters: &'a TxMacParameters,
error_behaviour: TxErrorBehaviour<'a>,
mpdu_buf: &'a mut [u8],
) -> impl Future<Output = Result<u8, TxError>> + Send + 'a {
self.ll_driver.transmit_with_retry(
interface,
plcp_parameters,
mac_parameters,
error_behaviour,
self.queue,
Pin::new(self.dma_descriptor),
mpdu_buf,
)
}
pub fn transmit_oneshot<'a>(
&'a mut self,
interface: usize,
plcp_parameters: &'a TxPlcpParameters,
mac_parameters: &'a TxMacParameters,
mpdu_buf: &'a mut [u8],
) -> impl Future<Output = Result<(), TxError>> + Send + Sync + 'a {
self.ll_driver.transmit_oneshot(
interface,
plcp_parameters,
mac_parameters,
self.queue,
Pin::new(self.dma_descriptor),
mpdu_buf,
)
}
pub const fn hardware_tx_queue(&self) -> HardwareTxQueue {
self.queue
}
}
impl HasLowLevelDriver for TxQueueEndpoint<'_> {
unsafe fn ll_driver_ref(&self) -> &LowLevelDriver {
self.ll_driver
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum TxError {
ChannelAccess(ChannelAccessError),
MacProtocol(MacProtocolError),
BufferTooShort,
DisabledKeySlot,
OutOfBounds,
InvalidEdcaParameters,
}
static CURRENT_SEQUENCE_NUMBER: AtomicU16 = AtomicU16::new(0);
mod private {
use core::{cell::RefCell, future::Future, ops::DerefMut, pin::Pin, sync::atomic::Ordering};
use embassy_sync::blocking_mutex;
use esp_hal::dma::{DmaDescriptor, Owner};
use macro_bits::bit;
use crate::{
DefaultRawMutex,
async_driver::{
CURRENT_SEQUENCE_NUMBER, FRAMES_SINCE_LAST_TXPWR_CTRL, HARDWARE_TX_RESULT_SIGNALS,
HasLowLevelDriver, RtsStrategy, TxError, TxMacParameters, TxPlcpParameters, WiFi,
},
dma_list::DmaList,
edca::EdcaContentionState,
ll::{HardwareTxQueue, LowLevelDriver, MacProtocolError},
prelude::TxErrorBehaviour,
rates::TxPhyRate,
sync::DropGuard,
};
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NonExhaustive;
pub trait HasDmaList<'res> {
fn dma_list_ref(&self) -> &'res blocking_mutex::Mutex<DefaultRawMutex, RefCell<DmaList>>;
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct ExtractedParameters {
duration: u16,
is_unicast: bool,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct EdcaTxParameters {
backoff_slots: usize,
aifsn: usize,
}
pub trait AsyncTransmitExt: HasLowLevelDriver {
#[allow(clippy::too_many_arguments)]
fn setup_tx(
&self,
interface: usize,
rate: TxPhyRate,
mac_parameters: &TxMacParameters,
queue: HardwareTxQueue,
dma_descriptor: Pin<&DmaDescriptor>,
extracted_parameters: ExtractedParameters,
edca_tx_parameters: EdcaTxParameters,
) {
let ll_driver = unsafe { self.ll_driver_ref() };
let rts_enabled = if let RtsStrategy::Forced(rts_enabled) = mac_parameters.rts_strategy
{
rts_enabled
} else {
extracted_parameters.is_unicast
};
ll_driver.set_channel_access_parameters(
queue,
10,
edca_tx_parameters.backoff_slots,
edca_tx_parameters.aifsn,
);
ll_driver.set_plcp0(
queue,
dma_descriptor,
mac_parameters.wait_for_ack,
rts_enabled,
);
ll_driver.set_plcp1(
queue,
rate,
dma_descriptor.len(),
interface,
mac_parameters.key_slot_index,
);
ll_driver.set_plcp2(queue);
ll_driver.set_duration(queue, extracted_parameters.duration);
if let TxPhyRate::Ht(ht_rate) = rate {
ll_driver.set_ht_parameters(
queue,
ht_rate.mcs_index(),
ht_rate.short_gi(),
dma_descriptor.len(),
);
}
}
fn transmit_raw<'a>(
&'a self,
interface: usize,
rate: TxPhyRate,
mac_parameters: &'a TxMacParameters,
queue: HardwareTxQueue,
dma_descriptor: Pin<&DmaDescriptor>,
fallible_params: Result<(ExtractedParameters, EdcaTxParameters), TxError>,
) -> impl Future<Output = Result<(), TxError>> + Send + Sync + 'a {
let ll_driver = unsafe { self.ll_driver_ref() };
let hardware_tx_result_signal = &HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()];
let tx_done_wait_drop_guard = DropGuard::new(move || {
ll_driver.tx_done(queue);
hardware_tx_result_signal.reset();
});
if let Ok((extracted_parameters, edca_tx_parameters)) = fallible_params {
self.setup_tx(
interface,
rate,
mac_parameters,
queue,
dma_descriptor.as_ref(),
extracted_parameters,
edca_tx_parameters,
);
hardware_tx_result_signal.reset();
ll_driver.start_tx_queue(queue);
}
async move {
fallible_params?;
HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()]
.wait()
.await
.inspect(|_| {
if FRAMES_SINCE_LAST_TXPWR_CTRL.fetch_add(1, Ordering::Relaxed) == 4 {
ll_driver.run_power_control();
FRAMES_SINCE_LAST_TXPWR_CTRL.store(0, Ordering::Relaxed);
}
})
.map_err(TxError::ChannelAccess)?;
tx_done_wait_drop_guard.detonate();
ll_driver
.get_tx_mac_protocol_result(queue)
.map_err(TxError::MacProtocol)
}
}
fn prepare_frame_for_tx(
&self,
interface: usize,
mac_parameters: &TxMacParameters,
mpdu_buf: &mut [u8],
) -> Result<ExtractedParameters, TxError> {
let Some(duration) = mpdu_buf
.get(2..4)
.map(|bytes| u16::from_le_bytes(bytes.try_into().unwrap()))
else {
return Err(TxError::BufferTooShort);
};
let is_unicast = mpdu_buf[4] & 1 == 0;
if mac_parameters.override_seq_num {
let seq_num = CURRENT_SEQUENCE_NUMBER.fetch_add(1, Ordering::Relaxed);
if let Some(sequence_number) = mpdu_buf.get_mut(22..24) {
sequence_number.copy_from_slice((seq_num << 4).to_le_bytes().as_slice());
} else {
return Err(TxError::BufferTooShort);
}
}
if let Some(key_slot_index) = mac_parameters.key_slot_index {
WiFi::validate_key_slot(key_slot_index as usize)
.map_err(|_| TxError::OutOfBounds)?;
if !unsafe { self.ll_driver_ref() }.key_slot_enabled(key_slot_index as usize) {
return Err(TxError::DisabledKeySlot)?;
}
}
WiFi::validate_interface(interface).map_err(|_| TxError::OutOfBounds)?;
Ok(ExtractedParameters {
duration,
is_unicast,
})
}
fn prepare_dma_descriptor_for_tx(mpdu_buf: &mut [u8], dma_descriptor: &mut DmaDescriptor) {
let frame_length = mpdu_buf.len() + 4;
dma_descriptor.set_size(frame_length);
dma_descriptor.set_length(frame_length);
dma_descriptor.set_owner(Owner::Dma);
dma_descriptor.set_suc_eof(true);
dma_descriptor.buffer = mpdu_buf.as_mut_ptr();
}
fn transmit_oneshot<'a>(
&'a self,
interface: usize,
plcp_parameters: &'a TxPlcpParameters,
mac_parameters: &'a TxMacParameters,
queue: HardwareTxQueue,
mut dma_descriptor: Pin<&'a mut DmaDescriptor>,
mpdu_buf: &mut [u8],
) -> impl Future<Output = Result<(), TxError>> + Send + Sync + 'a {
let duration_and_is_unicast =
self.prepare_frame_for_tx(interface, mac_parameters, mpdu_buf);
Self::prepare_dma_descriptor_for_tx(mpdu_buf, dma_descriptor.deref_mut());
let aifsn = mac_parameters
.edca_parameters
.aifsn
.unwrap_or(queue.default_aifsn()) as usize;
let backoff_slots = if let HardwareTxQueue::Edcaf(edca_ac) = queue {
EdcaContentionState::new(
mac_parameters
.edca_parameters
.contention_window_exponent_range
.clone()
.unwrap_or(edca_ac.default_cw_exponent_range()),
)
.map(|contention_state| contention_state.random_backoff_slot_count())
} else {
Some(1)
};
let duration_and_backoff_slots = backoff_slots
.ok_or(TxError::InvalidEdcaParameters)
.and_then(|backoff_slots| {
duration_and_is_unicast.map(|extracted_parameters| {
(
extracted_parameters,
EdcaTxParameters {
aifsn,
backoff_slots,
},
)
})
});
self.transmit_raw(
interface,
plcp_parameters.rate,
mac_parameters,
queue,
dma_descriptor.into_ref(),
duration_and_backoff_slots,
)
}
#[allow(clippy::too_many_arguments)]
async fn transmit_with_retry<'a>(
&'a self,
interface: usize,
plcp_parameters: &'a TxPlcpParameters,
mac_parameters: &'a TxMacParameters,
error_behaviour: TxErrorBehaviour<'a>,
queue: HardwareTxQueue,
mut dma_descriptor: Pin<&'a mut DmaDescriptor>,
mpdu_buf: &mut [u8],
) -> Result<u8, TxError> {
let extracted_parameters =
self.prepare_frame_for_tx(interface, mac_parameters, mpdu_buf)?;
Self::prepare_dma_descriptor_for_tx(mpdu_buf, dma_descriptor.deref_mut());
let mut last_res = Ok::<u8, TxError>(0);
let mut edca_contention_state = if let HardwareTxQueue::Edcaf(edca_ac) = queue {
EdcaContentionState::new(
mac_parameters
.edca_parameters
.contention_window_exponent_range
.clone()
.unwrap_or(edca_ac.default_cw_exponent_range()),
)
} else {
None
};
let aifsn = mac_parameters
.edca_parameters
.aifsn
.unwrap_or(queue.default_aifsn()) as usize;
for (i, tx_attempt_rate) in error_behaviour
.tx_attempt_rate_iter(plcp_parameters.rate)
.enumerate()
{
let backoff_slots = edca_contention_state
.as_ref()
.map(EdcaContentionState::random_backoff_slot_count)
.unwrap_or(1);
last_res = self
.transmit_raw(
interface,
tx_attempt_rate,
mac_parameters,
queue,
dma_descriptor.as_ref(),
Ok((
extracted_parameters,
EdcaTxParameters {
aifsn,
backoff_slots,
},
)),
)
.await
.map(|_| i as u8);
match last_res {
Ok(_) => break,
Err(TxError::MacProtocol(MacProtocolError::AckTimeout)) => {
if let Some(ref mut contention_state) = edca_contention_state {
trace!("Incremented LRC");
contention_state.increment_lrc();
contention_state.reset_src();
}
}
Err(TxError::MacProtocol(_)) => {
if let Some(ref mut contention_state) = edca_contention_state {
trace!("Incremented SRC");
contention_state.increment_src();
}
}
_ => {}
}
}
if let Some(byte) = mpdu_buf.get_mut(1) {
*byte &= !bit!(3);
}
if last_res.is_err() {
trace!("Transmission of MPDU failed.");
}
last_res
}
}
impl HasLowLevelDriver for LowLevelDriver {
unsafe fn ll_driver_ref(&self) -> &LowLevelDriver {
self
}
}
impl AsyncTransmitExt for LowLevelDriver {}
}
pub trait AsyncReceive<'res>: HasDmaList<'res> {
fn receive(&mut self) -> impl Future<Output = BorrowedBuffer<'res>> {
async {
let dma_list_item = loop {
WIFI_RX_SIGNAL_QUEUE.next().await;
if let Some(current) = self
.dma_list_ref()
.lock(|dma_list| dma_list.borrow_mut().take_first())
&& current.len() >= BorrowedBuffer::RX_CONTROL_HEADER_LENGTH
{
trace!("Received packet. len: {}", current.len());
break current;
}
trace!("Received empty packet.");
};
BorrowedBuffer {
dma_list: self.dma_list_ref(),
dma_descriptor: dma_list_item,
}
}
}
fn clear_rx_queue(&mut self) {
self.dma_list_ref()
.lock(|rx_dma_list| rx_dma_list.borrow_mut().clear());
WIFI_RX_SIGNAL_QUEUE.reset();
}
fn set_rx_status(&mut self, enabled: bool) {
unsafe {
LowLevelDriver::set_rx_enable(enabled);
}
}
fn log_dma_list_stats(&self) {
self.dma_list_ref()
.lock(|dma_list| dma_list.borrow().log_stats())
}
}
pub struct AsyncRxEndpoint<'res> {
dma_list: &'res blocking_mutex::Mutex<DefaultRawMutex, RefCell<DmaList>>,
}
impl<'res> HasDmaList<'res> for AsyncRxEndpoint<'res> {
fn dma_list_ref(&self) -> &'res blocking_mutex::Mutex<DefaultRawMutex, RefCell<DmaList>> {
self.dma_list
}
}
impl<'res> AsyncReceive<'res> for AsyncRxEndpoint<'res> {}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct OutOfBounds;
pub struct SplitDriverComponents<'res> {
pub rx_interface_controllers: [RxInterfaceController<'res>; INTERFACE_COUNT],
pub rx_endpoint: AsyncRxEndpoint<'res>,
pub tx_queue_endpoints: [TxQueueEndpoint<'res>; 5],
pub channel_controller: ChannelController<'res>,
pub crypto_controller: CryptoController<'res>,
}
pub struct WiFi<'res> {
dma_list: &'res blocking_mutex::Mutex<DefaultRawMutex, RefCell<DmaList>>,
ll_driver: &'res LowLevelDriver,
tx_dma_descriptors: Option<&'res mut [DmaDescriptor; 5]>,
}
impl<'res> WiFi<'res> {
pub fn new<const BUFFER_COUNT: usize>(
wifi: WIFI<'res>,
wifi_resources: &'res mut WiFiResources<BUFFER_COUNT>,
) -> Self {
trace!("Initializing WiFi.");
let ll_driver = LowLevelDriver::new(wifi);
ll_driver.configure_interrupt(WiFiInterrupt::Mac, mac_handler);
#[cfg(pwr_interrupt_present)]
ll_driver.configure_interrupt(WiFiInterrupt::Pwr, pwr_handler);
let (dma_list, ll_driver, tx_dma_descriptors) = unsafe { wifi_resources.init(ll_driver) };
let mut temp = Self {
ll_driver,
dma_list,
tx_dma_descriptors: Some(tx_dma_descriptors),
};
temp.set_channel(1).unwrap();
temp
}
pub fn split(mut self) -> SplitDriverComponents<'res> {
let mut i = 0;
let tx_queue_endpoints =
self.tx_dma_descriptors
.take()
.unwrap()
.each_mut()
.map(|dma_descriptor| {
i += 1;
TxQueueEndpoint {
ll_driver: self.ll_driver,
queue: HardwareTxQueue::from_hardware_slot(i - 1).unwrap(),
dma_descriptor,
}
});
SplitDriverComponents {
rx_interface_controllers: core::array::from_fn(|i| RxInterfaceController {
ll_driver: self.ll_driver,
interface: i,
}),
channel_controller: ChannelController {
ll_driver: self.ll_driver,
},
rx_endpoint: AsyncRxEndpoint {
dma_list: self.dma_list,
},
tx_queue_endpoints,
crypto_controller: CryptoController {
ll_driver: self.ll_driver,
},
}
}
#[inline(never)]
pub fn transmit<'a>(
&'a mut self,
interface: usize,
plcp_parameters: &'a TxPlcpParameters,
mac_parameters: &'a TxMacParameters,
error_behaviour: TxErrorBehaviour<'a>,
queue: HardwareTxQueue,
mpdu_buf: &'a mut [u8],
) -> impl Future<Output = Result<u8, TxError>> + 'a {
self.ll_driver.transmit_with_retry(
interface,
plcp_parameters,
mac_parameters,
error_behaviour,
queue,
Pin::new(self.tx_dma_descriptors.as_mut().unwrap().each_mut()[queue.hardware_slot()]),
mpdu_buf,
)
}
pub fn transmit_oneshot<'a>(
&'a mut self,
interface: usize,
plcp_parameters: &'a TxPlcpParameters,
mac_parameters: &'a TxMacParameters,
queue: HardwareTxQueue,
mpdu_buf: &'a mut [u8],
) -> impl Future<Output = Result<(), TxError>> + Send + Sync + 'a {
self.ll_driver.transmit_oneshot(
interface,
plcp_parameters,
mac_parameters,
queue,
Pin::new(self.tx_dma_descriptors.as_mut().unwrap().each_mut()[queue.hardware_slot()]),
mpdu_buf,
)
}
pub const fn validate_interface(interface: usize) -> Result<(), OutOfBounds> {
if interface < INTERFACE_COUNT {
Ok(())
} else {
Err(OutOfBounds)
}
}
pub fn set_filter_bssid_check(
&mut self,
interface: usize,
enabled: bool,
) -> Result<(), OutOfBounds> {
Self::validate_interface(interface)
.inspect(|_| self.ll_driver.set_bssid_check_enable(interface, enabled))
}
pub fn set_filter(
&mut self,
interface: usize,
filter_bank: RxFilterBank,
address: [u8; 6],
) -> Result<(), OutOfBounds> {
Self::validate_interface(interface).inspect(|_| {
self.ll_driver
.set_filter_address(interface, filter_bank, &address);
self.ll_driver
.set_filter_mask(interface, filter_bank, &[0xff; 6]);
self.ll_driver
.set_filter_enable(interface, filter_bank, true);
})
}
pub fn override_filter_mask(
&mut self,
interface: usize,
filter_bank: RxFilterBank,
mask: [u8; 6],
) -> Result<(), OutOfBounds> {
Self::validate_interface(interface).inspect(|_| {
self.ll_driver
.set_filter_mask(interface, filter_bank, &mask)
})
}
pub fn clear_filter(
&mut self,
interface: usize,
filter_bank: RxFilterBank,
) -> Result<(), OutOfBounds> {
Self::validate_interface(interface)
.inspect(|_| self.ll_driver.clear_filter(interface, filter_bank))
}
pub fn set_scanning_mode(
&mut self,
interface: usize,
scanning_mode: ScanningMode,
) -> Result<(), OutOfBounds> {
Self::validate_interface(interface).inspect(|_| {
self.ll_driver.set_scanning_mode_parameters(
interface,
scanning_mode == ScanningMode::BeaconsOnly,
scanning_mode == ScanningMode::ManagementAndData,
);
})
}
pub fn set_filtered_address_types(
&self,
interface: usize,
unicast: bool,
multicast: bool,
) -> Result<(), OutOfBounds> {
Self::validate_interface(interface).inspect(|_| {
self.ll_driver
.set_filtered_address_types(interface, unicast, multicast);
})
}
pub fn set_control_frame_filter(
&self,
interface: usize,
config: &ControlFrameFilterConfig,
) -> Result<(), OutOfBounds> {
Self::validate_interface(interface).inspect(|_| {
self.ll_driver.set_control_frame_filter(interface, config);
})
}
pub const fn validate_key_slot(key_slot: usize) -> Result<(), OutOfBounds> {
if key_slot < KEY_SLOT_COUNT {
Ok(())
} else {
Err(OutOfBounds)
}
}
}
impl HasLowLevelDriver for WiFi<'_> {
unsafe fn ll_driver_ref(&self) -> &LowLevelDriver {
self.ll_driver
}
}
impl ChannelControl for WiFi<'_> {}
impl CryptoControl for WiFi<'_> {}
impl<'res> HasDmaList<'res> for WiFi<'res> {
fn dma_list_ref(&self) -> &'res blocking_mutex::Mutex<DefaultRawMutex, RefCell<DmaList>> {
self.dma_list
}
}
impl AsyncTransmitExt for WiFi<'_> {}
impl<'res> AsyncReceive<'res> for WiFi<'res> {}
impl Drop for WiFi<'_> {
fn drop(&mut self) {
}
}