1#![warn(missing_docs)]
8
9use self::spec::CmosReg;
10use self::spec::ENABLE_OSCILLATOR_CONTROL;
11use self::spec::StatusRegA;
12use self::spec::StatusRegB;
13use self::spec::StatusRegC;
14use self::spec::StatusRegD;
15use chipset_device::ChipsetDevice;
16use chipset_device::io::IoError;
17use chipset_device::io::IoResult;
18use chipset_device::pio::PortIoIntercept;
19use chipset_device::poll_device::PollDevice;
20use inspect::Inspect;
21use inspect::InspectMut;
22use local_clock::InspectableLocalClock;
23use local_clock::LocalClockTime;
24use std::ops::RangeInclusive;
25use std::task::Poll;
26use std::time::Duration;
27use vmcore::device_state::ChangeDeviceState;
28use vmcore::line_interrupt::LineInterrupt;
29use vmcore::vmtime::VmTime;
30use vmcore::vmtime::VmTimeAccess;
31use vmcore::vmtime::VmTimeSource;
32use vmcore::vmtime::VmTimerPeriodic;
33
34mod spec {
35 use bitfield_struct::bitfield;
38 use inspect::Inspect;
39
40 open_enum::open_enum! {
41 #[derive(Inspect)]
43 #[inspect(debug)]
44 pub enum CmosReg: u8 {
45 SECOND = 0x00,
46 SECOND_ALARM = 0x01,
47 MINUTE = 0x02,
48 MINUTE_ALARM = 0x03,
49 HOUR = 0x04,
50 HOUR_ALARM = 0x05,
51 DAY_OF_WEEK = 0x06,
52 DAY_OF_MONTH = 0x07,
53 MONTH = 0x08,
54 YEAR = 0x09,
55 STATUS_A = 0x0A,
56 STATUS_B = 0x0B,
57 STATUS_C = 0x0C,
58 STATUS_D = 0x0D,
59 }
60 }
61
62 impl CmosReg {
63 pub fn depends_on_rtc(&self, century: CmosReg) -> bool {
65 matches!(
66 *self,
67 CmosReg::SECOND
68 | CmosReg::MINUTE
69 | CmosReg::HOUR
70 | CmosReg::DAY_OF_WEEK
71 | CmosReg::DAY_OF_MONTH
72 | CmosReg::MONTH
73 | CmosReg::YEAR
74 ) || *self == century
75 }
76
77 pub fn depends_on_alarm(&self) -> bool {
79 matches!(
80 *self,
81 CmosReg::SECOND_ALARM | CmosReg::MINUTE_ALARM | CmosReg::HOUR_ALARM
82 )
83 }
84 }
85
86 pub const ENABLE_OSCILLATOR_CONTROL: u8 = 0b010;
87
88 pub const PERIODIC_TIMER_RATE_HZ: [usize; 15] = [
91 256, 128, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2,
92 ];
93
94 #[rustfmt::skip]
95 #[bitfield(u8)]
96 pub struct StatusRegA {
97 #[bits(4)] pub periodic_timer_rate: u8,
98 #[bits(3)] pub oscillator_control: u8,
99 #[bits(1)] pub update: bool,
100 }
101
102 #[rustfmt::skip]
103 #[bitfield(u8)]
104 pub struct StatusRegB {
105 pub dst: bool, pub h24_mode: bool,
107 pub disable_bcd: bool,
108 pub square_wave_enable: bool, pub irq_enable_update: bool,
110 pub irq_enable_alarm: bool,
111 pub irq_enable_periodic: bool,
112 pub set: bool,
113 }
114
115 #[rustfmt::skip]
116 #[bitfield(u8)]
117 pub struct StatusRegC {
118 #[bits(4)] _unused: u8,
119 pub irq_update: bool,
120 pub irq_alarm: bool,
121 pub irq_periodic: bool,
122 pub irq_combined: bool,
123 }
124
125 #[rustfmt::skip]
126 #[bitfield(u8)]
127 pub struct StatusRegD {
128 #[bits(7)] _unused: u8,
129 pub vrt: bool,
132 }
133
134 pub const ALARM_WILDCARD: u8 = 0xFF;
135}
136
137open_enum::open_enum! {
138 enum RtcIoPort: u16 {
140 ADDR = 0x70,
141 DATA = 0x71,
142 }
143}
144
145#[derive(Debug, Clone, Inspect)]
147#[inspect(transparent)]
148struct CmosData(
149 #[inspect(with = "|x| inspect::iter_by_index(x).map_value(inspect::AsHex)")] [u8; 256],
150);
151
152impl CmosData {
153 fn empty() -> CmosData {
154 CmosData([0; 256])
155 }
156}
157
158impl std::ops::Index<CmosReg> for CmosData {
159 type Output = u8;
160
161 fn index(&self, index: CmosReg) -> &Self::Output {
162 &self.0[index.0 as usize]
163 }
164}
165
166impl std::ops::IndexMut<CmosReg> for CmosData {
167 fn index_mut(&mut self, index: CmosReg) -> &mut Self::Output {
168 &mut self.0[index.0 as usize]
169 }
170}
171
172#[derive(InspectMut)]
174pub struct Rtc {
175 century_reg: CmosReg,
177 initial_cmos: Option<[u8; 256]>,
178 enlightened_interrupts: bool,
179
180 real_time_source: Box<dyn InspectableLocalClock>,
182 interrupt: LineInterrupt,
183 vmtime_alarm: VmTimeAccess,
184 vmtimer_periodic: VmTimerPeriodic,
185 vmtimer_update: VmTimerPeriodic,
186
187 #[inspect(debug)]
189 last_update_bit_blip: LocalClockTime,
190
191 state: RtcState,
193}
194
195#[derive(Debug, Inspect)]
196struct RtcState {
197 addr: u8,
198 cmos: CmosData,
199}
200
201impl RtcState {
202 fn new(initial_cmos: Option<[u8; 256]>) -> Self {
203 let mut cmos = initial_cmos.map(CmosData).unwrap_or_else(CmosData::empty);
204
205 cmos[CmosReg::STATUS_A] = {
206 StatusRegA::new()
207 .with_periodic_timer_rate(0b0110)
208 .with_oscillator_control(ENABLE_OSCILLATOR_CONTROL)
209 .into()
210 };
211 cmos[CmosReg::STATUS_B] = {
212 StatusRegB::new()
213 .with_disable_bcd(false)
214 .with_h24_mode(true)
215 .into()
216 };
217 cmos[CmosReg::STATUS_C] = StatusRegC::new().into();
218 cmos[CmosReg::STATUS_D] = StatusRegD::new().with_vrt(true).into();
219
220 Self {
221 addr: 0x80,
224 cmos,
225 }
226 }
227}
228
229impl ChangeDeviceState for Rtc {
230 fn start(&mut self) {}
231
232 async fn stop(&mut self) {}
233
234 async fn reset(&mut self) {
235 self.state = RtcState::new(self.initial_cmos);
236
237 self.update_timers();
238 self.update_interrupt_line_level();
239 }
240}
241
242impl ChipsetDevice for Rtc {
243 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
244 Some(self)
245 }
246
247 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
248 Some(self)
249 }
250}
251
252fn to_bcd(n: u8) -> u8 {
253 ((n / 10) << 4) | (n % 10)
254}
255
256fn from_bcd(n: u8) -> u8 {
257 (n >> 4) * 10 + (n & 0xf)
258}
259
260fn canonical_hms(status_b: StatusRegB, mut hour: u8, mut min: u8, mut sec: u8) -> (u8, u8, u8) {
262 if !status_b.disable_bcd() {
263 sec = from_bcd(sec);
264 min = from_bcd(min);
265
266 if status_b.h24_mode() {
267 hour = from_bcd(hour);
268 } else {
269 let hour_ampm = from_bcd(hour & 0x7F);
270 if hour & 0x80 != 0 {
271 hour = hour_ampm + 12;
272 } else {
273 hour = hour_ampm
274 }
275 }
276 } else {
277 if !status_b.h24_mode() {
278 if hour & 0x80 != 0 {
279 hour = (hour & 0x7F) + 12;
280 }
281 }
282 }
283
284 (hour, min, sec)
285}
286
287impl Rtc {
288 pub fn new(
305 real_time_source: Box<dyn InspectableLocalClock>,
306 interrupt: LineInterrupt,
307 vmtime_source: &VmTimeSource,
308 century_reg_idx: u8,
309 initial_cmos: Option<[u8; 256]>,
310 enlightened_interrupts: bool,
311 ) -> Self {
312 Rtc {
313 century_reg: CmosReg(century_reg_idx),
314 initial_cmos,
315 enlightened_interrupts,
316
317 real_time_source,
318 interrupt,
319 vmtime_alarm: vmtime_source.access("rtc-alarm"),
320 vmtimer_periodic: VmTimerPeriodic::new(vmtime_source.access("rtc-periodic")),
321 vmtimer_update: VmTimerPeriodic::new(vmtime_source.access("rtc-update")),
322
323 last_update_bit_blip: LocalClockTime::from_millis_since_unix_epoch(0),
324
325 state: RtcState::new(initial_cmos),
326 }
327 }
328
329 pub fn raw_cmos(&mut self) -> &mut [u8; 256] {
331 &mut self.state.cmos.0
332 }
333
334 fn calculate_alarm_duration(&self) -> Duration {
368 use self::spec::ALARM_WILDCARD;
369
370 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
371 let (now_hour, now_min, now_sec) = canonical_hms(
372 status_b,
373 self.state.cmos[CmosReg::HOUR],
374 self.state.cmos[CmosReg::MINUTE],
375 self.state.cmos[CmosReg::SECOND],
376 );
377 let (alarm_hour, alarm_min, alarm_sec) = canonical_hms(
378 status_b,
379 self.state.cmos[CmosReg::HOUR_ALARM],
380 self.state.cmos[CmosReg::MINUTE_ALARM],
381 self.state.cmos[CmosReg::SECOND_ALARM],
382 );
383
384 let mut delta_hour: u8 = 0;
385 let mut delta_min: u8 = 0;
386 let mut delta_sec: u8 = 0;
387
388 if alarm_sec == ALARM_WILDCARD {
389 delta_sec = 0;
390 } else {
391 delta_sec = delta_sec.wrapping_add(alarm_sec.wrapping_sub(now_sec));
392 if alarm_sec < now_sec {
393 delta_sec = delta_sec.wrapping_add(60);
394 delta_min = delta_min.wrapping_sub(1);
395 }
396 }
397
398 if alarm_min == ALARM_WILDCARD {
399 delta_min = 0;
400 } else {
401 delta_min = delta_min.wrapping_add(alarm_min.wrapping_sub(now_min));
402 if alarm_min < now_min {
403 delta_min = delta_min.wrapping_add(60);
404 delta_hour = delta_hour.wrapping_sub(1);
405 }
406 }
407
408 if alarm_hour == ALARM_WILDCARD {
409 delta_hour = 0;
410 } else {
411 delta_hour = delta_hour.wrapping_add(alarm_hour.wrapping_sub(now_hour));
412 if alarm_hour < now_hour {
413 delta_hour = delta_hour.wrapping_add(24);
414 }
415 }
416
417 const DURATION_SEC: Duration = Duration::from_secs(1);
418 const DURATION_MIN: Duration = Duration::from_secs(60);
419 const DURATION_HOUR: Duration = Duration::from_secs(60 * 60);
420 const DURATION_DAY: Duration = Duration::from_secs(60 * 60 * 24);
421
422 let go_around_again = match (alarm_sec, alarm_min, alarm_hour) {
423 (ALARM_WILDCARD, _, _) => DURATION_SEC,
424 (_, ALARM_WILDCARD, _) => DURATION_MIN,
425 (_, _, ALARM_WILDCARD) => DURATION_HOUR,
426 _ => DURATION_DAY,
428 };
429
430 let alarm_duration = {
431 DURATION_HOUR * delta_hour as u32
432 + DURATION_MIN * delta_min as u32
433 + DURATION_SEC * delta_sec as u32
434 };
435
436 tracing::debug!(
437 now = ?(now_hour, now_min, now_sec),
438 alarm = ?(alarm_hour, alarm_min, alarm_sec),
439 delta = ?(delta_hour, delta_min, delta_sec),
440 ?go_around_again,
441 ?alarm_duration,
442 "setting alarm"
443 );
444
445 if alarm_duration.is_zero() {
446 go_around_again
447 } else {
448 alarm_duration
449 }
450 }
451
452 fn set_alarm_timer(&mut self, now: VmTime) {
453 self.sync_clock_to_cmos();
454 let alarm_duration = self.calculate_alarm_duration();
455
456 self.vmtime_alarm
457 .set_timeout(now.wrapping_add(alarm_duration));
458 }
459
460 fn on_alarm_timer(&mut self, now: VmTime) {
461 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
462 self.state.cmos[CmosReg::STATUS_C] =
463 status_c.with_irq_alarm(true).with_irq_combined(true).into();
464
465 self.update_interrupt_line_level();
466
467 self.set_alarm_timer(now)
469 }
470
471 fn set_periodic_timer(&mut self) {
472 use self::spec::PERIODIC_TIMER_RATE_HZ;
473
474 let status_a = StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]);
475
476 if status_a.periodic_timer_rate() == 0 {
478 return;
479 }
480
481 let tick_hz = PERIODIC_TIMER_RATE_HZ[status_a.periodic_timer_rate() as usize - 1];
482 let tick_period = Duration::from_secs_f32(1. / tick_hz as f32);
483
484 tracing::debug!(
485 periodic_timer_rate = ?status_a.periodic_timer_rate(),
486 ?tick_hz,
487 ?tick_period,
488 "setting periodic timer"
489 );
490
491 self.vmtimer_periodic.start(tick_period);
492 }
493
494 fn on_periodic_timer(&mut self) {
495 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
496 self.state.cmos[CmosReg::STATUS_C] = status_c
497 .with_irq_periodic(true)
498 .with_irq_combined(true)
499 .into();
500
501 self.update_interrupt_line_level();
502 }
503
504 fn set_update_timer(&mut self) {
505 self.vmtimer_update.start(Duration::from_secs(1));
506 }
507
508 fn on_update_timer(&mut self) {
509 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
510 self.state.cmos[CmosReg::STATUS_C] = status_c
511 .with_irq_update(true)
512 .with_irq_combined(true)
513 .into();
514
515 self.update_interrupt_line_level();
516 }
517
518 fn update_interrupt_line_level(&self) {
521 let status_c = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
522
523 if status_c.irq_update() || status_c.irq_periodic() || status_c.irq_alarm() {
524 assert!(status_c.irq_combined());
525 if self.enlightened_interrupts {
526 self.interrupt.set_level(false);
527 }
528 self.interrupt.set_level(true);
529 } else {
530 assert!(!status_c.irq_combined());
531 self.interrupt.set_level(false);
532 }
533 }
534
535 fn update_timers(&mut self) {
538 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
539
540 if status_b.irq_enable_alarm() {
541 if self.vmtime_alarm.get_timeout().is_none() {
542 self.set_alarm_timer(self.vmtime_alarm.now())
543 }
544 } else {
545 self.vmtime_alarm.cancel_timeout();
546 }
547
548 if status_b.irq_enable_periodic() {
549 if !self.vmtimer_periodic.is_running() {
550 self.set_periodic_timer()
551 }
552 } else {
553 self.vmtimer_periodic.cancel();
554 }
555
556 if status_b.irq_enable_update() {
557 if !self.vmtimer_update.is_running() {
558 self.set_update_timer()
559 }
560 } else {
561 self.vmtimer_update.cancel();
562 }
563 }
564
565 pub fn set_cmos_byte(&mut self, addr: u8, data: u8) {
571 let addr = CmosReg(addr);
572
573 tracing::trace!(?addr, ?data, "set_cmos_byte");
574
575 if (CmosReg::STATUS_A..=CmosReg::STATUS_D).contains(&addr) {
576 self.set_status_byte(addr, data);
577 } else {
578 let old_data = self.state.cmos[addr];
579
580 if data != old_data {
581 if addr.depends_on_rtc(self.century_reg) {
583 self.sync_clock_to_cmos();
584 }
585
586 self.state.cmos[addr] = data;
587
588 if addr.depends_on_rtc(self.century_reg) {
590 self.sync_cmos_to_clock();
591 }
592
593 if addr.depends_on_alarm() || addr.depends_on_rtc(self.century_reg) {
594 if self.vmtime_alarm.get_timeout().is_some() {
595 self.set_alarm_timer(self.vmtime_alarm.now());
596 }
597 }
598 }
599 }
600 }
601
602 pub fn get_cmos_byte(&mut self, addr: u8) -> u8 {
608 let addr = CmosReg(addr);
609
610 let data = if (CmosReg::STATUS_A..=CmosReg::STATUS_D).contains(&addr) {
611 self.get_status_byte(addr)
612 } else {
613 if addr.depends_on_rtc(self.century_reg) {
614 self.sync_clock_to_cmos();
615 }
616
617 self.state.cmos[addr]
618 };
619
620 tracing::trace!(?addr, ?data, "get_cmos_byte");
621
622 data
623 }
624
625 fn set_status_byte(&mut self, addr: CmosReg, data: u8) {
626 match addr {
627 CmosReg::STATUS_A => {
628 let new_reg = StatusRegA::from(data);
629 let old_reg = StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]);
630
631 if new_reg.oscillator_control() != old_reg.oscillator_control() {
633 self.vmtime_alarm.cancel_timeout();
635 }
636
637 if new_reg.periodic_timer_rate() != old_reg.periodic_timer_rate() {
638 self.vmtimer_periodic.cancel();
640 }
641
642 self.state.cmos[CmosReg::STATUS_A] = data & 0x7F;
644
645 self.update_timers();
646 }
647 CmosReg::STATUS_B => {
648 let mut new_reg = StatusRegB::from(data);
649
650 if new_reg.set() {
652 tracing::debug!("disable timer update and interrupt");
653 new_reg.set_irq_enable_update(false)
654 }
655
656 self.state.cmos[CmosReg::STATUS_B] = new_reg.into();
657
658 self.update_timers();
659 }
660 CmosReg::STATUS_C | CmosReg::STATUS_D => {}
663 _ => unreachable!("passed invalid status reg"),
664 }
665 }
666
667 fn get_status_byte(&mut self, addr: CmosReg) -> u8 {
668 match addr {
669 CmosReg::STATUS_A => {
670 let mut data = StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]);
671
672 if !StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]).set() {
679 let now = self.real_time_source.get_time();
680 let elapsed = now - self.last_update_bit_blip;
681
682 if elapsed.as_millis().is_negative() {
684 tracing::debug!("clock jumped backwards between update bit blips");
685 self.last_update_bit_blip = LocalClockTime::from_millis_since_unix_epoch(0);
686 }
687
688 tracing::trace!(
689 ?self.last_update_bit_blip,
690 ?now,
691 ?elapsed,
692 "get_status_byte"
693 );
694
695 let elapsed: time::Duration = elapsed.into();
696 if elapsed > time::Duration::seconds(1) {
697 data.set_update(true);
699 self.sync_clock_to_cmos();
700 self.last_update_bit_blip = now;
701 tracing::trace!(
702 ?data,
703 ?elapsed,
704 cmos_date_time = ?self.read_cmos_date_time(),
705 "blip'd status a update bit"
706 );
707 }
708 }
709
710 data.into()
711 }
712 CmosReg::STATUS_B => self.state.cmos[CmosReg::STATUS_B],
713 CmosReg::STATUS_C => {
714 let data = StatusRegC::from(self.state.cmos[CmosReg::STATUS_C]);
715
716 tracing::debug!("clearing rtc interrupts");
718 self.state.cmos[CmosReg::STATUS_C] = StatusRegC::new().into();
719 self.update_interrupt_line_level();
720
721 data.into()
722 }
723 CmosReg::STATUS_D => {
724 StatusRegD::new().with_vrt(true).into()
726 }
727 _ => unreachable!("passed invalid status reg"),
728 }
729 }
730
731 fn read_cmos_date_time(&self) -> Result<time::PrimitiveDateTime, time::error::ComponentRange> {
732 let mut sec = self.state.cmos[CmosReg::SECOND];
733 let mut min = self.state.cmos[CmosReg::MINUTE];
734 let mut hour = self.state.cmos[CmosReg::HOUR];
735 let mut day = self.state.cmos[CmosReg::DAY_OF_MONTH];
736 let mut month = self.state.cmos[CmosReg::MONTH];
737 let mut year = self.state.cmos[CmosReg::YEAR];
738 let mut century = self.state.cmos[self.century_reg];
739
740 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
741
742 (hour, min, sec) = canonical_hms(status_b, hour, min, sec);
744 if !status_b.disable_bcd() {
745 (day, month, year, century) = (
746 from_bcd(day),
747 from_bcd(month),
748 from_bcd(year),
749 from_bcd(century),
750 );
751 }
752
753 Ok(time::PrimitiveDateTime::new(
754 time::Date::from_calendar_date(
755 year as i32 + century as i32 * 100,
756 month.try_into()?,
757 day,
758 )?,
759 time::Time::from_hms(hour, min, sec)?,
760 ))
761 }
762
763 fn sync_clock_to_cmos(&mut self) {
766 if StatusRegA::from(self.state.cmos[CmosReg::STATUS_A]).oscillator_control()
767 != ENABLE_OSCILLATOR_CONTROL
768 {
769 tracing::trace!(
771 cmos_reg_status_a = self.state.cmos[CmosReg::STATUS_A],
772 "sync_clock_to_cmos: Oscillator is disabled."
773 );
774
775 return;
776 }
777
778 let real_time = self.real_time_source.get_time();
779 let Ok(clock_time): Result<time::OffsetDateTime, _> = real_time.try_into() else {
780 tracelimit::warn_ratelimited!(
781 ?real_time,
782 "invalid date/time in real_time_source, skipping sync"
783 );
784 return;
785 };
786
787 let status_b = StatusRegB::from(self.state.cmos[CmosReg::STATUS_B]);
788
789 self.state.cmos[CmosReg::SECOND] = clock_time.second();
790 self.state.cmos[CmosReg::MINUTE] = clock_time.minute();
791 self.state.cmos[CmosReg::HOUR] = {
792 let hour = clock_time.hour();
793 if status_b.h24_mode() {
794 hour
795 } else {
796 if clock_time.hour() > 12 {
797 hour - 12
798 } else {
799 hour
800 }
801 }
802 };
803 self.state.cmos[CmosReg::DAY_OF_WEEK] = clock_time.weekday().number_from_sunday();
804 self.state.cmos[CmosReg::DAY_OF_MONTH] = clock_time.day();
805 self.state.cmos[CmosReg::MONTH] = clock_time.month() as u8;
806 self.state.cmos[CmosReg::YEAR] = (clock_time.year() % 100) as u8;
807 self.state.cmos[self.century_reg] = (clock_time.year() / 100) as u8;
808
809 if !status_b.disable_bcd() {
810 let regs = [
811 CmosReg::SECOND,
812 CmosReg::MINUTE,
813 CmosReg::HOUR,
814 CmosReg::DAY_OF_WEEK,
815 CmosReg::DAY_OF_MONTH,
816 CmosReg::MONTH,
817 CmosReg::YEAR,
818 self.century_reg,
819 ];
820
821 for reg in regs {
822 self.state.cmos[reg] = to_bcd(self.state.cmos[reg])
823 }
824 }
825
826 if !status_b.h24_mode() {
827 if clock_time.hour() > 12 {
828 self.state.cmos[CmosReg::HOUR] |= 0x80;
829 }
830 }
831
832 tracing::trace!(
833 cmos_reg_status_b = self.state.cmos[CmosReg::STATUS_B],
834 use_bcd_encoding = ?!status_b.disable_bcd(),
835 use_24h_time = ?status_b.h24_mode(),
836 cmos_date_time = ?self.read_cmos_date_time(),
837 "sync_clock_to_cmos"
838 );
839 }
840
841 fn sync_cmos_to_clock(&mut self) {
844 let cmos_time: time::OffsetDateTime = match self.read_cmos_date_time() {
845 Ok(cmos_time) => cmos_time.assume_utc(),
846 Err(e) => {
847 tracelimit::warn_ratelimited!(?e, "invalid date/time in RTC registers!");
848 return;
849 }
850 };
851 self.real_time_source.set_time(cmos_time.into());
852 }
853
854 #[cfg(test)]
855 fn get_cmos_date_time(
856 &mut self,
857 ) -> Result<time::PrimitiveDateTime, time::error::ComponentRange> {
858 self.sync_clock_to_cmos();
859 self.read_cmos_date_time()
860 }
861}
862
863impl PortIoIntercept for Rtc {
864 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
865 data[0] = match RtcIoPort(io_port) {
869 RtcIoPort::ADDR => self.state.addr,
870 RtcIoPort::DATA => self.get_cmos_byte(self.state.addr),
871 _ => return IoResult::Err(IoError::InvalidRegister),
872 };
873
874 IoResult::Ok
875 }
876
877 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
878 match RtcIoPort(io_port) {
882 RtcIoPort::ADDR => {
883 if data[0] & 0x7F != data[0] {
884 tracing::debug!("guest tried to set high-bit in CMOS addr register")
885 }
886
887 self.state.addr = data[0] & 0x7F
888 }
889 RtcIoPort::DATA => self.set_cmos_byte(self.state.addr, data[0]),
890 _ => return IoResult::Err(IoError::InvalidRegister),
891 }
892
893 IoResult::Ok
894 }
895
896 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
897 &[("io", (RtcIoPort::ADDR.0)..=(RtcIoPort::DATA.0))]
898 }
899}
900
901impl PollDevice for Rtc {
902 fn poll_device(&mut self, cx: &mut std::task::Context<'_>) {
903 while let Poll::Ready(now) = self.vmtime_alarm.poll_timeout(cx) {
904 self.on_alarm_timer(now)
905 }
906
907 if let Poll::Ready(_now) = self.vmtimer_periodic.poll_timeout(cx) {
908 self.on_periodic_timer()
909 }
910
911 if let Poll::Ready(_now) = self.vmtimer_update.poll_timeout(cx) {
912 self.on_update_timer()
913 }
914 }
915}
916
917mod save_restore {
918 use super::*;
919 use vmcore::save_restore::RestoreError;
920 use vmcore::save_restore::SaveError;
921 use vmcore::save_restore::SaveRestore;
922
923 mod state {
924 use mesh::payload::Protobuf;
925 use vmcore::save_restore::SavedStateRoot;
926
927 #[derive(Protobuf, SavedStateRoot)]
929 #[mesh(package = "chipset.cmos_rtc")]
930 pub struct SavedState {
931 #[mesh(1)]
932 pub addr: u8,
933 #[mesh(2)]
934 pub cmos: [u8; 256],
935 }
936 }
937
938 impl SaveRestore for Rtc {
939 type SavedState = state::SavedState;
940
941 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
942 let RtcState { addr, ref cmos } = self.state;
943
944 let saved_state = state::SavedState { addr, cmos: cmos.0 };
945
946 Ok(saved_state)
947 }
948
949 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
950 let state::SavedState { addr, cmos } = state;
951
952 self.state = RtcState {
953 addr,
954 cmos: CmosData(cmos),
955 };
956
957 self.update_timers();
958 self.update_interrupt_line_level();
959
960 Ok(())
961 }
962 }
963}
964
965#[cfg(test)]
966mod tests {
967 use super::*;
968 use local_clock::MockLocalClock;
969 use local_clock::MockLocalClockAccessor;
970 use test_with_tracing::test;
971
972 fn new_test_rtc() -> (
973 pal_async::DefaultPool,
974 vmcore::vmtime::VmTimeKeeper,
975 MockLocalClockAccessor,
976 Rtc,
977 ) {
978 let mut pool = pal_async::DefaultPool::new();
979 let driver = pool.driver();
980 let vm_time_keeper =
981 vmcore::vmtime::VmTimeKeeper::new(&pool.driver(), VmTime::from_100ns(0));
982 let vm_time_source = pool
983 .run_until(vm_time_keeper.builder().build(&driver))
984 .unwrap();
985
986 let time = MockLocalClock::new();
987 let time_access = time.accessor();
988
989 let rtc = Rtc::new(
990 Box::new(time),
991 LineInterrupt::detached(),
992 &vm_time_source,
993 0x32,
994 None,
995 false,
996 );
997
998 (pool, vm_time_keeper, time_access, rtc)
999 }
1000
1001 fn get_cmos_data(rtc: &mut Rtc, addr: CmosReg) -> u8 {
1002 let mut temp = [addr.0];
1003 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1004 rtc.io_read(RtcIoPort::DATA.0, &mut temp).unwrap();
1005 temp[0]
1006 }
1007
1008 fn set_cmos_data(rtc: &mut Rtc, addr: CmosReg, data: u8) {
1009 let mut temp = [addr.0];
1010 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1011 temp[0] = data;
1012 rtc.io_write(RtcIoPort::DATA.0, &temp).unwrap();
1013 }
1014
1015 fn get_rtc_data(rtc: &mut Rtc, addr: CmosReg, bcd: bool, hour24: bool) -> u8 {
1016 let mut temp = [addr.0];
1017 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1018 rtc.io_read(RtcIoPort::DATA.0, &mut temp).unwrap();
1019 let mut data = temp[0];
1020 if addr == CmosReg::HOUR {
1021 let pm = if hour24 { false } else { (data & 0x80) != 0 };
1022 if pm {
1023 data &= 0x7f
1024 };
1025 if bcd {
1026 data = from_bcd(data)
1027 };
1028 if pm {
1029 data += 12;
1030 }
1031 } else {
1032 if bcd {
1033 data = from_bcd(data)
1034 };
1035 }
1036
1037 println!("get {0}({0:#x}) convert to {1}", temp[0], data);
1038 data
1039 }
1040
1041 fn set_rtc_data(rtc: &mut Rtc, addr: CmosReg, data: u8, bcd: bool, hour24: bool) {
1042 let mut temp = [addr.0];
1043 rtc.io_write(RtcIoPort::ADDR.0, &temp).unwrap();
1044 let mut new_data = data;
1045 if addr == CmosReg::HOUR {
1046 let pm = if hour24 { false } else { new_data > 12 };
1047 if bcd {
1048 new_data =
1049 to_bcd(if pm { new_data - 12 } else { new_data }) | if pm { 0x80 } else { 0 };
1050 } else {
1051 if pm {
1052 new_data = (new_data - 12) | 0x80;
1053 }
1054 }
1055 } else {
1056 if bcd {
1057 new_data = to_bcd(new_data)
1058 };
1059 }
1060
1061 println!("set {0} convert to {1}({1:#x})", data, new_data);
1062 temp[0] = new_data;
1063 rtc.io_write(RtcIoPort::DATA.0, &temp).unwrap();
1064 }
1065
1066 fn wait_for_edge(rtc: &mut Rtc, high: bool, time: MockLocalClockAccessor) -> bool {
1069 let limit = 5; let stall_ms = 10; for _i in 0..(limit * 1000 / stall_ms) {
1073 let value = get_cmos_data(rtc, CmosReg::STATUS_A);
1074 if high {
1075 if value & 0x80 != 0 {
1076 return true;
1077 }
1078 } else {
1079 if value & 0x80 == 0 {
1080 return true;
1081 }
1082 }
1083
1084 time.tick(Duration::from_millis(stall_ms));
1085 }
1086
1087 false
1088 }
1089
1090 fn set_bcd(rtc: &mut Rtc) {
1091 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1092 value &= 0xFB;
1093 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1094 }
1095
1096 fn set_binary(rtc: &mut Rtc) {
1097 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1098 value |= 0x4;
1099 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1100 }
1101
1102 fn set_24hour(rtc: &mut Rtc) {
1103 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1104 value |= 0x2;
1105 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1106 }
1107
1108 fn set_12hour(rtc: &mut Rtc) {
1109 let mut value = get_cmos_data(rtc, CmosReg::STATUS_B);
1110 value &= 0xFD;
1111 set_cmos_data(rtc, CmosReg::STATUS_B, value);
1112 }
1113
1114 #[test]
1115 fn test_setup() {
1116 let (_, _, _, _rtc) = new_test_rtc();
1117 }
1118
1119 #[test]
1120 fn test_default() {
1121 let default_state = RtcState::new(None);
1122
1123 let (_, _, _, mut rtc) = new_test_rtc();
1124
1125 let mut data = [0];
1126 rtc.io_read(RtcIoPort::ADDR.0, &mut data).unwrap();
1127 assert_eq!(data[0], 0x80);
1128 assert_eq!(
1129 get_cmos_data(&mut rtc, CmosReg::STATUS_A),
1130 default_state.cmos[CmosReg::STATUS_A] | 0x80
1131 );
1132 assert_eq!(
1133 get_cmos_data(&mut rtc, CmosReg::STATUS_B),
1134 default_state.cmos[CmosReg::STATUS_B]
1135 );
1136 assert_eq!(
1137 get_cmos_data(&mut rtc, CmosReg::STATUS_C),
1138 default_state.cmos[CmosReg::STATUS_C]
1139 );
1140 assert_eq!(
1141 get_cmos_data(&mut rtc, CmosReg::STATUS_D),
1142 default_state.cmos[CmosReg::STATUS_D]
1143 );
1144 }
1145
1146 fn test_time_move(rtc: &mut Rtc, is_move: bool, time: MockLocalClockAccessor) {
1147 if let Ok(before) = rtc.get_cmos_date_time() {
1148 time.tick(Duration::from_secs(2));
1149 if let Ok(after) = rtc.get_cmos_date_time() {
1150 if is_move {
1151 assert_ne!(before, after);
1152 } else {
1153 assert_eq!(before, after);
1154 }
1155 } else {
1156 panic!("get_cmos_date_time failed");
1157 }
1158 } else {
1159 panic!("get_cmos_date_time failed");
1160 }
1161 }
1162
1163 #[test]
1164 fn test_oscillator() {
1165 let default_state = RtcState::new(None);
1166
1167 let (_, _, time, mut rtc) = new_test_rtc();
1168
1169 assert_eq!(
1170 get_cmos_data(&mut rtc, CmosReg::STATUS_A),
1171 default_state.cmos[CmosReg::STATUS_A] | 0x80
1172 );
1173
1174 println!("RTC should move forward when oscillator is enabled (default control mask 010)");
1175 test_time_move(&mut rtc, true, time.clone());
1176
1177 set_cmos_data(&mut rtc, CmosReg::STATUS_A, 0x66);
1179 println!("RTC should not move forward when oscillator is disabled (control mask 110)");
1180 test_time_move(&mut rtc, false, time.clone());
1181
1182 set_cmos_data(&mut rtc, CmosReg::STATUS_A, 0x26);
1184 println!("RTC should move forward when oscillator is re-enabled (control mask 010)");
1185 test_time_move(&mut rtc, true, time);
1186 }
1187
1188 #[test]
1189 fn test_uip() {
1190 let (_, _, time, mut rtc) = new_test_rtc();
1191
1192 assert!(
1193 wait_for_edge(&mut rtc, false, time.clone())
1194 && wait_for_edge(&mut rtc, true, time.clone())
1195 );
1196 if let Ok(start) = rtc.get_cmos_date_time() {
1197 let seconds_to_wait: i64 = 10;
1198 for _i in 0..seconds_to_wait {
1199 assert!(
1200 wait_for_edge(&mut rtc, false, time.clone())
1201 && wait_for_edge(&mut rtc, true, time.clone())
1202 );
1203 }
1204
1205 if let Ok(end) = rtc.get_cmos_date_time() {
1206 let elapsed = end - start;
1207 let expected = Duration::from_secs(seconds_to_wait as u64);
1208 let allowance = Duration::from_secs(1);
1209 println!(
1210 "Expected: {:?} Start: {:?} End: {:?} Elapsed: {:?} Allowance: {:?}, RTC generates update strobe at expected rate.",
1211 expected, start, end, elapsed, allowance
1212 );
1213 assert!(elapsed <= (expected + allowance) && elapsed >= (expected - allowance));
1214 } else {
1215 panic!("get_cmos_date_time failed");
1216 }
1217 } else {
1218 panic!("get_cmos_date_time failed");
1219 }
1220 }
1221
1222 #[test]
1223 fn test_readonly() {
1224 let default_state = RtcState::new(None);
1225
1226 let (_, _, _, mut rtc) = new_test_rtc();
1227
1228 assert_eq!(
1229 get_cmos_data(&mut rtc, CmosReg::STATUS_D),
1230 default_state.cmos[CmosReg::STATUS_D]
1231 );
1232
1233 for i in 0..=0xFF {
1235 set_cmos_data(&mut rtc, CmosReg::STATUS_D, i);
1236 assert_eq!(
1237 get_cmos_data(&mut rtc, CmosReg::STATUS_D),
1238 default_state.cmos[CmosReg::STATUS_D]
1239 );
1240 }
1241 }
1242
1243 #[test]
1244 fn test_writeable() {
1245 let (_, _, _, mut rtc) = new_test_rtc();
1246
1247 for i in (0x0F..=0x7F).map(CmosReg) {
1249 if i == rtc.century_reg {
1250 continue;
1251 }
1252
1253 set_cmos_data(&mut rtc, i, 0xFF);
1254 assert_eq!(get_cmos_data(&mut rtc, i), 0xFF);
1255 set_cmos_data(&mut rtc, i, 0);
1256 assert_eq!(get_cmos_data(&mut rtc, i), 0);
1257 }
1258 }
1259
1260 fn test_count(bcd: bool, hour24: bool) {
1261 let (_, _, time, mut rtc) = new_test_rtc();
1262
1263 if bcd {
1264 set_bcd(&mut rtc);
1265 } else {
1266 set_binary(&mut rtc);
1267 }
1268
1269 if hour24 {
1270 set_24hour(&mut rtc);
1271 } else {
1272 set_12hour(&mut rtc);
1273 }
1274
1275 let init: time::OffsetDateTime = time.get_time().try_into().unwrap();
1276 println!("init: {:?}", init);
1277 set_rtc_data(&mut rtc, CmosReg::HOUR, 11, bcd, hour24);
1278 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1279 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1280 time.tick(Duration::from_secs(2));
1281 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1282 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1283 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 12);
1284 assert_eq!(
1285 get_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, bcd, hour24),
1286 init.day()
1287 );
1288 assert_eq!(
1289 get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24),
1290 init.weekday().number_from_sunday()
1291 );
1292
1293 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1294 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1295 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1296 time.tick(Duration::from_secs(2));
1297 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1298 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1299 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1300 let temp = init + time::Duration::days(1);
1301 assert_eq!(
1302 get_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, bcd, hour24),
1303 temp.day()
1304 );
1305 assert_eq!(
1306 get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24),
1307 temp.weekday().number_from_sunday()
1308 );
1309
1310 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1311 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1312 time.tick(Duration::from_secs(2));
1313 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1314 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1315 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 1);
1316 }
1317
1318 #[test]
1319 fn test_bcd_binary() {
1320 println!("----Testing BCD mode...");
1321 test_count(true, true);
1322 println!("----Testing Binary mode...");
1323 test_count(false, true);
1324 }
1325
1326 #[test]
1327 fn test_hour_mode() {
1328 println!("----Testing Binary + 12 hour mode...");
1329 test_count(false, false);
1330 println!("----Testing Binary + 24 hour mode...");
1331 test_count(false, true);
1332 println!("----Testing BCD + 12 hour mode...");
1333 test_count(true, false);
1334 println!("----Testing BCD + 24 hour mode...");
1335 test_count(true, true);
1336 }
1337
1338 fn test_day_of_month(bcd: bool, hour24: bool) {
1339 let century_reg_idx = 0x32;
1340 let century_reg = CmosReg(century_reg_idx);
1341
1342 let (_, _, time, mut rtc) = new_test_rtc();
1343
1344 if bcd {
1345 set_bcd(&mut rtc);
1346 } else {
1347 set_binary(&mut rtc);
1348 }
1349
1350 if hour24 {
1351 set_24hour(&mut rtc);
1352 } else {
1353 set_12hour(&mut rtc);
1354 }
1355
1356 for month in 1..=12 {
1357 let mut day;
1358 let mut year_check_count = 1;
1359 if month == 4 || month == 6 || month == 9 || month == 11 {
1360 day = 30;
1361 } else if month != 2 {
1362 day = 31;
1363 } else {
1364 day = 0;
1365 year_check_count = 4;
1366 }
1367
1368 while year_check_count > 0 {
1369 let century = 30;
1370 let year = 4 + year_check_count;
1371 if month == 2 {
1372 day = if (year & 3) > 0 { 28 } else { 29 };
1373 }
1374
1375 println!(
1376 "----Testing {:02}{:02}-{:02}-{:02}",
1377 century, year, month, day
1378 );
1379 set_rtc_data(&mut rtc, century_reg, century, bcd, hour24);
1380 set_rtc_data(&mut rtc, CmosReg::YEAR, year, bcd, hour24);
1381 set_rtc_data(&mut rtc, CmosReg::MONTH, month, bcd, hour24);
1382 set_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, day, bcd, hour24);
1383 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1384 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1385 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1386 time.tick(Duration::from_secs(2));
1387 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1388 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1389 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1390 assert_eq!(
1391 get_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, bcd, hour24),
1392 1
1393 );
1394 assert_eq!(
1395 get_rtc_data(&mut rtc, CmosReg::MONTH, bcd, hour24),
1396 if month == 12 { 1 } else { month + 1 }
1397 );
1398 assert_eq!(
1399 get_rtc_data(&mut rtc, CmosReg::YEAR, bcd, hour24),
1400 if month < 12 { year } else { year + 1 }
1401 );
1402 assert_eq!(get_rtc_data(&mut rtc, century_reg, bcd, hour24), century);
1403 year_check_count -= 1;
1404 }
1405 }
1406 }
1407
1408 #[test]
1409 fn test_month() {
1410 println!("----Testing BCD mode...");
1411 test_day_of_month(true, true);
1412 println!("----Testing Binary mode...");
1413 test_day_of_month(false, true);
1414 }
1415
1416 fn test_day_of_week(bcd: bool, hour24: bool) {
1417 let century_reg_idx = 0x32;
1418 let century_reg = CmosReg(century_reg_idx);
1419
1420 let (_, _, time, mut rtc) = new_test_rtc();
1421
1422 if bcd {
1423 set_bcd(&mut rtc);
1424 } else {
1425 set_binary(&mut rtc);
1426 }
1427
1428 if hour24 {
1429 set_24hour(&mut rtc);
1430 } else {
1431 set_12hour(&mut rtc);
1432 }
1433
1434 let century = 30;
1435 let year = 5;
1436 let month = 1;
1437 let day = 5;
1438
1439 println!(
1440 "----Testing {:02}{:02}-{:02}-{:02}",
1441 century, year, month, day
1442 );
1443 set_rtc_data(&mut rtc, century_reg, century, bcd, hour24);
1444 set_rtc_data(&mut rtc, CmosReg::YEAR, year, bcd, hour24);
1445 set_rtc_data(&mut rtc, CmosReg::MONTH, month, bcd, hour24);
1446 set_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, day, bcd, hour24);
1447 set_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, 7, bcd, hour24);
1448 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1449 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1450 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1451 time.tick(Duration::from_secs(2));
1452 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1453 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1454 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1455 assert_eq!(get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24), 1);
1456
1457 set_rtc_data(&mut rtc, CmosReg::DAY_OF_MONTH, day - 1, bcd, hour24);
1458 set_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, 6, bcd, hour24);
1459 set_rtc_data(&mut rtc, CmosReg::HOUR, 23, bcd, hour24);
1460 set_rtc_data(&mut rtc, CmosReg::MINUTE, 59, bcd, hour24);
1461 set_rtc_data(&mut rtc, CmosReg::SECOND, 59, bcd, hour24);
1462 time.tick(Duration::from_secs(2));
1463 assert_eq!(get_rtc_data(&mut rtc, CmosReg::SECOND, bcd, hour24), 1);
1464 assert_eq!(get_rtc_data(&mut rtc, CmosReg::MINUTE, bcd, hour24), 0);
1465 assert_eq!(get_rtc_data(&mut rtc, CmosReg::HOUR, bcd, hour24), 0);
1466 assert_eq!(get_rtc_data(&mut rtc, CmosReg::DAY_OF_WEEK, bcd, hour24), 7);
1467 }
1468
1469 #[test]
1470 fn test_week() {
1471 println!("----Testing Binary mode...");
1472 test_day_of_week(false, true);
1473 println!("----Testing BCD mode...");
1474 test_day_of_week(true, true);
1475 }
1476
1477 #[test]
1478 fn test_alarm() {
1479 fn hms_to_duration(h: u8, m: u8, s: u8) -> Duration {
1480 Duration::from_secs(s as u64 + 60 * m as u64 + h as u64 * 60 * 60)
1481 }
1482
1483 let (_, _, _time, mut rtc) = new_test_rtc();
1484
1485 set_binary(&mut rtc);
1486
1487 set_cmos_data(&mut rtc, CmosReg::HOUR, 2);
1488 set_cmos_data(&mut rtc, CmosReg::MINUTE, 2);
1489 set_cmos_data(&mut rtc, CmosReg::SECOND, 2);
1490
1491 set_cmos_data(&mut rtc, CmosReg::HOUR_ALARM, 3);
1492 set_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM, 3);
1493 set_cmos_data(&mut rtc, CmosReg::SECOND_ALARM, 3);
1494
1495 assert_eq!(rtc.calculate_alarm_duration(), hms_to_duration(1, 1, 1));
1496 set_cmos_data(&mut rtc, CmosReg::HOUR_ALARM, 0xff);
1497 set_cmos_data(&mut rtc, CmosReg::MINUTE_ALARM, 0xff);
1498 set_cmos_data(&mut rtc, CmosReg::SECOND_ALARM, 0xff);
1499 assert_eq!(rtc.calculate_alarm_duration(), hms_to_duration(0, 0, 1));
1500
1501 }
1503}