use alloc::{boxed::Box, sync::Arc, vec};
use core::time::Duration;
use rdif_eth::{
DriverGeneric, NetDevice, NetDeviceInfo, NetDeviceParts, NetError, NetHardIrqEndpoint,
NetIrqSourceId, NetPollGroupId, NetPollGroupParts, NetQueuePairParts, QueueConfig,
WifiTransaction,
};
use sdmmc_protocol::sdio::CompletionIrqRearmHost;
use super::{
control::{AicNetControl, AicWifiControl},
irq::AicHardIrq,
startup::{AicOwnerStartup, AicPollIrqControl},
};
use crate::rdif::{
device::{MacAddressState, OwnerChannels, WifiChannels, queues::queue_parts, shared_irq_latch},
error::AicRdifError,
owner::AicOwner,
};
const GROUP_ID: NetPollGroupId = NetPollGroupId::new(0);
const DEFAULT_QUEUE_SIZE: usize = 32;
const DEFAULT_FRAME_SIZE: usize = 2048;
const DEFAULT_STARTUP_TIMEOUT: Duration = Duration::from_secs(60);
const DEFAULT_CONTROL_TIMEOUT: Duration = Duration::from_secs(30);
#[derive(Clone, Debug)]
pub struct AicRdifOptions {
pub irq_source: NetIrqSourceId,
pub startup_transaction: Option<WifiTransaction>,
pub queue_size: usize,
pub frame_size: usize,
pub startup_delay: Duration,
pub startup_timeout: Duration,
pub control_timeout: Duration,
}
impl AicRdifOptions {
pub const fn new(irq_source: NetIrqSourceId) -> Self {
Self {
irq_source,
startup_transaction: None,
queue_size: DEFAULT_QUEUE_SIZE,
frame_size: DEFAULT_FRAME_SIZE,
startup_delay: Duration::ZERO,
startup_timeout: DEFAULT_STARTUP_TIMEOUT,
control_timeout: DEFAULT_CONTROL_TIMEOUT,
}
}
pub fn with_startup_transaction(mut self, transaction: WifiTransaction) -> Self {
self.startup_transaction = Some(transaction);
self
}
pub const fn with_startup_delay(mut self, delay: Duration) -> Self {
self.startup_delay = delay;
self
}
pub const fn with_startup_timeout(mut self, timeout: Duration) -> Self {
self.startup_timeout = timeout;
self
}
pub const fn with_control_timeout(mut self, timeout: Duration) -> Self {
self.control_timeout = timeout;
self
}
}
pub struct AicRdifDevice<H: CompletionIrqRearmHost + 'static> {
host: H,
options: AicRdifOptions,
dma_mask: u64,
}
impl<H: CompletionIrqRearmHost + Send + 'static> AicRdifDevice<H> {
pub fn new(host: H, options: AicRdifOptions) -> Result<Self, AicRdifError> {
if options.queue_size < 2
|| options.frame_size == 0
|| options.startup_timeout.is_zero()
|| options.control_timeout.is_zero()
{
return Err(AicRdifError::QueueUnavailable);
}
let dma_mask = host
.device_dma()
.map_err(|_| AicRdifError::DmaUnavailable)?
.info()
.constraints()
.addr_mask;
Ok(Self {
host,
options,
dma_mask,
})
}
}
impl<H: CompletionIrqRearmHost + Send + 'static> DriverGeneric for AicRdifDevice<H> {
fn name(&self) -> &str {
"aic8800"
}
}
impl<H: CompletionIrqRearmHost + Send + 'static> NetDevice for AicRdifDevice<H> {
fn into_parts(self: Box<Self>) -> Result<NetDeviceParts, NetError> {
let Self {
host,
options,
dma_mask,
} = *self;
let queue_config = QueueConfig {
dma_mask,
align: 4,
buf_size: options.frame_size,
ring_size: options.queue_size,
};
let (tx, rx, queue_ports) = queue_parts(queue_config);
let irq_latch = shared_irq_latch();
let mac = Arc::new(MacAddressState::new([0; 6]));
let parts = host.into_parts();
let wifi_channels = WifiChannels::new();
let progress_signal = Arc::clone(&wifi_channels.progress_signal);
let (owner, wifi_requests, wifi_progress) = AicOwner::new(
parts.bus,
parts.card_irq,
queue_ports,
wifi_channels,
Arc::clone(&irq_latch),
Arc::clone(&mac),
);
let OwnerChannels {
sender: owner_sender,
receiver: owner_receiver,
} = OwnerChannels::new();
Ok(NetDeviceParts {
info: NetDeviceInfo::new("aic8800", [0; 6]),
control: Box::new(AicNetControl::new(Arc::clone(&mac))),
wifi_control: Some(Box::new(AicWifiControl::new(
wifi_requests,
wifi_progress,
Arc::clone(&progress_signal),
options.startup_transaction,
options.control_timeout,
))),
poll_groups: vec![NetPollGroupParts {
id: GROUP_ID,
queues: NetQueuePairParts {
tx: Box::new(tx),
rx: Box::new(rx),
},
irq_control: Box::new(AicPollIrqControl::new(owner_receiver, progress_signal)),
owner_startup: Some(Box::new(AicOwnerStartup::new(
owner,
owner_sender,
options.startup_delay,
options.startup_timeout,
))),
irq_endpoints: vec![NetHardIrqEndpoint::new(
options.irq_source,
Box::new(AicHardIrq::new(parts.irq, irq_latch)),
)],
}],
})
}
}