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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
#![no_std]
#![feature(unboxed_closures)]
#![feature(abi_x86_interrupt)]
#![feature(fn_traits)]
// extern crate alloc;
#[macro_use] extern crate lazy_static;
extern crate port_io;
extern crate irq_safety;
extern crate spin;
extern crate state_store;
#[macro_use] extern crate log;
extern crate x86_64;
use port_io::Port;
use irq_safety::hold_interrupts;
use core::sync::atomic::{AtomicUsize, Ordering};
use spin::Mutex;
// use spin::Once;
use state_store::{get_state, insert_state, SSCached};
//standard port to write to on CMOS to select registers
const CMOS_WRITE_PORT: u16 = 0x70;
//standard port to read register values from on CMOS or write to to change settings
const CMOS_READ_PORT: u16 = 0x71;
//used to select register
static CMOS_WRITE: Mutex<Port<u8>> = Mutex::new( Port::new(CMOS_WRITE_PORT));
//used to change cmos settings
static CMOS_WRITE_SETTINGS: Mutex<Port<u8>> = Mutex::new(Port::new(CMOS_READ_PORT));
//used to read from cmos register
static CMOS_READ: Mutex<Port<u8>> = Mutex::new( Port::new(CMOS_READ_PORT));
type RtcTicks = AtomicUsize;
lazy_static! {
static ref RTC_TICKS: SSCached<RtcTicks> = {
insert_state(RtcTicks::new(0));
get_state::<RtcTicks>()
};
}
pub type RtcInterruptFunction = fn(Option<usize>);
// static RTC_INTERRUPT_FUNC: Once<RtcInterruptFunction> = Once::new();
// /// Initialize the RTC interrupt with the given frequency
// /// and the given closure that will run on each RTC interrupt.
// /// The closure is provided with the current number of RTC ticks since boot,
// /// in the form of an `Option<usize>` because it is not guaranteed that the number of ticks can be retrieved.
// pub fn init(rtc_freq: usize, interrupt_func: RtcInterruptFunction) -> Result<(InterruptHandler), ()> {
// RTC_INTERRUPT_FUNC.call_once(|| interrupt_func);
// enable_rtc_interrupt();
// let res = set_rtc_frequency(rtc_freq);
// res.map( |_| rtc_interrupt_handler as InterruptHandler )
// }
//write a u8 to the CMOS port (0x70)
fn write_cmos(value: u8) {
unsafe{
CMOS_WRITE.lock().write(value)
}
}
//read a u8 from CMOS port 0x71
fn read_cmos() -> u8{
CMOS_READ.lock().read()
}
//returns true if update in progress, false otherwise
fn is_update_in_progress() -> bool{
//writing to this register causes cmos to output 1 if rtc update in progress
write_cmos(0x0A);
let is_in_progress: bool = read_cmos() == 1;
is_in_progress
}
//register value is entered, rtc's associated value is output, waits for update in progress signal to end
fn read_register(register: u8) -> u8{
//waits for "update in progress" signal to finish in order to read correct values
while is_update_in_progress() {}
write_cmos(register);
//converts bcd value to binary value which is what is used for printing
let bcd = read_cmos();
(bcd/16)*10 + (bcd & 0xf)
}
/// A timestamp obtained from the real-time clock.
#[derive(Debug)]
pub struct RtcTime {
pub seconds: u8,
pub minutes: u8,
pub hours: u8,
pub days: u8,
pub months: u8,
pub years: u8,
}
use core::fmt;
impl fmt::Display for RtcTime {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "RTC Time: {}/{}/{} {}:{}:{}",
self.years, self.months, self.days, self.hours, self.minutes, self.seconds)
}
}
//call this function to print RTC's date and time
pub fn read_rtc() -> RtcTime {
//calls read register function which writes to port 0x70 to set RTC then reads from 0x71 which outputs correct value
let second = read_register(0x00);
let minute = read_register(0x02);
let hour = read_register(0x04);
let day = read_register(0x07);
let month = read_register(0x08);
let year = read_register(0x09);
RtcTime {
seconds: second,
minutes: minute,
hours: hour,
days: day,
months: month,
years: year
}
}
/// Returns the current RTC tick count.
pub fn get_rtc_ticks() -> Option<usize> {
RTC_TICKS.get().map(|ticks| ticks.load(Ordering::Acquire))
}
/// turn on IRQ 8 (mapped to 0x28), rtc begins sending interrupts
pub fn enable_rtc_interrupt()
{
let _held_interrupts = hold_interrupts();
write_cmos(0x0C);
read_cmos();
//select cmos register 0x8B
write_cmos(0x8B);
//value needed to turn on bit 6 of register B
let prev = read_cmos();
//we want it to go back to register 0x8B, it was reset when read
write_cmos(0x8B);
//here we don't use the cmos_write function because that only writes to port 0x70, in this case we need to write to 0x71
//writing to 0x71 because not selecting register, setting rtc
unsafe{
CMOS_WRITE_SETTINGS.lock().write(prev | 0x40);
}
trace!("RTC Enabled!");
// here: _held_interrupts falls out of scope, re-enabling interrupts if they were previously enabled.
}
/// the log base 2 of an integer value
fn log2(value: usize) -> usize {
let mut v = value;
let mut result = 0;
v >>= 1;
while v > 0 {
result += 1;
v >>= 1;
}
result
}
/// The error returned from [`set_rtc_frequency()`] if an invalid rate is provided.
#[derive(Debug)]
pub struct InvalidRtcRate;
/// Sets the period of the RTC interrupt to the given `rate`.
///
/// `rate` must be a power of 2, between 2 and 8192 inclusive;
/// otherwise, an error is returned.
pub fn set_rtc_frequency(rate: usize) -> Result<(), InvalidRtcRate> {
if !(rate.is_power_of_two() && (2..=8192).contains(&rate)) {
error!("RTC rate was {}, must be a power of two between [2: 8192] inclusive!", rate);
return Err(InvalidRtcRate);
}
// formula is "rate = 32768 Hz >> (dividor - 1)"
let dividor: u8 = log2(rate) as u8 + 2;
let _held_interrupts = hold_interrupts();
// bottom 4 bits of register A are the "rate dividor", setting them to rate we want without altering top 4 bits
write_cmos(0x8A);
let prev = read_cmos();
write_cmos(0x8A);
unsafe{
CMOS_WRITE_SETTINGS.lock().write((prev & 0xF0) | dividor);
}
trace!("RTC frequency changed to {} Hz!", rate);
Ok(())
// here: _held_interrupts falls out of scope, re-enabling interrupts if they were previously enabled.
}