use alloc::{boxed::Box, collections::VecDeque, sync::Arc, vec::Vec};
use core::sync::atomic::Ordering;
use rd_net::{
DmaBuffer, Event, IRxQueue, ITxQueue, Interface, NetError, QueueConfig, WifiControl,
WifiLinkPolicy,
};
use rdif_eth::DriverGeneric;
use crate::{
common::ChipVariant,
fdrv::{
core::bus::WifiBus,
thread::{rx::register_rx_data_callback, tx},
wifi::api::{WifiClient, WifiConfig},
},
};
const DEVICE_NAME: &str = "aic8800-wifi";
const QUEUE_ID: usize = 0;
const QUEUE_SIZE: usize = 64;
const BUFFER_SIZE: usize = 2048;
const MAX_TX_QUEUE_LEN: usize = 128;
fn net_err(e: crate::fdrv::wifi::api::WifiError) -> NetError {
NetError::Other(Box::new(e))
}
pub struct AicWifiNetDev {
bus: Arc<WifiBus>,
client: WifiClient,
chip: ChipVariant,
mac: [u8; 6],
link_policy: Option<WifiLinkPolicy>,
tx_created: bool,
rx_created: bool,
irq_enabled: bool,
}
unsafe impl Send for AicWifiNetDev {}
unsafe impl Sync for AicWifiNetDev {}
impl AicWifiNetDev {
pub fn new(bus: Arc<WifiBus>, chip: ChipVariant, mac: [u8; 6]) -> Self {
Self {
client: WifiClient::new(Arc::clone(&bus)),
bus,
chip,
mac,
link_policy: None,
tx_created: false,
rx_created: false,
irq_enabled: false,
}
}
pub fn with_link_policy(mut self, policy: WifiLinkPolicy) -> Self {
self.link_policy = Some(policy);
self
}
}
impl DriverGeneric for AicWifiNetDev {
fn name(&self) -> &str {
DEVICE_NAME
}
}
impl WifiControl for AicWifiNetDev {
fn connect(&mut self, ssid: &str, password: &str) -> Result<(), NetError> {
self.client
.lmac_configure(self.chip, 6000)
.map_err(net_err)?;
let config = if password.is_empty() {
WifiConfig::open(ssid)
} else {
WifiConfig::wpa2_psk(ssid, password)
};
let mut last_err = None;
for attempt in 0..2 {
if attempt > 0 {
log::info!("[aic8800] retrying connect (attempt {})...", attempt + 1);
crate::runtime::runtime().sleep_ms(3000);
}
match self.client.connect(&config, 15000) {
Ok(()) => {
log::info!("[aic8800] connected to '{}'", ssid);
return Ok(());
}
Err(e) => {
log::warn!("[aic8800] connect attempt {} failed: {:?}", attempt + 1, e);
last_err = Some(e);
}
}
}
Err(net_err(last_err.unwrap_or(
crate::fdrv::wifi::api::WifiError::OperationFailed("connect failed".into()),
)))
}
fn disconnect(&mut self) -> Result<(), NetError> {
self.client.disconnect().map_err(net_err)?;
log::info!("[aic8800] disconnected");
Ok(())
}
fn start_ap_open(&mut self, ssid: &[u8], channel: u8) -> Result<(), NetError> {
let cfm = self
.client
.start_ap_open(self.chip, ssid, channel, 6000)
.map_err(net_err)?;
log::info!("[aic8800] AP started, APM_START_CFM={:02x?}", cfm);
Ok(())
}
fn set_rx_wake(&mut self, wake: fn()) {
register_rx_data_callback(wake);
}
fn link_policy(&self) -> Option<WifiLinkPolicy> {
self.link_policy
}
}
impl Interface for AicWifiNetDev {
fn mac_address(&self) -> [u8; 6] {
self.bus.conn.sta_mac.lock().unwrap_or(self.mac)
}
fn create_tx_queue(&mut self) -> Option<Box<dyn ITxQueue>> {
if self.tx_created {
return None;
}
self.tx_created = true;
Some(Box::new(AicTxQueue {
bus: self.bus.clone(),
tx_done: VecDeque::with_capacity(QUEUE_SIZE),
}))
}
fn create_rx_queue(&mut self) -> Option<Box<dyn IRxQueue>> {
if self.rx_created {
return None;
}
self.rx_created = true;
Some(Box::new(AicRxQueue {
bus: self.bus.clone(),
rx_buffers: VecDeque::with_capacity(QUEUE_SIZE),
}))
}
fn enable_irq(&mut self) {
self.irq_enabled = true;
}
fn disable_irq(&mut self) {
self.irq_enabled = false;
}
fn is_irq_enabled(&self) -> bool {
self.irq_enabled
}
fn handle_irq(&mut self) -> Event {
let mut event = Event::none();
event.tx_queue.insert(QUEUE_ID);
event.rx_queue.insert(QUEUE_ID);
event
}
fn wifi_control(&mut self) -> Option<&mut dyn WifiControl> {
Some(self)
}
}
fn aic_queue_config() -> QueueConfig {
QueueConfig {
dma_mask: u64::MAX,
align: 1,
buf_size: BUFFER_SIZE,
ring_size: QUEUE_SIZE,
}
}
struct AicTxQueue {
bus: Arc<WifiBus>,
tx_done: VecDeque<u64>,
}
impl ITxQueue for AicTxQueue {
fn id(&self) -> usize {
QUEUE_ID
}
fn config(&self) -> QueueConfig {
aic_queue_config()
}
fn submit(&mut self, buffer: DmaBuffer) -> Result<(), NetError> {
if self.bus.conn.vif_idx.load(Ordering::Acquire) == 0xFF
|| self.bus.tx.pktcnt.load(Ordering::Acquire) >= MAX_TX_QUEUE_LEN as u32
{
return Err(NetError::Retry);
}
let packet = unsafe { core::slice::from_raw_parts(buffer.virt.as_ptr(), buffer.len) };
let eth_frame: Vec<u8> = packet.to_vec();
tx::enqueue_data_frame(&self.bus, eth_frame).map_err(|_| NetError::Retry)?;
self.tx_done.push_back(buffer.bus_addr);
Ok(())
}
fn reclaim(&mut self) -> Option<u64> {
self.tx_done.pop_front()
}
}
struct AicRxQueue {
bus: Arc<WifiBus>,
rx_buffers: VecDeque<RuntimeBuffer>,
}
#[derive(Clone, Copy)]
struct RuntimeBuffer {
virt: usize,
bus_addr: u64,
len: usize,
}
impl From<DmaBuffer> for RuntimeBuffer {
fn from(buffer: DmaBuffer) -> Self {
Self {
virt: buffer.virt.as_ptr() as usize,
bus_addr: buffer.bus_addr,
len: buffer.len,
}
}
}
impl IRxQueue for AicRxQueue {
fn id(&self) -> usize {
QUEUE_ID
}
fn config(&self) -> QueueConfig {
aic_queue_config()
}
fn submit(&mut self, buffer: DmaBuffer) -> Result<(), NetError> {
self.rx_buffers.push_back(buffer.into());
Ok(())
}
fn reclaim(&mut self) -> Option<(u64, usize)> {
if self.rx_buffers.is_empty() {
return None;
}
let frame = self.bus.rx.data_queue.lock().pop_front()?;
let buffer = self.rx_buffers.pop_front()?;
let len = core::cmp::min(frame.len(), buffer.len);
unsafe {
core::ptr::copy_nonoverlapping(frame.as_ptr(), buffer.virt as *mut u8, len);
}
Some((buffer.bus_addr, len))
}
}