Skip to main content

cmos_rtc/
lib.rs

1//! "CMOS" is a tiny bit of very low power static memory that lives on the same chip as the Real-Time Clock (RTC)
2//!
3//! ## Usage
4//! ```rust
5//! use cmos_rtc::{ReadRTC, Time};
6//!
7//! let mut cmos = ReadRTC::new(0x00, 0x00);
8//! let time: Time = cmos.read();
9//! ```
10#![no_std]
11use core::cmp::Ordering;
12use x86_64::instructions::port::Port;
13
14/// Selecting a CMOS register port
15const CMOS_ADDRESS: u16 = 0x70;
16
17/// Data receiving port
18const CMOS_DATA: u16 = 0x71;
19
20/// Struct for storage time
21#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
22pub struct Time {
23    pub second: u8,
24    pub minute: u8,
25    pub hour: u8,
26    pub day: u8,
27    pub month: u8,
28    pub year: u8,
29    pub century: u8,
30}
31
32impl PartialOrd for Time {
33    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
34        Some(self.cmp(other))
35    }
36}
37
38impl Ord for Time {
39    fn cmp(&self, other: &Self) -> Ordering {
40        self.century
41            .cmp(&other.century)
42            .then(self.year.cmp(&other.year))
43            .then(self.month.cmp(&other.month))
44            .then(self.day.cmp(&other.day))
45            .then(self.hour.cmp(&other.hour))
46            .then(self.minute.cmp(&other.minute))
47            .then(self.second.cmp(&other.second))
48    }
49}
50
51/// Struct for storage ports, current year and century register
52pub struct ReadRTC {
53    cmos_address: Port<u8>,
54    cmos_data: Port<u8>,
55    current_year: u8,
56    century_register: u8,
57}
58
59impl ReadRTC {
60    /// Creates a new `ReadRTC`.
61    #[must_use]
62    pub const fn new(current_year: u8, century_register: u8) -> ReadRTC {
63        ReadRTC {
64            cmos_address: Port::new(CMOS_ADDRESS),
65            cmos_data: Port::new(CMOS_DATA),
66            current_year,
67            century_register,
68        }
69    }
70
71    /// Lets you know if a time update is in progress
72    fn get_update_in_progress_flag(&mut self) -> u8 {
73        unsafe {
74            self.cmos_address.write(0x0A);
75            self.cmos_data.read() & 0x80
76        }
77    }
78
79    /// Retrieves a value from a time register
80    fn get_rtc_register(&mut self, reg: u8) -> u8 {
81        unsafe {
82            self.cmos_address.write(reg);
83            self.cmos_data.read()
84        }
85    }
86
87    /// Updating our time
88    fn update_time(&mut self) -> Time {
89        // Make sure an update isn't in progress
90        while self.get_update_in_progress_flag() != 0 {}
91
92        Time {
93            second: self.get_rtc_register(0x00),
94            minute: self.get_rtc_register(0x02),
95            hour: self.get_rtc_register(0x04),
96            day: self.get_rtc_register(0x07),
97            month: self.get_rtc_register(0x08),
98            year: self.get_rtc_register(0x09),
99            century: if self.century_register == 0 {
100                0
101            } else {
102                self.get_rtc_register(self.century_register)
103            },
104        }
105    }
106
107    /// Gets the time without regard to the time zone
108    pub fn read(&mut self) -> Time {
109        let mut last_time: Time;
110        let mut time: Time = self.update_time();
111
112        loop {
113            last_time = time;
114            time = self.update_time();
115
116            if (last_time.second == time.second)
117                && (last_time.minute == time.minute)
118                && (last_time.hour == time.hour)
119                && (last_time.day == time.day)
120                && (last_time.month == time.month)
121                && (last_time.year == time.year)
122                && (last_time.century == time.century)
123            {
124                break;
125            }
126        }
127
128        let register_b = self.get_rtc_register(0x0B);
129
130        if register_b & 0x04 == 0 {
131            time.second = (time.second & 0x0F) + ((time.second / 16) * 10);
132            time.minute = (time.minute & 0x0F) + ((time.minute / 16) * 10);
133            time.hour =
134                ((time.hour & 0x0F) + (((time.hour & 0x70) / 16) * 10)) | (time.hour & 0x80);
135            time.day = (time.day & 0x0F) + ((time.day / 16) * 10);
136            time.month = (time.month & 0x0F) + ((time.month / 16) * 10);
137            time.year = (time.year & 0x0F) + ((time.year / 16) * 10);
138
139            if self.century_register != 0 {
140                time.century = (time.century & 0x0F) + ((time.century / 16) * 10);
141            }
142        }
143
144        // Convert 12 hour clock to 24 hour clock
145        if register_b & 0x02 == 0 && (time.hour & 0x80 != 0) {
146            time.hour = ((time.hour & 0x7F) + 12) % 24;
147        }
148
149        // Calculate the full (4-digit) year
150        if self.century_register == 0 {
151            time.year += (self.current_year / 100) * 100;
152
153            if time.year < self.current_year {
154                time.year += 100;
155            };
156        } else {
157            time.year += time.century * 100;
158        }
159
160        time
161    }
162}