use core::time::Duration;
use rdif_eth::{
NetError, NetIrqSnapshot, NetOwnerStartup, NetOwnerStartupProgress, NetPollIrqControl,
NetRearmResult,
};
use ringbuf::traits::{Consumer, Producer};
use sdmmc_protocol::sdio::SdMmcIrqHost;
use crate::{
AicError, SdioFailure,
rdif::{
device::{OwnerReceiver, OwnerSender},
owner::{AicOwner, OwnerProgress, OwnerWait},
},
};
pub(super) struct AicOwnerStartup<H: SdMmcIrqHost + Send + 'static> {
owner: Option<AicOwner<H>>,
owner_sender: OwnerSender<H>,
startup_delay: Duration,
startup_timeout: Duration,
delay_deadline: Option<u64>,
timeout_deadline: Option<u64>,
started: bool,
}
impl<H: SdMmcIrqHost + Send + 'static> AicOwnerStartup<H> {
pub(super) fn new(
owner: AicOwner<H>,
owner_sender: OwnerSender<H>,
startup_delay: Duration,
startup_timeout: Duration,
) -> Self {
Self {
owner: Some(owner),
owner_sender,
startup_delay,
startup_timeout,
delay_deadline: None,
timeout_deadline: None,
started: false,
}
}
fn finish(&mut self, progress: OwnerProgress) -> Result<NetOwnerStartupProgress, NetError> {
match progress {
OwnerProgress::Ready => {
let owner_sender = &mut self.owner_sender;
transfer_owner(&mut self.owner, |owner| owner_sender.try_push(owner).err())
.map_err(|error| match error {
TransferOwnerError::Missing => NetError::Stopped,
TransferOwnerError::Rejected => NetError::InvalidParts,
})?;
Ok(NetOwnerStartupProgress::Ready)
}
OwnerProgress::Wait(OwnerWait::Interrupt) => {
Ok(NetOwnerStartupProgress::WaitForInterruptUntil {
deadline_nanos: self.timeout_deadline.ok_or(NetError::InvalidParts)?,
})
}
OwnerProgress::Wait(OwnerWait::RetryAt(deadline_nanos)) => {
Ok(NetOwnerStartupProgress::RetryAt { deadline_nanos })
}
}
}
}
impl<H: SdMmcIrqHost + Send + 'static> NetOwnerStartup for AicOwnerStartup<H> {
fn start(&mut self, now_nanos: u64) -> Result<NetOwnerStartupProgress, NetError> {
let timeout = u64::try_from(self.startup_timeout.as_nanos()).unwrap_or(u64::MAX);
self.timeout_deadline = Some(now_nanos.saturating_add(timeout));
if !self.startup_delay.is_zero() {
let delay = u64::try_from(self.startup_delay.as_nanos()).unwrap_or(u64::MAX);
let delay_deadline = now_nanos.saturating_add(delay);
self.delay_deadline = Some(delay_deadline);
return Ok(NetOwnerStartupProgress::RetryAt {
deadline_nanos: delay_deadline
.min(self.timeout_deadline.ok_or(NetError::InvalidParts)?),
});
}
self.started = true;
let progress = self
.owner
.as_mut()
.ok_or(NetError::Stopped)?
.start(now_nanos)
.map_err(NetError::from)?;
self.finish(progress)
}
fn advance(&mut self, now_nanos: u64) -> Result<NetOwnerStartupProgress, NetError> {
let timeout_deadline = self.timeout_deadline.ok_or(NetError::InvalidParts)?;
if now_nanos >= timeout_deadline {
shutdown_owner(&mut self.owner, |owner| {
owner.shutdown().map_err(NetError::from)
})?;
return Err(NetError::Other(alloc::boxed::Box::new(AicError::Sdio(
SdioFailure::Timeout,
))));
}
let owner = self.owner.as_mut().ok_or(NetError::Stopped)?;
let progress = if self.started {
owner.advance(now_nanos)
} else {
let deadline = self.delay_deadline.ok_or(NetError::InvalidParts)?;
if now_nanos < deadline {
return Ok(NetOwnerStartupProgress::RetryAt {
deadline_nanos: deadline,
});
}
self.started = true;
owner.start(now_nanos)
}
.map_err(NetError::from)?;
self.finish(progress)
}
fn cancel(&mut self) -> Result<(), NetError> {
shutdown_owner(&mut self.owner, |owner| {
owner.shutdown().map_err(NetError::from)
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum TransferOwnerError {
Missing,
Rejected,
}
fn transfer_owner<T>(
slot: &mut Option<T>,
transfer: impl FnOnce(T) -> Option<T>,
) -> Result<(), TransferOwnerError> {
let owner = slot.take().ok_or(TransferOwnerError::Missing)?;
match transfer(owner) {
None => Ok(()),
Some(owner) => {
*slot = Some(owner);
Err(TransferOwnerError::Rejected)
}
}
}
fn shutdown_owner<T, E>(
slot: &mut Option<T>,
shutdown: impl FnOnce(&mut T) -> Result<(), E>,
) -> Result<(), E> {
let Some(mut owner) = slot.take() else {
return Ok(());
};
if let Err(error) = shutdown(&mut owner) {
core::mem::forget(owner);
return Err(error);
}
Ok(())
}
pub(super) struct AicPollIrqControl<H: SdMmcIrqHost + Send + 'static> {
owner: Option<AicOwner<H>>,
owner_receiver: OwnerReceiver<H>,
}
impl<H: SdMmcIrqHost + Send + 'static> AicPollIrqControl<H> {
pub(super) fn new(owner_receiver: OwnerReceiver<H>) -> Self {
Self {
owner: None,
owner_receiver,
}
}
fn owner(&mut self) -> Result<&mut AicOwner<H>, NetError> {
if self.owner.is_none() {
self.owner = self.owner_receiver.try_pop();
}
self.owner.as_mut().ok_or(NetError::Stopped)
}
}
impl<H: SdMmcIrqHost + Send + 'static> NetPollIrqControl for AicPollIrqControl<H> {
fn quiesce(&mut self) -> Result<(), NetError> {
self.owner()?.quiesce().map_err(NetError::from)
}
fn shutdown(&mut self) -> Result<(), NetError> {
self.owner()?.shutdown().map_err(NetError::from)
}
fn rearm_and_check(&mut self, now_nanos: u64) -> Result<NetRearmResult, NetError> {
let (progress, pending) = self
.owner()?
.rearm_and_advance(now_nanos)
.map_err(NetError::from)?;
Ok(if pending {
NetRearmResult::WorkPending(NetIrqSnapshot::all_queue_work())
} else if let OwnerProgress::Wait(OwnerWait::RetryAt(deadline_nanos)) = progress {
NetRearmResult::RetryAt { deadline_nanos }
} else {
NetRearmResult::Idle
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn failed_owner_transfer_restores_the_unique_owner() {
let mut owner = Some(7_u8);
assert_eq!(
transfer_owner(&mut owner, Some),
Err(TransferOwnerError::Rejected)
);
assert_eq!(owner, Some(7));
}
#[test]
fn shutdown_consumes_owner_and_is_idempotent() {
let mut owner = Some(());
let mut shutdown_count = 0;
shutdown_owner(&mut owner, |_| {
shutdown_count += 1;
Ok::<_, ()>(())
})
.unwrap();
shutdown_owner(&mut owner, |_| {
shutdown_count += 1;
Ok::<_, ()>(())
})
.unwrap();
assert_eq!(shutdown_count, 1);
}
}