1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
//! The core timing of Sunrise.

use core::sync::atomic::{AtomicUsize, Ordering};

use super::event;
use super::event::{IRQEvent, Waitable};
use super::sync::Once;
use super::utils::div_ceil;

/// This represent the information to derive all internal timing in Sunrise.
struct KernelTimerInfo {
    /// The frequency of the oscillator used as primary source of this timer, when not divided, in Hertz.
    ///
    /// The value here is only informative, you should use `.irq_periode_ns`.
    oscillator_frequency: u64,

    /// The IRQ period used on this timer in nanoseconds.
    pub irq_period_ns: u64,

    /// The IRQ number that the timer use.
    pub irq_number: u8,
}

/// Stores the information needed for Sunrise's internal timing.
static KERNEL_TIMER_INFO: Once<KernelTimerInfo> = Once::new();

/// Set the information required for Sunrise timer to work.
/// 
/// # Panics
///
/// Panics if the timer info has already been initialized.
pub fn set_kernel_timer_info(irq_number: u8, oscillator_frequency: u64, irq_period_ns: u64) {
    assert!(KERNEL_TIMER_INFO.r#try().is_none(), "Kernel Timer Info is already initialized!");
    KERNEL_TIMER_INFO.call_once(|| {
        KernelTimerInfo {
            irq_number,
            oscillator_frequency,
            irq_period_ns
        }
    });
}

/// Returns a stream of event that trigger every `ns` amount of nanoseconds.
/// 
/// # Note
/// 
/// - If the timer resolution cannot handle it, this is not going to be accurate.
/// - Minimal resolution for HPET (10Mhz) / HPET QEMU (100Mhz): 100ns / 10ns
/// - Minimal resolution for PIC (~1Mhz): 10ms
pub fn wait_ns(ns: usize) -> impl Waitable {
    let timer_info = KERNEL_TIMER_INFO.r#try().expect("Kernel Timer Info is not initialized!");
    IRQTimer::new(ns, timer_info.irq_number, timer_info.irq_period_ns)
}

#[derive(Debug)]
/// A stream of event that trigger every `ns` amount of nanoseconds, by counting interruptions.
pub struct IRQTimer {
    /// Approximation of number of ns spent between triggers.
    every_ns: usize,
    /// IRQ event period in nanoseconds.
    irq_period_ns: u64,
    /// The IRQ that we wait on.
    parent_event: IRQEvent,
    /// The reset value of ``.countdown_value``.
    reset_value: usize,
    /// Number of IRQ triggers to wait for. Derived from `.every_ns`. This is the exact time amout that is used.
    countdown_value: AtomicUsize
}

impl IRQTimer {
    /// Create a new IRQ timer instance from the time to wait (in ns), the irq number and irq event period (in ns).
    pub fn new(ns: usize, irq: u8, irq_period_ns: u64) -> Self {
        let mut reset_value = div_ceil(ns as u64, irq_period_ns) as usize;
        if reset_value == 0 {
            reset_value = 1;
        }

        IRQTimer {
            every_ns: ns,
            irq_period_ns,
            parent_event: event::wait_event(irq),
            reset_value,
            countdown_value: AtomicUsize::new(0)
        }
    }
}

impl Waitable for IRQTimer {
    fn register(&self) {
        self.parent_event.register()
    }

    fn is_signaled(&self) -> bool {
        // First, reset the spins if necessary
        self.countdown_value.compare_and_swap(0, self.reset_value, Ordering::SeqCst);

        // Then, check if it's us.
        self.parent_event.is_signaled()
            // Then, check if we need more spins.
            && self.countdown_value.fetch_sub(1, Ordering::SeqCst) == 1
    }
}