#![no_std]
use cpuio::Port;
use core::{
cmp::Ordering,
fmt::{Display, Formatter, Result},
usize,
};
#[derive(Debug)]
pub struct CMOS {
address_port: Port<u8>,
data_port: Port<u8>,
}
impl CMOS {
pub unsafe fn new() -> CMOS { CMOS { address_port: Port::<u8>::new(0x70), data_port: Port::<u8>::new(0x71) } }
pub fn read_all(&mut self, output: &mut [u8; 128]) {
for i in 0..128 {
self.address_port.write(i);
output[i as usize] = self.data_port.read();
}
}
pub fn write_all(&mut self, input: &[u8; 128]) {
for i in 0..128 {
self.address_port.write(i);
self.data_port.write(input[i as usize]);
}
}
pub fn read(&mut self, reg: u8) -> u8 {
self.address_port.write(reg);
self.data_port.read()
}
pub fn write(&mut self, reg: u8, val: u8) {
self.address_port.write(reg);
self.data_port.write(val);
}
pub fn get_update_in_progress_flag(&mut self) -> u8 { self.read(0x0A) & 0x80 }
fn read_into_rtc(&mut self, rtc_time: &mut RTCDateTime) {
while self.get_update_in_progress_flag() != 0 {
rtc_time.second = self.read(0x00);
rtc_time.minute = self.read(0x02);
rtc_time.hour = self.read(0x04);
rtc_time.day = self.read(0x07);
rtc_time.month = self.read(0x08);
rtc_time.year = self.read(0x09) as usize;
}
}
pub fn read_rtc(&mut self, century_handler: CMOSCenturyHandler) -> RTCDateTime {
let mut rtc_time = RTCDateTime { second: 0, minute: 0, hour: 0, day: 0, month: 0, year: 0 };
self.read_into_rtc(&mut rtc_time);
let mut century = 0;
if let CMOSCenturyHandler::CenturyRegister(century_reg) = century_handler {
century = self.read(century_reg);
}
let mut last_second;
let mut last_minute;
let mut last_hour;
let mut last_day;
let mut last_month;
let mut last_year;
let mut last_century;
loop {
last_second = rtc_time.second;
last_minute = rtc_time.minute;
last_hour = rtc_time.hour;
last_day = rtc_time.day;
last_month = rtc_time.month;
last_year = rtc_time.year;
last_century = century;
self.read_into_rtc(&mut rtc_time);
if last_second != rtc_time.second
|| last_minute != rtc_time.minute
|| last_hour != rtc_time.hour
|| last_day != rtc_time.day
|| last_month != rtc_time.month
|| last_year != rtc_time.year
|| last_century != century
{
break;
}
}
let register_b = self.read(0x0B);
if (register_b & 0x04) == 0 {
rtc_time.second = (rtc_time.second & 0x0F) + ((rtc_time.second / 16) * 10);
rtc_time.minute = (rtc_time.minute & 0x0F) + ((rtc_time.minute / 16) * 10);
rtc_time.hour = ((rtc_time.hour & 0x0F) + (((rtc_time.hour & 0x70) / 16) * 10)) | (rtc_time.hour & 0x80);
rtc_time.day = (rtc_time.day & 0x0F) + ((rtc_time.day / 16) * 10);
rtc_time.month = (rtc_time.month & 0x0F) + ((rtc_time.month / 16) * 10);
rtc_time.year = (rtc_time.year & 0x0F) + ((rtc_time.year / 16) * 10);
if let CMOSCenturyHandler::CenturyRegister(_) = century_handler {
century = (century & 0x0F) + ((century / 16) * 10);
}
}
if ((register_b & 0x02) == 0) && ((rtc_time.hour & 0x80) != 0) {
rtc_time.hour = ((rtc_time.hour & 0x7F) + 12) % 24;
}
match century_handler {
CMOSCenturyHandler::CenturyRegister(_) => rtc_time.year += (century * 100) as usize,
CMOSCenturyHandler::CurrentYear(current_year) => {
rtc_time.year += (current_year / 100) * 100;
if rtc_time.year < current_year {
rtc_time.year += 100;
}
}
}
rtc_time
}
}
#[derive(Debug, Clone, Copy)]
pub enum CMOSCenturyHandler {
CenturyRegister(u8),
CurrentYear(usize),
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct RTCDateTime {
pub year: usize,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
}
pub const MAX: RTCDateTime = RTCDateTime { year: usize::MAX, month: 12, day: 31, hour: 23, minute: 59, second: 59 };
pub const MIN: RTCDateTime = RTCDateTime { year: 0, month: 1, day: 1, hour: 0, minute: 0, second: 0 };
impl Ord for RTCDateTime {
fn cmp(&self, other: &Self) -> Ordering {
(self.year, self.month, self.day, self.hour, self.minute, self.second).cmp(&(
other.year,
other.month,
other.day,
other.hour,
other.minute,
other.second,
))
}
}
impl PartialOrd for RTCDateTime {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}
impl Display for RTCDateTime {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}-{}-{}T{}:{}:{}Z", self.year, self.month, self.day, self.hour, self.minute, self.second)
}
}
impl RTCDateTime {
#[inline]
pub fn is_valid(&self) -> bool {
self.month < 13
&& self.hour < 24
&& self.minute < 60
&& self.second < 60
&& self.day < RTCDateTime::days_by_month(self.year, self.month)
}
#[inline]
pub fn from_tuple(tuple: &(usize, u8, u8, u8, u8, u8)) -> Option<Self> {
let datetime = Self { year: tuple.0, month: tuple.1, day: tuple.2, hour: tuple.3, minute: tuple.4, second: tuple.5 };
if datetime.is_valid() {
Some(datetime)
} else {
None
}
}
#[inline]
pub fn as_tuple(&self) -> (usize, u8, u8, u8, u8, u8) {
(self.year, self.month, self.day, self.hour, self.minute, self.second)
}
#[doc(hidden)]
fn days_by_month(year: usize, month: u8) -> u8 {
match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 => {
if (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 {
29
} else {
28
}
},
_ => 0,
}
}
}