use alloc::{sync::Arc, vec, vec::Vec};
use crate::fdrv::{
core::bus::WifiBus,
protocol::{cmd::send_cmd, lmac_msg::*},
};
fn build_open_beacon(
bssid: &[u8; 6],
ssid: &[u8],
channel: u8,
bcn_int: u16,
) -> (Vec<u8>, u16, u8) {
let mut head: Vec<u8> = Vec::with_capacity(64);
head.extend_from_slice(&[0x80, 0x00]); head.extend_from_slice(&[0x00, 0x00]); head.extend_from_slice(&[0xFF; 6]); head.extend_from_slice(bssid); head.extend_from_slice(bssid); head.extend_from_slice(&[0x00, 0x00]);
head.extend_from_slice(&[0u8; 8]); head.extend_from_slice(&bcn_int.to_le_bytes()); head.extend_from_slice(&0x0021u16.to_le_bytes());
head.push(0x00); head.push(ssid.len() as u8);
head.extend_from_slice(ssid);
head.push(0x01); head.push(8);
head.extend_from_slice(&[0x82, 0x84, 0x8B, 0x96, 0x0C, 0x12, 0x18, 0x24]);
head.push(0x03); head.push(1);
head.push(channel);
let tim_oft = head.len() as u16;
let mut beacon = head;
beacon.extend_from_slice(&[0x05, 0x04, 0x00, 0x01, 0x00, 0x00]);
(beacon, tim_oft, 6)
}
pub fn send_apm_set_beacon_ie_req(
bus: &Arc<WifiBus>,
vif_idx: u8,
beacon: &[u8],
timeout_ms: u64,
) -> Result<Vec<u8>, CmdError> {
assert!(
beacon.len() <= 512,
"beacon too large for apm_set_bcn_ie_req"
);
let mut param = vec![0u8; 4 + 512];
param[0] = vif_idx;
param[2..4].copy_from_slice(&(beacon.len() as u16).to_le_bytes());
param[4..4 + beacon.len()].copy_from_slice(beacon);
log::info!(
"[apm] APM_SET_BEACON_IE_REQ: vif_idx={}, bcn_len={}",
vif_idx,
beacon.len()
);
send_cmd(bus, APM_SET_BEACON_IE_REQ, TASK_APM, ¶m, timeout_ms)
}
pub fn send_apm_start_req(
bus: &Arc<WifiBus>,
vif_idx: u8,
channel: u8,
bcn_len: u16,
tim_oft: u16,
tim_len: u8,
timeout_ms: u64,
) -> Result<Vec<u8>, CmdError> {
const APM_START_REQ_SIZE: usize = 52;
let mut param = vec![0u8; APM_START_REQ_SIZE];
param[0] = 4; param[1] = 0x82; param[2] = 0x84; param[3] = 0x8B; param[4] = 0x96;
let freq = CHAN_2G4_FREQS[(channel as usize - 1).min(13)];
param[14..16].copy_from_slice(&freq.to_le_bytes());
param[16] = 0; param[17] = 0; param[18] = 20;
param[20..24].copy_from_slice(&(freq as u32).to_le_bytes());
param[28] = PHY_CHNL_BW_20;
param[36..38].copy_from_slice(&bcn_len.to_le_bytes());
param[38..40].copy_from_slice(&tim_oft.to_le_bytes());
param[40..42].copy_from_slice(&100u16.to_le_bytes());
param[48..50].copy_from_slice(&0x888Eu16.to_be_bytes());
param[50] = tim_len;
param[51] = vif_idx;
log::info!(
"[apm] APM_START_REQ: vif_idx={}, ch={}, freq={}, bcn_len={}, tim_oft={}, tim_len={}",
vif_idx,
channel,
freq,
bcn_len,
tim_oft,
tim_len
);
send_cmd(bus, APM_START_REQ, TASK_APM, ¶m, timeout_ms)
}
pub fn start_open_ap(
bus: &Arc<WifiBus>,
vif_idx: u8,
bssid: &[u8; 6],
ssid: &[u8],
channel: u8,
timeout_ms: u64,
) -> Result<Vec<u8>, CmdError> {
let (beacon, tim_oft, tim_len) = build_open_beacon(bssid, ssid, channel, 100);
log::info!(
"[apm] start_open_ap: ssid={:?}, ch={}, beacon_len={}, tim_oft={}",
core::str::from_utf8(ssid).unwrap_or("<non-utf8>"),
channel,
beacon.len(),
tim_oft
);
send_apm_set_beacon_ie_req(bus, vif_idx, &beacon, timeout_ms)?;
send_apm_start_req(
bus,
vif_idx,
channel,
beacon.len() as u16,
tim_oft,
tim_len,
timeout_ms,
)
}
pub fn send_apm_stop_req(
bus: &Arc<WifiBus>,
vif_idx: u8,
timeout_ms: u64,
) -> Result<Vec<u8>, CmdError> {
let param = [vif_idx];
send_cmd(bus, APM_STOP_REQ, TASK_APM, ¶m, timeout_ms)
}
pub fn send_me_sta_add_req(
bus: &Arc<WifiBus>,
sta_mac: &[u8; 6],
rates: &[u8],
aid: u16,
vif_idx: u8,
timeout_ms: u64,
) -> Result<u8, CmdError> {
let mut param = vec![0u8; ME_STA_ADD_REQ_SIZE];
param[0..6].copy_from_slice(sta_mac);
let n = rates.len().min(12);
param[6] = n as u8;
param[7..7 + n].copy_from_slice(&rates[..n]);
param[120..124].copy_from_slice(&STA_QOS_CAPA.to_le_bytes());
param[124..126].copy_from_slice(&aid.to_le_bytes());
param[129] = vif_idx;
let cfm = send_cmd(bus, ME_STA_ADD_REQ, TASK_ME, ¶m, timeout_ms)?;
if cfm.len() < 2 {
log::warn!("[apm] ME_STA_ADD_CFM too short: {}", cfm.len());
return Err(CmdError::InvalidResponse);
}
let sta_idx = cfm[0];
let status = cfm[1];
log::info!(
"[apm] ME_STA_ADD_CFM: sta_idx={} status={}",
sta_idx,
status
);
if status != 0 {
return Err(CmdError::InvalidResponse);
}
Ok(sta_idx)
}