use alloc::{collections::VecDeque, sync::Arc, vec::Vec};
use core::sync::atomic::{AtomicBool, AtomicU8, AtomicU16, AtomicU32, AtomicUsize, Ordering};
use atomic_waker::AtomicWaker;
use spin::Mutex;
use crate::{
common::SDIOWIFI_INTR_CONFIG_REG,
fdrv::core::{pollset::PollSet, sdio_transport::SdioTransport},
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BusState {
Down,
Up,
}
pub struct TxFrame {
pub data: Vec<u8>,
pub priority: u8,
pub is_mgmt: bool,
}
pub struct ConnectionState {
status: AtomicU8,
pub vif_idx: AtomicU8,
pub sta_idx: AtomicU8,
pub sta_mac: Mutex<Option<[u8; 6]>>,
pub ap_mac: Mutex<Option<[u8; 6]>>,
}
pub const STATUS_DISCONNECTED: u8 = 0;
pub const STATUS_CONNECTING: u8 = 1;
pub const STATUS_CONNECTED: u8 = 2;
pub const STATUS_FAILED: u8 = 3;
impl Default for ConnectionState {
fn default() -> Self {
Self::new()
}
}
impl ConnectionState {
pub fn new() -> Self {
Self {
status: AtomicU8::new(STATUS_DISCONNECTED),
vif_idx: AtomicU8::new(0xFF),
sta_idx: AtomicU8::new(0xFF),
sta_mac: Mutex::new(None),
ap_mac: Mutex::new(None),
}
}
pub fn get_status(&self) -> u8 {
self.status.load(Ordering::Acquire)
}
pub fn set_status(&self, s: u8) {
self.status.store(s, Ordering::Release)
}
pub fn is_connected(&self) -> bool {
self.status.load(Ordering::Acquire) == STATUS_CONNECTED
}
}
pub struct CmdState {
pub pending: Mutex<Option<Vec<u8>>>,
pub pending_flag: AtomicBool,
pub expected_cfm_id: AtomicU16,
pub rsp_error: AtomicBool,
pub rsp_queue: Mutex<VecDeque<Vec<u8>>>,
pub rsp_pollset: PollSet,
}
impl Default for CmdState {
fn default() -> Self {
Self::new()
}
}
impl CmdState {
pub fn new() -> Self {
Self {
pending: Mutex::new(None),
pending_flag: AtomicBool::new(false),
expected_cfm_id: AtomicU16::new(0),
rsp_error: AtomicBool::new(false),
rsp_queue: Mutex::new(VecDeque::new()),
rsp_pollset: PollSet::new(),
}
}
}
pub struct RxState {
pub irq_waker: AtomicWaker,
pub irq_pending: AtomicBool,
pub data_queue: Mutex<VecDeque<Vec<u8>>>,
pub data_pollset: PollSet,
pub eapol_queue: Mutex<VecDeque<Vec<u8>>>,
pub eapol_pollset: PollSet,
pub tx_cfm_queue: Mutex<VecDeque<Vec<u8>>>,
pub tx_cfm_pollset: PollSet,
}
impl Default for RxState {
fn default() -> Self {
Self::new()
}
}
impl RxState {
pub fn new() -> Self {
Self {
irq_waker: AtomicWaker::new(),
irq_pending: AtomicBool::new(false),
data_queue: Mutex::new(VecDeque::new()),
data_pollset: PollSet::new(),
eapol_queue: Mutex::new(VecDeque::new()),
eapol_pollset: PollSet::new(),
tx_cfm_queue: Mutex::new(VecDeque::new()),
tx_cfm_pollset: PollSet::new(),
}
}
}
pub struct TxState {
pub queue: Mutex<VecDeque<TxFrame>>,
pub pktcnt: AtomicU32,
pub wake_pollset: PollSet,
pub ind_queue: Mutex<VecDeque<Vec<u8>>>,
pub ind_pollset: PollSet,
}
impl Default for TxState {
fn default() -> Self {
Self::new()
}
}
impl TxState {
pub fn new() -> Self {
Self {
queue: Mutex::new(VecDeque::new()),
pktcnt: AtomicU32::new(0),
wake_pollset: PollSet::new(),
ind_queue: Mutex::new(VecDeque::new()),
ind_pollset: PollSet::new(),
}
}
}
pub struct ApState {
pub assoc_queue: Mutex<VecDeque<Vec<u8>>>,
pub assoc_pollset: PollSet,
}
impl Default for ApState {
fn default() -> Self {
Self::new()
}
}
impl ApState {
pub fn new() -> Self {
Self {
assoc_queue: Mutex::new(VecDeque::new()),
assoc_pollset: PollSet::new(),
}
}
}
pub struct WifiBus {
pub transport: Arc<SdioTransport>,
pub state: Mutex<BusState>,
pub conn: ConnectionState,
pub cmd: CmdState,
pub rx: RxState,
pub tx: TxState,
pub ap: ApState,
}
impl WifiBus {
pub fn new(transport: Arc<SdioTransport>) -> Arc<Self> {
Arc::new(Self {
transport,
state: Mutex::new(BusState::Down),
conn: ConnectionState::new(),
cmd: CmdState::new(),
rx: RxState::new(),
tx: TxState::new(),
ap: ApState::new(),
})
}
pub fn shutdown(self: &Arc<Self>) {
*self.state.lock() = BusState::Down;
let _ = self.transport.write_byte(1, SDIOWIFI_INTR_CONFIG_REG, 0x00);
self.transport.disable_irq();
self.tx.wake_pollset.wake();
self.tx.queue.lock().clear();
self.rx.irq_waker.wake();
self.rx.data_queue.lock().clear();
self.ap.assoc_pollset.wake();
self.ap.assoc_queue.lock().clear();
self.cmd.rsp_error.store(true, Ordering::Release);
self.cmd.rsp_pollset.wake();
self.rx.tx_cfm_pollset.wake();
self.rx.eapol_pollset.wake();
self.rx.eapol_queue.lock().clear();
self.tx.ind_pollset.wake();
self.tx.ind_queue.lock().clear();
clear_global_bus();
log::debug!("[wifi-bus] shutdown complete");
}
}
static WIFI_BUS_PTR: AtomicUsize = AtomicUsize::new(0);
pub fn set_global_bus(bus: &Arc<WifiBus>) {
let ptr = Arc::into_raw(Arc::clone(bus));
let old = WIFI_BUS_PTR.swap(ptr as usize, Ordering::AcqRel);
if old != 0 {
unsafe {
Arc::from_raw(old as *const WifiBus);
}
}
}
pub fn get_global_bus() -> Option<&'static WifiBus> {
let ptr = WIFI_BUS_PTR.load(Ordering::Acquire);
if ptr == 0 {
None
} else {
unsafe { Some(&*(ptr as *const WifiBus)) }
}
}
pub fn clear_global_bus() {
let old = WIFI_BUS_PTR.swap(0, Ordering::AcqRel);
if old != 0 {
unsafe {
Arc::from_raw(old as *const WifiBus);
}
}
}
use core::sync::atomic::AtomicU64;
pub(crate) static IRQ_COUNT: AtomicU64 = AtomicU64::new(0);
pub fn sdio1_irq_handler() {
IRQ_COUNT.fetch_add(1, Ordering::Relaxed);
let Some(bus) = get_global_bus() else { return };
bus.transport.mask_card_irq();
bus.rx.irq_pending.store(true, Ordering::Release);
bus.rx.irq_waker.wake();
}