diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2019-01-31 11:05:13 -0500 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2020-04-11 18:02:26 +0200 |
commit | 6984a7ce1535422891ce4de57070cc55a486d352 (patch) | |
tree | 1f585e50cac0dd3ef4b713c2156d6c501b82f7bf /firmware/target/hosted/rtc.c | |
parent | 02d347bc6f6e37ead72986436a6fffdeafeec484 (diff) | |
download | rockbox-6984a7ce1535422891ce4de57070cc55a486d352.tar.gz rockbox-6984a7ce1535422891ce4de57070cc55a486d352.zip |
RTC: Add support RTC alarms on hosted targets
Only AGPTeck Rocker is enabled for now, and it doesn't work properly:
* No generic way to determine wakeup reason under Linux
* No generic way to be asynchronously notified if the alarm is
triggered when we're already awake
* Shutting down may clobber RTC wakeup (driver/etc dependent)
And finally:
* AGPTek kernel's RTC driver has some 24h clock and
some timezone-related issues.
So, the infrastructure is arguably useful, but the only applicable
hardware I have is pathologically brain-dead.
Change-Id: Iac6a26a9b6e4efec5d0b3030b87f456eb23fc01d
Diffstat (limited to 'firmware/target/hosted/rtc.c')
-rw-r--r-- | firmware/target/hosted/rtc.c | 154 |
1 files changed, 146 insertions, 8 deletions
diff --git a/firmware/target/hosted/rtc.c b/firmware/target/hosted/rtc.c index 488531c77c..ced298a5c8 100644 --- a/firmware/target/hosted/rtc.c +++ b/firmware/target/hosted/rtc.c | |||
@@ -27,9 +27,14 @@ | |||
27 | #include <linux/rtc.h> | 27 | #include <linux/rtc.h> |
28 | #include <fcntl.h> | 28 | #include <fcntl.h> |
29 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #include <stdbool.h> | ||
30 | #endif | 31 | #endif |
32 | |||
33 | #include "config.h" | ||
34 | |||
31 | void rtc_init(void) | 35 | void rtc_init(void) |
32 | { | 36 | { |
37 | tzset(); | ||
33 | } | 38 | } |
34 | 39 | ||
35 | int rtc_read_datetime(struct tm *tm) | 40 | int rtc_read_datetime(struct tm *tm) |
@@ -46,8 +51,6 @@ int rtc_write_datetime(const struct tm *tm) | |||
46 | struct timeval tv; | 51 | struct timeval tv; |
47 | struct tm *tm_time; | 52 | struct tm *tm_time; |
48 | 53 | ||
49 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
50 | |||
51 | tv.tv_sec = mktime((struct tm *)tm); | 54 | tv.tv_sec = mktime((struct tm *)tm); |
52 | tv.tv_usec = 0; | 55 | tv.tv_usec = 0; |
53 | 56 | ||
@@ -58,12 +61,147 @@ int rtc_write_datetime(const struct tm *tm) | |||
58 | time_t now = time(NULL); | 61 | time_t now = time(NULL); |
59 | tm_time = gmtime(&now); | 62 | tm_time = gmtime(&now); |
60 | 63 | ||
61 | ioctl(rtc, RTC_SET_TIME, (struct rtc_time *)tm_time); | 64 | /* Try to write the HW RTC, if present. */ |
62 | close(rtc); | 65 | int rtc = open("/dev/rtc0", O_WRONLY); |
63 | 66 | if (rtc > 0) { | |
64 | return 0; | 67 | ioctl(rtc, RTC_SET_TIME, (struct rtc_time *)tm_time); |
68 | close(rtc); | ||
69 | } | ||
65 | #else | 70 | #else |
66 | (void)tm; | 71 | (void)(*tm); |
67 | return -1; | ||
68 | #endif | 72 | #endif |
73 | return 0; | ||
74 | } | ||
75 | |||
76 | #if defined(HAVE_RTC_ALARM) && !defined(SIMULATOR) | ||
77 | void rtc_set_alarm(int h, int m) | ||
78 | { | ||
79 | struct rtc_time tm; | ||
80 | long sec; | ||
81 | |||
82 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
83 | if (rtc < 0) | ||
84 | return; | ||
85 | |||
86 | /* Get RTC time */ | ||
87 | ioctl(rtc, RTC_RD_TIME, &tm); | ||
88 | |||
89 | /* Convert to seconds into the GMT day. Can be negative! */ | ||
90 | sec = h * 3600 + m * 60 + timezone; | ||
91 | h = sec / 3600; | ||
92 | sec -= h * 3600; | ||
93 | m = sec / 60; | ||
94 | |||
95 | /* Handle negative or positive wraps */ | ||
96 | while (m < 0) { | ||
97 | m += 60; | ||
98 | h--; | ||
99 | } | ||
100 | while (m > 59) { | ||
101 | m -= 60; | ||
102 | h++; | ||
103 | } | ||
104 | while (h < 0) { | ||
105 | h += 24; | ||
106 | tm.tm_mday--; | ||
107 | } | ||
108 | while (h > 23) { | ||
109 | h -= 24; | ||
110 | tm.tm_mday++; | ||
111 | } | ||
112 | |||
113 | /* Update the struct */ | ||
114 | tm.tm_sec = 0; | ||
115 | tm.tm_hour = h; | ||
116 | tm.tm_min = m; | ||
117 | |||
118 | ioctl(rtc, RTC_ALM_SET, &tm); | ||
119 | close(rtc); | ||
120 | } | ||
121 | |||
122 | void rtc_get_alarm(int *h, int *m) | ||
123 | { | ||
124 | struct rtc_time tm; | ||
125 | long sec; | ||
126 | |||
127 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
128 | if (rtc < 0) | ||
129 | return; | ||
130 | |||
131 | ioctl(rtc, RTC_ALM_READ, &tm); | ||
132 | close(rtc); | ||
133 | |||
134 | /* Convert RTC from UTC to local time zone.. */ | ||
135 | sec = (tm.tm_min * 60) + (tm.tm_hour * 3600) - timezone; | ||
136 | |||
137 | /* Handle wrapping and negative offsets */ | ||
138 | *h = (sec / 3600); | ||
139 | sec -= *h * 3600; | ||
140 | *m = sec / 60; | ||
141 | |||
142 | while (*m < 0) { | ||
143 | *m = *m + 60; | ||
144 | *h = *h - 1; | ||
145 | } | ||
146 | while (*m > 59) { | ||
147 | *m = *m - 60; | ||
148 | *h = *h + 1; | ||
149 | } | ||
150 | while (*h < 0) { | ||
151 | *h = *h + 24; | ||
152 | } | ||
153 | while (*h > 23) { | ||
154 | *h = *h - 24; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | void rtc_enable_alarm(bool enable) | ||
159 | { | ||
160 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
161 | if (rtc < 0) | ||
162 | return; | ||
163 | |||
164 | ioctl(rtc, enable ? RTC_AIE_ON : RTC_AIE_OFF, NULL); | ||
165 | close(rtc); | ||
166 | |||
167 | /* XXX Note that this may or may not work; Linux may need to be suspended | ||
168 | or shut down in a special way to keep the RTC alarm active */ | ||
69 | } | 169 | } |
170 | |||
171 | /* Returns true if alarm was the reason we started up */ | ||
172 | bool rtc_check_alarm_started(bool release_alarm) | ||
173 | { | ||
174 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
175 | if (rtc < 0) | ||
176 | return false; | ||
177 | |||
178 | /* XXX There is no generic way of determining wakeup reason. Will | ||
179 | likely need a target-specific hook. */ | ||
180 | |||
181 | /* Disable alarm if requested */ | ||
182 | if (release_alarm) | ||
183 | ioctl(rtc, RTC_AIE_OFF, NULL); | ||
184 | |||
185 | close(rtc); | ||
186 | return false; | ||
187 | } | ||
188 | |||
189 | /* See if we received an alarm. */ | ||
190 | bool rtc_check_alarm_flag(void) | ||
191 | { | ||
192 | struct rtc_wkalrm alrm; | ||
193 | |||
194 | int rtc = open("/dev/rtc0", O_WRONLY); | ||
195 | if (rtc < 0) | ||
196 | return false; | ||
197 | |||
198 | alrm.pending = 0; | ||
199 | /* XXX Documented as "mostly useless on Linux" except with EFI RTCs | ||
200 | Will likely need a target-specific hook. */ | ||
201 | ioctl(rtc, RTC_WKALM_RD, &alrm); | ||
202 | close(rtc); | ||
203 | |||
204 | return alrm.pending; | ||
205 | } | ||
206 | |||
207 | #endif /* HAVE_RTC_ALARM */ | ||