use alloc::vec::Vec;
use core::{fmt, num::NonZeroU16, time::Duration};
use sdmmc_protocol::sdio::io::{AddressMode, FunctionNumber, IoAddress, TransferMode};
use thiserror::Error;
use zeroize::Zeroize;
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct MonotonicTime(u64);
impl MonotonicTime {
pub const fn from_nanos(nanos: u64) -> Self {
Self(nanos)
}
pub const fn as_nanos(self) -> u64 {
self.0
}
pub fn after(self, duration: Duration) -> Self {
Self(
self.0
.saturating_add(u64::try_from(duration.as_nanos()).unwrap_or(u64::MAX)),
)
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct Entropy([u8; 32]);
impl Entropy {
pub const fn new(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub const fn bytes(&self) -> &[u8; 32] {
&self.0
}
}
impl fmt::Debug for Entropy {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("Entropy([REDACTED])")
}
}
impl Drop for Entropy {
fn drop(&mut self) {
self.0.zeroize();
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct Pmk([u8; 32]);
impl Pmk {
pub const fn new(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub const fn bytes(&self) -> &[u8; 32] {
&self.0
}
}
impl fmt::Debug for Pmk {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("Pmk([REDACTED])")
}
}
impl Drop for Pmk {
fn drop(&mut self) {
self.0.zeroize();
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct TxToken(u64);
impl TxToken {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SdioFailure {
Busy,
Timeout,
Crc,
NoCard,
Unsupported,
InvalidRequest,
Bus,
Aborted,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SdioRequestKind {
EnableFunction(FunctionNumber),
SetBlockSize {
function: FunctionNumber,
block_size: NonZeroU16,
},
EnableFunctionInterrupt(FunctionNumber),
ReadByte {
function: FunctionNumber,
address: IoAddress,
},
WriteByte {
function: FunctionNumber,
address: IoAddress,
value: u8,
read_after_write: bool,
},
Read {
function: FunctionNumber,
address: IoAddress,
address_mode: AddressMode,
transfer_mode: TransferMode,
length: usize,
},
Write {
function: FunctionNumber,
address: IoAddress,
address_mode: AddressMode,
transfer_mode: TransferMode,
bytes: Vec<u8>,
},
SetClockHz(u32),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SdioRequest {
pub id: u64,
pub kind: SdioRequestKind,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SdioResponse {
Unit,
Byte(u8),
Data(Vec<u8>),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SdioCompletion {
pub request_id: u64,
pub result: Result<SdioResponse, SdioFailure>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct IrqSnapshot {
pub sequence: u64,
pub card_interrupt: bool,
pub transfer_complete: bool,
pub error: Option<SdioFailure>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ControlRequest {
Scan {
ssid: Option<Vec<u8>>,
},
Connect {
ssid: Vec<u8>,
pmk: Option<Pmk>,
entropy: Option<Entropy>,
},
Disconnect,
StartOpenAccessPoint {
ssid: Vec<u8>,
channel: u8,
},
Cancel,
Shutdown,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum AicInputEvent {
Sdio(SdioCompletion),
Irq(IrqSnapshot),
Control(ControlRequest),
Tx { token: TxToken, frame: Vec<u8> },
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AicInput {
pub now: MonotonicTime,
pub event: Option<AicInputEvent>,
}
impl AicInput {
pub const fn tick(now: MonotonicTime) -> Self {
Self { now, event: None }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AicState {
Stopped,
Starting,
Ready,
Stopping,
Failed,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MailboxWaitPhase {
Flow,
Write,
Confirmation,
Complete,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MailboxRequest {
DebugMemoryRead { address: u32 },
DebugMemoryWrite { address: u32 },
DebugMemoryBlockWrite { address: u32, length: u32 },
DebugMemoryMaskWrite { address: u32 },
StartApplication { address: u32 },
Debug { message_id: u16 },
Lmac { message_id: u16 },
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum AicEvent {
Started { mac_address: [u8; 6] },
ControlComplete,
ControlCancelled,
ControlFailed(AicError),
Receive(Vec<u8>),
TransmitComplete(TxToken),
Stopped,
Failed(AicError),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum AicAction {
SubmitSdio(SdioRequest),
AbortSdio { request_id: u64 },
RetryAt(MonotonicTime),
WaitForInterrupt,
WaitForInterruptUntil(MonotonicTime),
Event(AicEvent),
Idle,
}
#[derive(Clone, Debug, Error, Eq, PartialEq)]
pub enum AicError {
#[error("unsupported AIC chip variant")]
UnsupportedChip,
#[error("caller did not provide WPA entropy")]
EntropyUnavailable,
#[error("invalid control request")]
InvalidControlRequest,
#[error("an operation is already active")]
Busy,
#[error("SDIO request failed: {0:?}")]
Sdio(SdioFailure),
#[error("SDIO completion did not match the active request")]
CompletionMismatch,
#[error(
"AIC mailbox timed out for {request:?} waiting for message {expected_message_id:#06x} in \
{phase:?} phase"
)]
MailboxTimeout {
request: MailboxRequest,
expected_message_id: u16,
phase: MailboxWaitPhase,
},
#[error("AIC mailbox response was malformed")]
MalformedResponse,
#[error(
"AIC receive frame at offset {offset} has type {packet_type:#04x}, declared length \
{declared_length}, and only {available_length} bytes available (header \
{header_words:016x?})"
)]
MalformedRxFrame {
offset: usize,
packet_type: u8,
declared_length: usize,
available_length: usize,
header_words: [u64; 3],
},
#[error("AIC byte-mode receive length has invalid raw value {units}")]
InvalidRxByteLength { units: u8 },
#[error(
"AIC mailbox response for {request:?} message {expected_message_id:#06x} was malformed \
({payload_length} payload bytes)"
)]
MalformedMailboxResponse {
request: MailboxRequest,
expected_message_id: u16,
payload_length: usize,
},
#[error(
"AIC mailbox received confirmation {actual_message_id:#06x} while waiting for \
{expected_message_id:#06x}"
)]
UnexpectedConfirmation {
expected_message_id: u16,
actual_message_id: u16,
},
#[error("AIC CMD52 RAW readback mismatch: expected {expected:#04x}, received {actual:#04x}")]
SdioWriteReadbackMismatch { expected: u8, actual: u8 },
#[error("firmware rejected message {message_id:#06x} with status {status}")]
FirmwareRejected { message_id: u16, status: u16 },
#[error("firmware rejected debug message {message_id:#06x} with status {status:#010x}")]
DebugFirmwareRejected { message_id: u16, status: u32 },
#[error("pinned AIC firmware asset has an invalid internal layout")]
InvalidFirmwareAsset,
#[error("firmware returned an invalid MAC address")]
InvalidMacAddress,
#[error("WPA2 EAPOL MIC verification failed")]
WpaMic,
#[error("WPA2 EAPOL replay validation failed")]
WpaReplay,
#[error("WPA2 RSN information does not match the requested profile")]
WpaRsn,
#[error("invalid WPA2 handshake message or state")]
WpaProtocol,
#[error("WPA2 key data validation failed")]
WpaKeyData,
#[error("firmware disconnected the station with reason code {reason_code}")]
Disconnected { reason_code: u16 },
#[error("unsupported chip revision {0}")]
UnsupportedRevision(u8),
#[error("TX queue is full")]
TxQueueFull,
#[error("AIC control command queue is full")]
ControlQueueFull,
#[error("AIC event queue is full")]
EventQueueFull,
#[error("owner supplied a non-monotonic timestamp")]
NonMonotonicTime,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum IoPurpose {
Startup,
MailboxFlow,
MailboxWrite,
ReceiveCount(RxPath),
ReceiveByteLength(RxPath),
ReceiveData(RxPath),
TransmitFlow,
TransmitData,
Shutdown,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum RxPath {
Command,
Data,
}
pub(super) struct PendingIo {
pub id: u64,
pub purpose: IoPurpose,
}