1 use system_error::SystemError;
2 
3 use crate::{
4     arch::{io::PortIOArch, CurrentIrqArch, CurrentPortIOArch},
5     exception::InterruptArch,
6 };
7 
8 pub struct RtcTime {
9     pub second: i32,
10     pub minute: i32,
11     pub hour: i32,
12     pub day: i32,
13     pub month: i32,
14     pub year: i32,
15 }
16 
17 impl Default for RtcTime {
default() -> Self18     fn default() -> Self {
19         Self {
20             second: (0),
21             minute: (0),
22             hour: (0),
23             day: (0),
24             month: (0),
25             year: (0),
26         }
27     }
28 }
29 
30 impl RtcTime {
31     ///@brief 从主板cmos中获取时间
32     ///
33     ///@param self time结构体
34     ///@return int 成功则为0
get(&mut self) -> Result<i32, SystemError>35     pub fn get(&mut self) -> Result<i32, SystemError> {
36         // 为防止中断请求打断该过程,需要先关中断
37         let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
38         //0x0B
39         let status_register_b: u8 = read_cmos(0x0B); // 读取状态寄存器B
40         let is_24h: bool = if (status_register_b & 0x02) != 0 {
41             true
42         } else {
43             false
44         }; // 判断是否启用24小时模式
45 
46         let is_binary: bool = if (status_register_b & 0x04) != 0 {
47             true
48         } else {
49             false
50         }; // 判断是否为二进制码
51 
52         loop {
53             self.year = read_cmos(CMOSTimeSelector::Year as u8) as i32;
54             self.month = read_cmos(CMOSTimeSelector::Month as u8) as i32;
55             self.day = read_cmos(CMOSTimeSelector::Day as u8) as i32;
56             self.hour = read_cmos(CMOSTimeSelector::Hour as u8) as i32;
57             self.minute = read_cmos(CMOSTimeSelector::Minute as u8) as i32;
58             self.second = read_cmos(CMOSTimeSelector::Second as u8) as i32;
59 
60             if self.second == read_cmos(CMOSTimeSelector::Second as u8) as i32 {
61                 break;
62             } // 若读取时间过程中时间发生跳变则重新读取
63         }
64 
65         unsafe {
66             CurrentPortIOArch::out8(0x70, 0x00);
67         }
68 
69         if !is_binary
70         // 把BCD转为二进制
71         {
72             self.second = (self.second & 0xf) + (self.second >> 4) * 10;
73             self.minute = (self.minute & 0xf) + (self.minute >> 4) * 10;
74             self.hour = ((self.hour & 0xf) + ((self.hour & 0x70) >> 4) * 10) | (self.hour & 0x80);
75             self.day = (self.day & 0xf) + ((self.day / 16) * 10);
76             self.month = (self.month & 0xf) + (self.month >> 4) * 10;
77             self.year = (self.year & 0xf) + (self.year >> 4) * 10;
78         }
79         self.year += 2000;
80 
81         if (!is_24h) && (self.hour & 0x80) != 0 {
82             self.hour = ((self.hour & 0x7f) + 12) % 24;
83         } // 将十二小时制转为24小时
84 
85         drop(irq_guard);
86 
87         return Ok(0);
88     }
89 }
90 
91 ///置位0x70的第7位,禁止不可屏蔽中断
92 #[inline]
read_cmos(addr: u8) -> u893 fn read_cmos(addr: u8) -> u8 {
94     unsafe {
95         CurrentPortIOArch::out8(0x70, 0x80 | addr);
96         return CurrentPortIOArch::in8(0x71);
97     }
98 }
99 
100 /// used in the form of u8
101 #[repr(u8)]
102 enum CMOSTimeSelector {
103     Second = 0x00,
104     Minute = 0x02,
105     Hour = 0x04,
106     Day = 0x07,
107     Month = 0x08,
108     Year = 0x09,
109 }
110