use alloc::{boxed::Box, sync::Arc, vec::Vec};
use core::{sync::atomic::Ordering, task::Poll};
use crate::fdrv::{
consts::{CONTROL_PORT_MAX_RETRY, CONTROL_PORT_RECONCILE_MS, MAX_REGISTERED_STAS},
core::bus::{BusState, WifiBus},
protocol::{send_me_sta_add_req, send_mm_sta_del_req, send_set_control_port_req},
thread::tx::enqueue_mgmt_frame,
};
pub fn start(bus: Arc<WifiBus>) {
log::debug!("[wifi-ap] worker thread starting");
crate::runtime::runtime().spawn_poll_task(
"wifi-ap",
Box::new(move |cx| {
if *bus.state.lock() == BusState::Down {
return Poll::Ready(());
}
if bus.transport.is_dual_pipe() {
loop {
let del_idx = bus.ap.sta_del_queue.lock().pop_front();
match del_idx {
Some(idx) => {
if let Err(e) = send_mm_sta_del_req(&bus, idx, 0) {
log::warn!("[wifi-ap] MM_STA_DEL sta_idx={} failed: {:?}", idx, e);
}
}
None => break,
}
}
}
loop {
let assoc_req = bus.ap.assoc_queue.lock().pop_front();
match assoc_req {
Some(mpdu) => handle_assoc_req(&bus, &mpdu),
None => break,
}
}
let has_pending = if bus.transport.is_dual_pipe() {
reconcile_control_ports(&bus)
} else {
false
};
bus.ap.assoc_pollset.register(cx.waker());
if !bus.ap.assoc_queue.lock().is_empty() {
cx.waker().wake_by_ref();
} else if has_pending {
crate::runtime::runtime().sleep_ms(CONTROL_PORT_RECONCILE_MS);
cx.waker().wake_by_ref();
}
Poll::Pending
}),
);
}
fn handle_assoc_req(bus: &Arc<WifiBus>, mpdu: &[u8]) {
if mpdu.len() < 28 {
return;
}
let mut sta_mac = [0u8; 6];
sta_mac.copy_from_slice(&mpdu[10..16]);
let vif_idx = bus.conn.vif_idx.load(Ordering::Acquire);
let aid: u16 = 1;
let rates = parse_supported_rates(&mpdu[28..]);
let dual = bus.transport.is_dual_pipe();
let existing = if dual {
bus.ap
.registered_stas
.lock()
.iter()
.find(|(mac, ..)| *mac == sta_mac)
.map(|(_, idx, ctrl, _)| (*idx, *ctrl))
} else {
None
};
let (sta_idx, ctrl_open) = if let Some((idx, ctrl)) = existing {
log::info!(
"[wifi-ap] STA {:02x?} already registered (sta_idx={}, ctrl_open={}), resend Assoc \
Response{}",
sta_mac,
idx,
ctrl,
if ctrl {
" only"
} else {
" + retry control port"
}
);
(idx, ctrl)
} else {
if dual && bus.ap.registered_stas.lock().len() >= MAX_REGISTERED_STAS {
log::warn!(
"[wifi-ap] registered_stas full ({}), reject new STA {:02x?}",
MAX_REGISTERED_STAS,
sta_mac
);
return;
}
let idx = match send_me_sta_add_req(bus, &sta_mac, &rates, aid, vif_idx, 0) {
Ok(idx) => idx,
Err(e) => {
log::warn!("[wifi-ap] ME_STA_ADD failed: {:?}", e);
return;
}
};
bus.conn.sta_idx.store(idx, Ordering::Release);
if dual {
bus.ap.registered_stas.lock().push((sta_mac, idx, false, 0));
}
log::info!(
"[wifi-ap] STA {:02x?} registered: sta_idx={}, aid={}",
sta_mac,
idx,
aid
);
(idx, false)
};
let ap_mac = match *bus.conn.sta_mac.lock() {
Some(m) => m,
None => {
log::warn!("[wifi-ap] no AP mac, cannot send Assoc Response");
return;
}
};
let frame = build_assoc_response(&sta_mac, &ap_mac, aid, &rates);
match enqueue_mgmt_frame(bus, frame) {
Ok(()) => log::info!("[wifi-ap] Assoc Response queued -> {:02x?}", sta_mac),
Err(e) => log::warn!("[wifi-ap] Assoc Response enqueue failed: {:?}", e),
}
if !ctrl_open {
try_open_control_port(bus, &sta_mac, sta_idx);
}
}
fn try_open_control_port(bus: &Arc<WifiBus>, sta_mac: &[u8; 6], sta_idx: u8) -> bool {
match send_set_control_port_req(bus, sta_idx, true, 0) {
Ok(_) => {
log::info!("[wifi-ap] control port OPENED for sta_idx={}", sta_idx);
if let Some(e) = bus
.ap
.registered_stas
.lock()
.iter_mut()
.find(|(mac, ..)| mac == sta_mac)
{
e.2 = true;
}
true
}
Err(e) => {
log::warn!(
"[wifi-ap] open control port failed (sta_idx={}): {:?}",
sta_idx,
e
);
false
}
}
}
fn reconcile_control_ports(bus: &Arc<WifiBus>) -> bool {
let pending: alloc::vec::Vec<([u8; 6], u8)> = {
let tbl = bus.ap.registered_stas.lock();
tbl.iter()
.filter(|(.., open, retries)| !*open && *retries < CONTROL_PORT_MAX_RETRY)
.map(|(mac, idx, ..)| (*mac, *idx))
.collect()
};
if pending.is_empty() {
return false;
}
let mut still_pending = false;
for (mac, idx) in pending {
{
let mut tbl = bus.ap.registered_stas.lock();
if let Some(e) = tbl.iter_mut().find(|(m, ..)| *m == mac) {
e.3 = e.3.saturating_add(1);
}
}
if !try_open_control_port(bus, &mac, idx) {
still_pending = true;
}
}
still_pending
}
fn parse_supported_rates(ies: &[u8]) -> Vec<u8> {
let mut i = 0;
while i + 2 <= ies.len() {
let eid = ies[i];
let len = ies[i + 1] as usize;
if i + 2 + len > ies.len() {
break;
}
if eid == 1 {
return ies[i + 2..i + 2 + len].to_vec();
}
i += 2 + len;
}
Vec::from([0x82, 0x84, 0x8b, 0x96])
}
fn build_assoc_response(dst: &[u8; 6], ap_mac: &[u8; 6], aid: u16, rates: &[u8]) -> Vec<u8> {
let mut f = Vec::with_capacity(40);
f.extend_from_slice(&[0x10, 0x00]); f.extend_from_slice(&[0x00, 0x00]); f.extend_from_slice(dst); f.extend_from_slice(ap_mac); f.extend_from_slice(ap_mac); f.extend_from_slice(&[0x00, 0x00]);
f.extend_from_slice(&0x0021u16.to_le_bytes());
f.extend_from_slice(&0u16.to_le_bytes());
f.extend_from_slice(&(aid | 0xC000).to_le_bytes());
let n = rates.len().min(8);
f.push(1);
f.push(n as u8);
f.extend_from_slice(&rates[..n]);
f
}