1use anyhow::Error;
2use chrono::{Datelike, Duration, Timelike, Utc};
3use nix::{ioctl_none, ioctl_read, ioctl_write_ptr};
4use std::fs::File;
5use std::mem;
6use std::os::unix::io::AsRawFd;
7use std::path::Path;
8
9ioctl_read!(rtc_read_alarm, b'p', 0x10, RtcWkalrm);
10ioctl_write_ptr!(rtc_write_alarm, b'p', 0x0f, RtcWkalrm);
11ioctl_none!(rtc_disable_alarm, b'p', 0x02);
12
13#[repr(C)]
14#[derive(Debug, Clone)]
15pub struct RtcTime {
16 tm_sec: libc::c_int,
17 tm_min: libc::c_int,
18 tm_hour: libc::c_int,
19 tm_mday: libc::c_int,
20 tm_mon: libc::c_int,
21 tm_year: libc::c_int,
22 tm_wday: libc::c_int,
23 tm_yday: libc::c_int,
24 tm_isdst: libc::c_int,
25}
26
27impl Default for RtcWkalrm {
28 fn default() -> Self {
29 unsafe { mem::zeroed() }
30 }
31}
32
33#[repr(C)]
34#[derive(Debug, Clone)]
35pub struct RtcWkalrm {
36 enabled: libc::c_uchar,
37 pending: libc::c_uchar,
38 time: RtcTime,
39}
40
41impl RtcTime {
42 fn year(&self) -> i32 {
43 1900 + self.tm_year as i32
44 }
45}
46
47impl RtcWkalrm {
48 pub fn enabled(&self) -> bool {
49 self.enabled == 1
50 }
51
52 pub fn year(&self) -> i32 {
53 self.time.year()
54 }
55}
56
57pub struct Rtc(File);
58
59impl Rtc {
60 pub fn new<P: AsRef<Path>>(path: P) -> Result<Rtc, Error> {
61 let file = File::open(path)?;
62 Ok(Rtc(file))
63 }
64
65 pub fn alarm(&self) -> Result<RtcWkalrm, Error> {
66 let mut rwa = RtcWkalrm::default();
67 unsafe {
68 rtc_read_alarm(self.0.as_raw_fd(), &mut rwa)
69 .map(|_| rwa)
70 .map_err(|e| e.into())
71 }
72 }
73
74 pub fn set_alarm(&self, days: f32) -> Result<i32, Error> {
75 let wt = Utc::now() + Duration::seconds((86_400.0 * days) as i64);
76 let rwa = RtcWkalrm {
77 enabled: 1,
78 pending: 0,
79 time: RtcTime {
80 tm_sec: wt.second() as libc::c_int,
81 tm_min: wt.minute() as libc::c_int,
82 tm_hour: wt.hour() as libc::c_int,
83 tm_mday: wt.day() as libc::c_int,
84 tm_mon: wt.month0() as libc::c_int,
85 tm_year: (wt.year() - 1900) as libc::c_int,
86 tm_wday: -1,
87 tm_yday: -1,
88 tm_isdst: -1,
89 },
90 };
91 unsafe { rtc_write_alarm(self.0.as_raw_fd(), &rwa).map_err(|e| e.into()) }
92 }
93
94 pub fn disable_alarm(&self) -> Result<i32, Error> {
95 unsafe { rtc_disable_alarm(self.0.as_raw_fd()).map_err(|e| e.into()) }
96 }
97}