#[cfg(target_arch = "aarch64")]
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use crate::systems::rpi::{
driver, exception,
exception::asynchronous::IRQNumber,
synchronization::{interface::Mutex, IRQSafeNullLock},
};
use alloc::{boxed::Box, vec::Vec};
use core::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use mango_core::warn;
struct Timeout
{
due_time: Duration,
period: Option<Duration>,
callback: TimeoutCallback,
}
struct OrderedTimeoutQueue
{
inner: Vec<Timeout>,
}
pub type TimeoutCallback = Box<dyn Fn() + Send>;
pub struct TimeManager
{
queue: IRQSafeNullLock<OrderedTimeoutQueue>,
}
static TIME_MANAGER: TimeManager = TimeManager::new();
impl Timeout
{
pub fn is_periodic(&self) -> bool
{
self.period.is_some()
}
pub fn refresh(&mut self)
{
if let Some(delay) = self.period
{
self.due_time += delay;
}
}
}
impl OrderedTimeoutQueue
{
pub const fn new() -> Self
{
Self { inner: Vec::new() }
}
pub fn push(&mut self, timeout: Timeout)
{
self.inner.push(timeout);
self.inner.sort_by(|a, b| b.due_time.cmp(&a.due_time));
}
pub fn peek_next_due_time(&self) -> Option<Duration>
{
let timeout = self.inner.last()?;
Some(timeout.due_time)
}
pub fn pop(&mut self) -> Option<Timeout>
{
self.inner.pop()
}
}
pub fn time_manager() -> &'static TimeManager
{
&TIME_MANAGER
}
impl TimeManager
{
pub const COMPATIBLE: &'static str = "ARM Architectural Timer";
pub const fn new() -> Self
{
Self {
queue: IRQSafeNullLock::new(OrderedTimeoutQueue::new()),
}
}
pub fn resolution(&self) -> Duration
{
arch_time::resolution()
}
pub fn uptime(&self) -> Duration
{
arch_time::uptime()
}
pub fn spin_for(&self, duration: Duration)
{
arch_time::spin_for(duration)
}
fn set_timeout(&self, timeout: Timeout)
{
self.queue.lock(|queue| {
queue.push(timeout);
arch_time::set_timeout_irq(queue.peek_next_due_time().unwrap());
});
}
pub fn set_timeout_once(&self, delay: Duration, callback: TimeoutCallback)
{
let timeout = Timeout {
due_time: self.uptime() + delay,
period: None,
callback,
};
self.set_timeout(timeout);
}
pub fn set_timeout_periodic(&self, delay: Duration, callback: TimeoutCallback)
{
let timeout = Timeout {
due_time: self.uptime() + delay,
period: Some(delay),
callback,
};
self.set_timeout(timeout);
}
}
pub fn init() -> Result<(), &'static str>
{
static INIT_DONE: AtomicBool = AtomicBool::new(false);
if INIT_DONE.load(Ordering::Relaxed)
{
return Err("Init already done");
}
let timer_descriptor =
driver::DeviceDriverDescriptor::new(time_manager(), None, Some(arch_time::timeout_irq()));
driver::driver_manager().register_driver(timer_descriptor);
INIT_DONE.store(true, Ordering::Relaxed);
Ok(())
}
impl driver::interface::DeviceDriver for TimeManager
{
type IRQNumberType = IRQNumber;
fn compatible(&self) -> &'static str
{
Self::COMPATIBLE
}
fn register_and_enable_irq_handler(
&'static self,
irq_number: &Self::IRQNumberType,
) -> Result<(), &'static str>
{
use exception::asynchronous::{irq_manager, IRQHandlerDescriptor};
let descriptor = IRQHandlerDescriptor::new(*irq_number, Self::COMPATIBLE, self);
irq_manager().register_handler(descriptor)?;
irq_manager().enable(irq_number);
Ok(())
}
}
impl exception::asynchronous::interface::IRQHandler for TimeManager
{
fn handle(&self) -> Result<(), &'static str>
{
arch_time::conclude_timeout_irq();
let maybe_timeout: Option<Timeout> = self.queue.lock(|queue| {
let next_due_time = queue.peek_next_due_time()?;
if next_due_time > self.uptime()
{
return None;
}
let mut timeout = queue.pop().unwrap();
if timeout.is_periodic()
{
timeout.refresh();
}
Some(timeout)
});
let timeout = match maybe_timeout
{
None =>
{
warn!("Spurious timeout IRQ");
return Ok(());
}
Some(t) => t,
};
(timeout.callback)();
self.queue.lock(|queue| {
if timeout.is_periodic()
{
queue.push(timeout);
};
if let Some(due_time) = queue.peek_next_due_time()
{
arch_time::set_timeout_irq(due_time);
}
});
Ok(())
}
}