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