use core::{
iter::IntoIterator,
ops::RangeInclusive,
pin::Pin,
ptr::{NonNull, with_exposed_provenance_mut},
};
use esp_hal::{
dma::DmaDescriptor,
interrupt::InterruptHandler,
peripherals::{LPWR, WIFI as HalWIFI},
};
cfg_select! {
feature = "esp32" => {
use esp_hal::peripherals::DPORT as SYSCON;
}
feature = "esp32s2" => {
use esp_hal::peripherals::SYSCON;
}
}
use esp_phy::{PhyInitGuard, enable_phy};
use macro_bits::{bit, check_bit};
use crate::{
esp_pac::{Interrupt as PacInterrupt, WIFI, wifi::crypto_key_slot::KEY_VALUE},
ffi::{disable_wifi_agc, enable_wifi_agc, hal_init, tx_pwctrl_background},
rates::TxPhyRate,
};
fn run_reversible_function_sequence<T, Iter>(functions: Iter, parameter: T, forward: bool)
where
T: Copy,
Iter: IntoIterator<Item = fn(T)>,
<Iter as IntoIterator>::IntoIter: DoubleEndedIterator,
{
let for_each_closure = |function: fn(T)| (function)(parameter);
let function_iter = functions.into_iter();
if forward {
function_iter.for_each(for_each_closure);
} else {
function_iter.rev().for_each(for_each_closure);
}
}
#[inline(always)]
fn split_address(address: &[u8; 6]) -> (u32, u16) {
let (low, high) = address.split_at(4);
(
u32::from_ne_bytes(low.try_into().unwrap()),
u16::from_ne_bytes(high.try_into().unwrap()),
)
}
pub struct KeySlotParameters {
pub address: [u8; 6],
pub key_id: u8,
pub interface: u8,
pub pairwise: bool,
pub group: bool,
pub algorithm: u8,
pub wep_104: bool,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RxFilterBank {
Bssid,
ReceiverAddress,
}
impl RxFilterBank {
pub(crate) fn into_bits(self) -> usize {
match self {
Self::Bssid => 0,
Self::ReceiverAddress => 1,
}
}
}
macro_rules! cause_bitmask {
([$($(@chip($chips:tt))? $mask:expr),*]) => {
const {
let mut temp = 0;
$(
#[allow(unused)]
let mut condition = true;
$(
condition = cfg!(any($chips));
)?
temp |= if condition { $mask } else { 0 };
)*
temp
}
};
($mask:expr) => {
$mask
};
}
macro_rules! interrupt_cause_struct {
($(#[$struct_meta:meta])* $struct_name:ident => {
$(
$(#[$function_meta:meta])*
$function_name:ident => $mask:tt
),*
}) => {
$(#[$struct_meta])*
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct $struct_name(u32);
impl $struct_name {
#[inline]
pub const fn raw_cause(&self) -> u32 {
self.0
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0 == 0
}
$(
#[inline]
$(#[$function_meta])*
pub fn $function_name(&self) -> bool {
(self.0 & cause_bitmask!($mask)) != 0
}
)*
}
};
}
interrupt_cause_struct! {
MacInterruptCause => {
rx => [0x100020, @chip(esp32) 0x4],
tx_success => 0x80,
tx_timeout => 0x80000,
tx_collision => 0x100
}
}
#[cfg(pwr_interrupt_present)]
interrupt_cause_struct! {
PwrInterruptCause => {
tbtt => [@chip(esp32s2) 0x1e],
tsf_timer => [@chip(esp32s2) 0x1e0]
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum WiFiInterrupt {
Mac,
#[cfg(pwr_interrupt_present)]
Pwr,
}
impl From<WiFiInterrupt> for PacInterrupt {
fn from(value: WiFiInterrupt) -> Self {
match value {
WiFiInterrupt::Mac => PacInterrupt::WIFI_MAC,
#[cfg(pwr_interrupt_present)]
WiFiInterrupt::Pwr => PacInterrupt::WIFI_PWR,
}
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ChannelAccessError {
Timeout,
Collision,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MacProtocolError {
AckTimeout,
CtsTimeout,
RtsChannelAccessError(ChannelAccessError),
InvalidKeyId,
Unknown {
error: u8,
sub_error: u8,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum HardwareTxQueueStatus {
Disabled,
Valid,
Ready,
}
impl HardwareTxQueueStatus {
fn valid(&self) -> bool {
*self != Self::Disabled
}
fn enabled(&self) -> bool {
*self == Self::Ready
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ControlFrameFilterConfig {
pub control_wrapper: bool,
pub block_ack_request: bool,
pub block_ack: bool,
pub ps_poll: bool,
pub rts: bool,
pub cts: bool,
pub ack: bool,
pub cf_end: bool,
pub cf_end_cf_ack: bool,
}
impl ControlFrameFilterConfig {
pub fn none() -> Self {
Self::default()
}
pub fn all() -> Self {
Self {
control_wrapper: true,
block_ack_request: true,
block_ack: true,
ps_poll: true,
rts: true,
cts: true,
ack: true,
cf_end: true,
cf_end_cf_ack: true,
}
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum EdcaAccessCategory {
Background = 1,
#[default]
BestEffort = 2,
Video = 3,
Voice = 4,
}
impl EdcaAccessCategory {
pub const DEFAULT_MANAGEMENT_QUEUE: HardwareTxQueue =
HardwareTxQueue::Edcaf(EdcaAccessCategory::Voice);
#[inline]
pub const fn default_cw_exponent_range(&self) -> RangeInclusive<u8> {
match self {
EdcaAccessCategory::Background => 4..=10,
EdcaAccessCategory::BestEffort => 4..=10,
EdcaAccessCategory::Video => 3..=10,
EdcaAccessCategory::Voice => 2..=10,
}
}
#[inline]
pub const fn access_category_index(&self) -> usize {
match *self {
Self::Background => 1,
Self::BestEffort => 0,
Self::Video => 2,
Self::Voice => 3,
}
}
#[inline]
pub const fn from_access_category_index(access_category_index: usize) -> Option<Self> {
Some(match access_category_index {
1 => Self::Background,
0 => Self::BestEffort,
2 => Self::Video,
3 => Self::Voice,
_ => return None,
})
}
#[inline]
pub const fn hardware_slot(&self) -> usize {
*self as usize
}
#[inline]
pub const fn from_hardware_slot(slot: usize) -> Option<Self> {
Some(match slot {
1 => Self::Background,
2 => Self::BestEffort,
3 => Self::Video,
4 => Self::Voice,
_ => return None,
})
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum HardwareTxQueue {
#[default]
Beacon,
Edcaf(EdcaAccessCategory),
}
impl HardwareTxQueue {
#[inline]
pub const fn default_aifsn(&self) -> u8 {
if let Self::Edcaf(edca_ac) = self {
match *edca_ac {
EdcaAccessCategory::Background => 7,
EdcaAccessCategory::BestEffort => 3,
EdcaAccessCategory::Video => 2,
EdcaAccessCategory::Voice => 2,
}
} else {
1
}
}
#[inline]
pub const fn hardware_slot(&self) -> usize {
match *self {
Self::Beacon => 0,
Self::Edcaf(access_category) => access_category.hardware_slot(),
}
}
#[inline]
pub const fn from_hardware_slot(slot: usize) -> Option<Self> {
if slot == 0 {
Some(Self::Beacon)
} else if let Some(edca_ac) = EdcaAccessCategory::from_hardware_slot(slot) {
Some(Self::Edcaf(edca_ac))
} else {
None
}
}
#[inline]
pub const fn is_edca(&self) -> bool {
matches!(self, Self::Edcaf(_))
}
}
impl From<EdcaAccessCategory> for HardwareTxQueue {
fn from(value: EdcaAccessCategory) -> Self {
Self::Edcaf(value)
}
}
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
pub const INTERFACE_COUNT: usize = 4;
pub const KEY_SLOT_COUNT: usize = 25;
#[cfg(feature = "esp32")]
static MAC_TIME_OFFSET: portable_atomic::AtomicU64 = portable_atomic::AtomicU64::new(0);
pub struct LowLevelDriver {
_phy_init_guard: Option<PhyInitGuard<'static>>,
}
impl LowLevelDriver {
pub unsafe fn regs() -> WIFI {
Self::regs_internal()
}
fn regs_internal() -> WIFI {
unsafe { WIFI::steal() }
}
pub fn new(_wifi: esp_hal::peripherals::WIFI<'_>) -> Self {
unsafe {
Self {
_phy_init_guard: None,
}
.init()
}
}
unsafe fn set_module_status(&self, enable_module: bool) {
run_reversible_function_sequence(
[
|power_down| {
LPWR::regs()
.dig_pwc()
.modify(|_, w| w.wifi_force_pd().bit(power_down));
},
|isolate| {
LPWR::regs()
.dig_iso()
.modify(|_, w| w.wifi_force_iso().bit(isolate));
},
],
!enable_module,
enable_module,
);
}
unsafe fn reset_mac(&self) {
cfg_select! {
any(feature = "esp32", feature = "esp32s2") => {
let syscon = SYSCON::regs();
syscon.wifi_rst_en().modify(|_, w| w.mac_rst().set_bit());
syscon.wifi_rst_en().modify(|_, w| w.mac_rst().clear_bit());
}
_ => compile_error!("Adjust this for a new chip.")
}
unsafe {
Self::set_rx_enable(false);
}
}
unsafe fn set_mac_state(&self, intialized: bool) {
cfg_select! {
feature = "esp32" => {
const MAC_INIT_MASK: u32 = 0xffffe800;
const MAC_READY_MASK: u32 = 0x2000;
}
feature = "esp32s2" => {
const MAC_INIT_MASK: u32 = 0xff00efff;
const MAC_READY_MASK: u32 = 0x6000;
}
_ => {
compile_error!("The MAC init mask may have to be updated for different chips.");
}
}
while !intialized
&& cfg!(feature = "esp32s2")
&& Self::regs_internal().ctrl().read().bits() & MAC_READY_MASK != 0
{}
Self::regs_internal().ctrl().modify(|r, w| unsafe {
w.bits(if intialized {
r.bits() & MAC_INIT_MASK
} else {
r.bits() | !MAC_INIT_MASK
})
});
while !intialized && Self::regs_internal().ctrl().read().bits() & MAC_READY_MASK != 0 {}
}
#[inline]
unsafe fn setup_mac(&self) {
unsafe {
hal_init();
}
self.setup_filtering();
self.setup_crypto();
}
unsafe fn init(mut self) -> Self {
unsafe {
self.set_module_status(true);
}
#[cfg(esp32)]
esp_phy::set_mac_time_update_cb(|duration| {
let _ = MAC_TIME_OFFSET
.fetch_add(duration.as_micros(), core::sync::atomic::Ordering::Relaxed);
});
let enable_mask = cfg_select! {
feature = "esp32" => 0x00000406,
feature = "esp32s2" => 0x000007cf,
_ => compile_error!("If you're adding a new chip, you have to adjust the modem clock enable mask.")
};
SYSCON::regs()
.wifi_clk_en()
.modify(|r, w| unsafe { w.bits(r.bits() | enable_mask) });
self._phy_init_guard = Some(enable_phy());
unsafe {
self.reset_mac();
self.set_mac_state(true);
self.setup_mac();
}
self
}
#[inline(always)]
pub unsafe fn get_and_clear_mac_interrupt_cause() -> MacInterruptCause {
let cause = Self::regs_internal()
.mac_interrupt()
.wifi_int_status()
.read()
.bits();
Self::regs_internal()
.mac_interrupt()
.wifi_int_clear()
.write(|w| unsafe { w.bits(cause) });
MacInterruptCause(cause)
}
#[cfg(pwr_interrupt_present)]
#[inline(always)]
pub unsafe fn get_and_clear_pwr_interrupt_cause() -> PwrInterruptCause {
let cause = Self::regs_internal()
.pwr_interrupt()
.pwr_int_status()
.read()
.bits();
Self::regs_internal()
.pwr_interrupt()
.pwr_int_clear()
.write(|w| unsafe { w.bits(cause) });
PwrInterruptCause(cause)
}
pub fn configure_interrupt(
&self,
interrupt: WiFiInterrupt,
interrupt_handler: InterruptHandler,
) {
let wifi = unsafe { HalWIFI::steal() };
match interrupt {
WiFiInterrupt::Mac => {
wifi.bind_mac_interrupt(interrupt_handler);
wifi.enable_mac_interrupt(interrupt_handler.priority());
}
#[cfg(pwr_interrupt_present)]
WiFiInterrupt::Pwr => {
wifi.bind_pwr_interrupt(interrupt_handler);
wifi.enable_pwr_interrupt(interrupt_handler.priority());
}
}
}
#[inline]
pub(crate) unsafe fn set_rx_enable(enable_rx: bool) {
Self::regs_internal()
.rx_ctrl()
.modify(|_, w| w.rx_enable().bit(enable_rx));
}
#[inline]
pub fn rx_enabled(&self) -> bool {
Self::regs_internal().rx_ctrl().read().rx_enable().bit()
}
pub fn reload_hw_rx_descriptors(&self) {
let wifi = Self::regs_internal();
let reg = wifi.rx_ctrl();
reg.modify(|_, w| w.rx_descr_reload().set_bit());
while reg.read().rx_descr_reload().bit() {}
}
pub fn set_base_rx_descriptor(&self, base_descriptor: NonNull<DmaDescriptor>) {
Self::regs_internal()
.rx_dma_list()
.rx_descr_base()
.write(|w| unsafe { w.bits(base_descriptor.expose_provenance().get() as u32) });
self.reload_hw_rx_descriptors();
}
pub fn clear_base_rx_descriptor(&self) {
Self::regs_internal().rx_dma_list().rx_descr_base().reset();
self.reload_hw_rx_descriptors();
}
pub fn start_rx(&self, base_descriptor: NonNull<DmaDescriptor>) {
self.set_base_rx_descriptor(base_descriptor);
unsafe {
Self::set_rx_enable(true);
}
}
pub fn stop_rx(&self) {
unsafe {
Self::set_rx_enable(false);
}
self.clear_base_rx_descriptor();
}
pub fn base_rx_descriptor(&self) -> Option<NonNull<DmaDescriptor>> {
NonNull::new(with_exposed_provenance_mut(
Self::regs_internal()
.rx_dma_list()
.rx_descr_base()
.read()
.bits() as usize,
))
}
pub fn next_rx_descriptor(&self) -> Option<NonNull<DmaDescriptor>> {
NonNull::new(with_exposed_provenance_mut(
Self::regs_internal()
.rx_dma_list()
.rx_descr_next()
.read()
.bits() as usize,
))
}
pub fn last_rx_descriptor(&self) -> Option<NonNull<DmaDescriptor>> {
NonNull::new(with_exposed_provenance_mut(
Self::regs_internal()
.rx_dma_list()
.rx_descr_last()
.read()
.bits() as usize,
))
}
pub fn set_filter_enable(&self, interface: usize, filter_bank: RxFilterBank, enabled: bool) {
Self::regs_internal()
.filter_bank(filter_bank.into_bits())
.mask_high(interface)
.modify(|_, w| w.enabled().bit(enabled));
}
pub fn filter_enabled(&self, interface: usize, filter_bank: RxFilterBank) -> bool {
Self::regs_internal()
.filter_bank(filter_bank.into_bits())
.mask_high(interface)
.read()
.enabled()
.bit()
}
pub fn set_filter_address(
&self,
interface: usize,
filter_bank: RxFilterBank,
address: &[u8; 6],
) {
let wifi = Self::regs_internal();
let filter_bank = wifi.filter_bank(filter_bank.into_bits());
let (address_low, address_high) = split_address(address);
filter_bank
.addr_low(interface)
.write(|w| unsafe { w.bits(address_low) });
filter_bank
.addr_high(interface)
.write(|w| unsafe { w.addr().bits(address_high) });
}
pub fn set_filter_mask(&self, interface: usize, filter_bank: RxFilterBank, mask: &[u8; 6]) {
let wifi = Self::regs_internal();
let filter_bank = wifi.filter_bank(filter_bank.into_bits());
let (mask_low, mask_high) = split_address(mask);
filter_bank
.mask_low(interface)
.write(|w| unsafe { w.bits(mask_low) });
filter_bank
.mask_high(interface)
.modify(|_, w| unsafe { w.mask().bits(mask_high) });
}
pub fn clear_filter(&self, interface: usize, filter_bank: RxFilterBank) {
let wifi = Self::regs_internal();
let filter_bank = wifi.filter_bank(filter_bank.into_bits());
filter_bank.addr_low(interface).reset();
filter_bank.addr_high(interface).reset();
filter_bank.mask_low(interface).reset();
filter_bank.mask_high(interface).reset();
}
pub fn reset_interface_filters(&self, interface: usize) {
self.clear_filter(interface, RxFilterBank::ReceiverAddress);
self.clear_filter(interface, RxFilterBank::Bssid);
self.set_bssid_check_enable(interface, true);
self.set_filtered_address_types(interface, true, true);
}
pub fn set_filtered_address_types(&self, interface: usize, unicast: bool, multicast: bool) {
Self::regs_internal()
.filter_control(interface)
.modify(|_, w| {
w.block_unicast()
.bit(unicast)
.block_multicast()
.bit(multicast)
});
}
pub fn set_bssid_check_enable(&self, interface: usize, enabled: bool) {
Self::regs_internal()
.filter_control(interface)
.modify(|_, w| w.bssid_check().bit(enabled));
}
pub fn set_control_frame_filter(&self, interface: usize, config: &ControlFrameFilterConfig) {
Self::regs_internal()
.rx_ctrl_filter(interface)
.modify(|_, w| {
w.control_wrapper()
.bit(config.control_wrapper)
.block_ack_request()
.bit(config.block_ack_request)
.block_ack()
.bit(config.block_ack)
.ps_poll()
.bit(config.ps_poll)
.rts()
.bit(config.rts)
.cts()
.bit(config.cts)
.ack()
.bit(config.ack)
.cf_end()
.bit(config.cf_end)
.cf_end_cf_ack()
.bit(config.cf_end_cf_ack)
});
}
pub fn set_scanning_mode_parameters(
&self,
interface: usize,
beacons: bool,
other_frames: bool,
) {
Self::regs_internal()
.filter_control(interface)
.modify(|_, w| {
w.scan_mode()
.bit(beacons)
.data_and_mgmt_mode()
.bit(other_frames)
});
}
#[inline]
fn setup_filtering(&self) {
(0..4).for_each(|interface| {
self.set_filtered_address_types(interface, true, true);
self.set_control_frame_filter(interface, &ControlFrameFilterConfig::none());
self.set_bssid_check_enable(interface, true);
});
}
unsafe fn raw_tx_status(tx_result: Result<(), ChannelAccessError>) -> u8 {
let wifi = LowLevelDriver::regs_internal();
(match tx_result {
Ok(()) => wifi.txq_state().tx_complete_status().read().bits(),
Err(ChannelAccessError::Timeout) => {
wifi.txq_state().tx_error_status().read().bits() >> 0x10
}
Err(ChannelAccessError::Collision) => wifi.txq_state().tx_error_status().read().bits(),
}) as u8
}
unsafe fn clear_tx_slot_bit(tx_result: Result<(), ChannelAccessError>, slot: usize) {
let wifi = LowLevelDriver::regs_internal();
match tx_result {
Ok(()) => wifi
.txq_state()
.tx_complete_clear()
.modify(|_, w| w.slot(slot as u8).set_bit()),
Err(ChannelAccessError::Timeout) => wifi
.txq_state()
.tx_error_clear()
.modify(|_, w| w.slot_timeout(slot as u8).set_bit()),
Err(ChannelAccessError::Collision) => wifi
.txq_state()
.tx_error_clear()
.modify(|_, w| w.slot_collision(slot as u8).set_bit()),
};
}
#[inline]
pub unsafe fn process_tx_status(
tx_result: Result<(), ChannelAccessError>,
f: impl Fn(HardwareTxQueue),
) {
let raw_status = unsafe { Self::raw_tx_status(tx_result) };
(0..5)
.filter(|i| check_bit!(raw_status, bit!(i)))
.for_each(|queue| {
(f)(HardwareTxQueue::from_hardware_slot(4 - queue).unwrap());
unsafe { Self::clear_tx_slot_bit(tx_result, queue) };
});
}
#[inline]
pub unsafe fn set_tx_queue_status(queue: HardwareTxQueue, queue_status: HardwareTxQueueStatus) {
Self::regs_internal()
.tx_slot_config(queue.hardware_slot())
.plcp0()
.modify(|_, w| {
w.slot_valid()
.bit(queue_status.valid())
.slot_enabled()
.bit(queue_status.enabled())
});
}
#[inline]
pub fn start_tx_queue(&self, queue: HardwareTxQueue) {
unsafe {
Self::set_tx_queue_status(queue, HardwareTxQueueStatus::Ready);
}
}
#[inline]
pub fn tx_done(&self, queue: HardwareTxQueue) {
unsafe {
Self::set_tx_queue_status(queue, HardwareTxQueueStatus::Disabled);
}
Self::regs_internal()
.tx_slot_config(queue.hardware_slot())
.plcp0()
.reset();
}
#[inline]
pub fn get_tx_mac_protocol_result(
&self,
queue: HardwareTxQueue,
) -> Result<(), MacProtocolError> {
let pmd = Self::regs_internal()
.pmd(queue.hardware_slot())
.read()
.bits();
let error = ((pmd >> 0xc) & 0xf) as u8;
let sub_error = (pmd & 0xff) as u8;
match error {
0 => Ok(()),
1 => {
if sub_error == 1 || sub_error > 2 {
Err(MacProtocolError::RtsChannelAccessError(
ChannelAccessError::Timeout,
))
} else {
Err(MacProtocolError::Unknown { error, sub_error })
}
}
2 => Err(MacProtocolError::CtsTimeout),
4 => {
if sub_error == 0xc0 {
Err(MacProtocolError::InvalidKeyId)
} else if sub_error == 0 {
Err(MacProtocolError::CtsTimeout)
} else if sub_error < 6 && sub_error != 1 {
Err(MacProtocolError::AckTimeout)
} else {
Err(MacProtocolError::Unknown { error, sub_error })
}
}
5 => Err(MacProtocolError::AckTimeout),
_ => Err(MacProtocolError::Unknown { error, sub_error }),
}
}
#[inline]
pub fn set_channel_access_parameters(
&self,
queue: HardwareTxQueue,
timeout: usize,
backoff_slots: usize,
aifsn: usize,
) {
trace!("AIFSN: {} Backoff Slots: {}", aifsn, backoff_slots);
Self::regs_internal()
.tx_slot_config(queue.hardware_slot())
.config()
.modify(|_, w| unsafe {
w.timeout()
.bits(timeout as _)
.backoff_time()
.bits(backoff_slots as _)
.aifsn()
.bits(aifsn as _)
});
}
#[inline]
pub fn set_plcp0(
&self,
queue: HardwareTxQueue,
dma_list_item: Pin<&DmaDescriptor>,
wait_for_ack: bool,
enable_rts: bool,
) {
Self::regs_internal()
.tx_slot_config(queue.hardware_slot())
.plcp0()
.modify(|_, w| unsafe {
w.dma_addr()
.bits(
(dma_list_item.get_ref() as *const DmaDescriptor).expose_provenance()
as u32,
)
.wait_for_ack()
.bit(wait_for_ack)
});
if enable_rts {
Self::regs_internal()
.tx_slot_config(queue.hardware_slot())
.plcp0()
.modify(|r, w| unsafe { w.bits(r.bits() | 0x0800_0000) });
}
}
#[inline]
pub fn set_plcp1(
&self,
queue: HardwareTxQueue,
rate: TxPhyRate,
frame_length: usize,
interface: usize,
key_slot: Option<u8>,
) {
Self::regs_internal()
.plcp1(queue.hardware_slot())
.write(|w| unsafe {
w.len()
.bits(frame_length as _)
.rate()
.bits(rate.as_hardware_rate())
.is_80211_n()
.bit(matches!(rate, TxPhyRate::Ht(_)))
.interface_id()
.bits(interface as _)
.key_slot_id()
.bits(key_slot.unwrap_or_default() as _)
.bandwidth()
.set_bit()
});
}
#[inline]
pub fn set_plcp2(&self, queue: HardwareTxQueue) {
Self::regs_internal()
.plcp2(queue.hardware_slot())
.write(|w| w.unknown().set_bit());
}
#[inline]
pub fn set_duration(&self, queue: HardwareTxQueue, duration: u16) {
let duration = duration as u32;
Self::regs_internal()
.duration(queue.hardware_slot())
.write(|w| unsafe { w.bits(duration | (duration << 16)) });
}
#[inline]
pub fn set_ht_parameters(
&self,
queue: HardwareTxQueue,
mcs: u8,
is_short_gi: bool,
frame_length: usize,
) {
Self::regs_internal()
.ht_sig(queue.hardware_slot())
.write(|w| unsafe {
w.bits(
(mcs as u32 & 0b111)
| ((frame_length as u32 & 0xffff) << 8)
| (0b111 << 24)
| ((is_short_gi as u32) << 31),
)
});
Self::regs_internal()
.ht_unknown(queue.hardware_slot())
.write(|w| unsafe { w.bits(frame_length as u32 | 0x50000) });
}
#[inline]
fn setup_crypto(&self) {
(0..INTERFACE_COUNT).for_each(|interface| {
Self::regs_internal()
.crypto_control()
.interface_crypto_control(interface)
.write(|w| unsafe { w.bits(0x0003_0000) });
});
Self::regs_internal()
.crypto_control()
.general_crypto_control()
.reset();
}
pub fn set_key_slot_enable(&self, key_slot: usize, enabled: bool) {
Self::regs_internal()
.crypto_control()
.crypto_key_slot_state()
.modify(|_, w| w.key_slot_enable(key_slot as u8).bit(enabled));
}
pub fn key_slot_enabled(&self, key_slot: usize) -> bool {
Self::regs_internal()
.crypto_control()
.crypto_key_slot_state()
.read()
.key_slot_enable(key_slot as u8)
.bit()
}
pub fn set_key(&self, key_slot: usize, key: &[u8]) {
assert!(key.len() <= 32, "Keys can't be longer than 32 bytes.");
let wifi = Self::regs_internal();
let key_slot = wifi.crypto_key_slot(key_slot);
let (full_key_words, remaining_key_bytes) = key.as_chunks::<4>();
for (word, key_value) in full_key_words
.iter()
.copied()
.zip(key_slot.key_value_iter())
{
key_value.write(|w| unsafe { w.bits(u32::from_ne_bytes(word)) });
}
if !remaining_key_bytes.is_empty() {
assert!(remaining_key_bytes.len() < 4);
let mut temp = [0u8; 4];
temp[..remaining_key_bytes.len()].copy_from_slice(remaining_key_bytes);
key_slot
.key_value(full_key_words.len())
.write(|w| unsafe { w.bits(u32::from_ne_bytes(temp)) });
}
}
pub fn set_key_slot_parameters(
&self,
key_slot: usize,
key_slot_parameters: &KeySlotParameters,
) {
let wifi = Self::regs_internal();
let key_slot = wifi.crypto_key_slot(key_slot);
let (address_low, address_high) = split_address(&key_slot_parameters.address);
key_slot
.addr_low()
.write(|w| unsafe { w.bits(address_low) });
key_slot.addr_high().modify(|_, w| unsafe {
w.key_id()
.bits(key_slot_parameters.key_id)
.pairwise_key()
.bit(key_slot_parameters.pairwise)
.group_key()
.bit(key_slot_parameters.group)
.wep_104()
.bit(key_slot_parameters.wep_104)
.algorithm()
.bits(key_slot_parameters.algorithm)
.interface_id()
.bits(key_slot_parameters.interface)
.unknown()
.set_bit()
.addr()
.bits(address_high)
});
}
pub fn clear_key_slot(&self, key_slot: usize) {
let wifi = Self::regs_internal();
let key_slot = wifi.crypto_key_slot(key_slot);
key_slot.addr_low().reset();
key_slot.addr_high().reset();
key_slot.key_value_iter().for_each(KEY_VALUE::reset);
}
pub fn set_interface_crypto_parameters(
&self,
interface: usize,
protect_management_frames: bool,
protect_signaling_and_payload: bool,
is_cipher_aead: bool,
) {
Self::regs_internal()
.crypto_control()
.interface_crypto_control(interface)
.modify(|r, w| unsafe {
w.bits(r.bits() | 0x10103)
.spp_enable()
.bit(protect_signaling_and_payload)
.pmf_disable()
.bit(!protect_management_frames)
.aead_cipher()
.bit(is_cipher_aead)
.sms4()
.clear_bit()
});
}
pub fn set_sms4_status(&self, enabled: bool) {
Self::regs_internal()
.crypto_control()
.general_crypto_control()
.modify(|r, w| unsafe {
w.bits(if enabled {
r.bits() | 0x00ffff00
} else {
r.bits() & 0xff0000ff
})
});
}
pub fn mac_time_offset() -> esp_hal::time::Duration {
cfg_select! {
esp32 => {
let offset = MAC_TIME_OFFSET.load(core::sync::atomic::Ordering::Relaxed);
}
_ => {
let offset = 0;
}
}
esp_hal::time::Duration::from_micros(offset)
}
pub unsafe fn mac_time() -> esp_hal::time::Instant {
esp_hal::time::Instant::EPOCH
+ esp_hal::time::Duration::from_micros(
Self::regs_internal().mac_time().read().bits() as u64
)
+ Self::mac_time_offset()
}
pub fn set_channel(&self, channel_number: u8) {
unsafe {
self.set_mac_state(false);
cfg_select! {
nomac_channel_set => {
crate::ffi::chip_v7_set_chan_nomac(channel_number, 0);
}
_ => {
crate::ffi::chip_v7_set_chan(channel_number, 0);
}
}
disable_wifi_agc();
self.set_mac_state(true);
enable_wifi_agc();
}
}
pub fn run_power_control(&self) {
unsafe {
tx_pwctrl_background(1, 0);
}
}
}