1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Real Time Clock Driver Test Program
4 *
5 * Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com>
6 */
7
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <linux/rtc.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/ioctl.h>
14 #include <sys/stat.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <time.h>
18 #include <unistd.h>
19
20 #include "../kselftest_harness.h"
21
22 #define NUM_UIE 3
23 #define ALARM_DELTA 3
24
25 static char *rtc_file = "/dev/rtc0";
26
FIXTURE(rtc)27 FIXTURE(rtc) {
28 int fd;
29 };
30
FIXTURE_SETUP(rtc)31 FIXTURE_SETUP(rtc) {
32 self->fd = open(rtc_file, O_RDONLY);
33 ASSERT_NE(-1, self->fd);
34 }
35
FIXTURE_TEARDOWN(rtc)36 FIXTURE_TEARDOWN(rtc) {
37 close(self->fd);
38 }
39
TEST_F(rtc,date_read)40 TEST_F(rtc, date_read) {
41 int rc;
42 struct rtc_time rtc_tm;
43
44 /* Read the RTC time/date */
45 rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
46 ASSERT_NE(-1, rc);
47
48 TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
49 rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
50 rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
51 }
52
53 #ifndef __ANDROID__ // b/31578457
54 TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
55 int i, rc, irq = 0;
56 unsigned long data;
57
58 /* Turn on update interrupts */
59 rc = ioctl(self->fd, RTC_UIE_ON, 0);
60 if (rc == -1) {
61 ASSERT_EQ(EINVAL, errno);
62 TH_LOG("skip update IRQs not supported.");
63 return;
64 }
65
66 for (i = 0; i < NUM_UIE; i++) {
67 /* This read will block */
68 rc = read(self->fd, &data, sizeof(data));
69 ASSERT_NE(-1, rc);
70 irq++;
71 }
72
73 EXPECT_EQ(NUM_UIE, irq);
74
75 rc = ioctl(self->fd, RTC_UIE_OFF, 0);
76 ASSERT_NE(-1, rc);
77 }
78
TEST_F(rtc,uie_select)79 TEST_F(rtc, uie_select) {
80 int i, rc, irq = 0;
81 unsigned long data;
82
83 /* Turn on update interrupts */
84 rc = ioctl(self->fd, RTC_UIE_ON, 0);
85 if (rc == -1) {
86 ASSERT_EQ(EINVAL, errno);
87 TH_LOG("skip update IRQs not supported.");
88 return;
89 }
90
91 for (i = 0; i < NUM_UIE; i++) {
92 struct timeval tv = { .tv_sec = 2 };
93 fd_set readfds;
94
95 FD_ZERO(&readfds);
96 FD_SET(self->fd, &readfds);
97 /* The select will wait until an RTC interrupt happens. */
98 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
99 ASSERT_NE(-1, rc);
100 ASSERT_NE(0, rc);
101
102 /* This read won't block */
103 rc = read(self->fd, &data, sizeof(unsigned long));
104 ASSERT_NE(-1, rc);
105 irq++;
106 }
107
108 EXPECT_EQ(NUM_UIE, irq);
109
110 rc = ioctl(self->fd, RTC_UIE_OFF, 0);
111 ASSERT_NE(-1, rc);
112 }
113
TEST_F(rtc,alarm_alm_set)114 TEST_F(rtc, alarm_alm_set) {
115 struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
116 unsigned long data;
117 struct rtc_time tm;
118 fd_set readfds;
119 time_t secs, new;
120 int rc;
121
122 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
123 ASSERT_NE(-1, rc);
124
125 secs = timegm((struct tm *)&tm) + ALARM_DELTA;
126 gmtime_r(&secs, (struct tm *)&tm);
127
128 rc = ioctl(self->fd, RTC_ALM_SET, &tm);
129 if (rc == -1) {
130 ASSERT_EQ(EINVAL, errno);
131 TH_LOG("skip alarms are not supported.");
132 return;
133 }
134
135 rc = ioctl(self->fd, RTC_ALM_READ, &tm);
136 ASSERT_NE(-1, rc);
137
138 TH_LOG("Alarm time now set to %02d:%02d:%02d.",
139 tm.tm_hour, tm.tm_min, tm.tm_sec);
140
141 /* Enable alarm interrupts */
142 rc = ioctl(self->fd, RTC_AIE_ON, 0);
143 ASSERT_NE(-1, rc);
144
145 FD_ZERO(&readfds);
146 FD_SET(self->fd, &readfds);
147
148 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
149 ASSERT_NE(-1, rc);
150 ASSERT_NE(0, rc);
151
152 /* Disable alarm interrupts */
153 rc = ioctl(self->fd, RTC_AIE_OFF, 0);
154 ASSERT_NE(-1, rc);
155
156 rc = read(self->fd, &data, sizeof(unsigned long));
157 ASSERT_NE(-1, rc);
158 TH_LOG("data: %lx", data);
159
160 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
161 ASSERT_NE(-1, rc);
162
163 new = timegm((struct tm *)&tm);
164 ASSERT_EQ(new, secs);
165 }
166
TEST_F(rtc,alarm_wkalm_set)167 TEST_F(rtc, alarm_wkalm_set) {
168 struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
169 struct rtc_wkalrm alarm = { 0 };
170 struct rtc_time tm;
171 unsigned long data;
172 fd_set readfds;
173 time_t secs, new;
174 int rc;
175
176 rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
177 ASSERT_NE(-1, rc);
178
179 secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
180 gmtime_r(&secs, (struct tm *)&alarm.time);
181
182 alarm.enabled = 1;
183
184 rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
185 if (rc == -1) {
186 ASSERT_EQ(EINVAL, errno);
187 TH_LOG("skip alarms are not supported.");
188 return;
189 }
190
191 rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
192 ASSERT_NE(-1, rc);
193
194 TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
195 alarm.time.tm_mday, alarm.time.tm_mon + 1,
196 alarm.time.tm_year + 1900, alarm.time.tm_hour,
197 alarm.time.tm_min, alarm.time.tm_sec);
198
199 FD_ZERO(&readfds);
200 FD_SET(self->fd, &readfds);
201
202 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
203 ASSERT_NE(-1, rc);
204 ASSERT_NE(0, rc);
205
206 rc = read(self->fd, &data, sizeof(unsigned long));
207 ASSERT_NE(-1, rc);
208
209 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
210 ASSERT_NE(-1, rc);
211
212 new = timegm((struct tm *)&tm);
213 ASSERT_EQ(new, secs);
214 }
215
216 TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
217 struct timeval tv = { .tv_sec = 62 };
218 unsigned long data;
219 struct rtc_time tm;
220 fd_set readfds;
221 time_t secs, new;
222 int rc;
223
224 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
225 ASSERT_NE(-1, rc);
226
227 secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
228 gmtime_r(&secs, (struct tm *)&tm);
229
230 rc = ioctl(self->fd, RTC_ALM_SET, &tm);
231 if (rc == -1) {
232 ASSERT_EQ(EINVAL, errno);
233 TH_LOG("skip alarms are not supported.");
234 return;
235 }
236
237 rc = ioctl(self->fd, RTC_ALM_READ, &tm);
238 ASSERT_NE(-1, rc);
239
240 TH_LOG("Alarm time now set to %02d:%02d:%02d.",
241 tm.tm_hour, tm.tm_min, tm.tm_sec);
242
243 /* Enable alarm interrupts */
244 rc = ioctl(self->fd, RTC_AIE_ON, 0);
245 ASSERT_NE(-1, rc);
246
247 FD_ZERO(&readfds);
248 FD_SET(self->fd, &readfds);
249
250 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
251 ASSERT_NE(-1, rc);
252 ASSERT_NE(0, rc);
253
254 /* Disable alarm interrupts */
255 rc = ioctl(self->fd, RTC_AIE_OFF, 0);
256 ASSERT_NE(-1, rc);
257
258 rc = read(self->fd, &data, sizeof(unsigned long));
259 ASSERT_NE(-1, rc);
260 TH_LOG("data: %lx", data);
261
262 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
263 ASSERT_NE(-1, rc);
264
265 new = timegm((struct tm *)&tm);
266 ASSERT_EQ(new, secs);
267 }
268
269 TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
270 struct timeval tv = { .tv_sec = 62 };
271 struct rtc_wkalrm alarm = { 0 };
272 struct rtc_time tm;
273 unsigned long data;
274 fd_set readfds;
275 time_t secs, new;
276 int rc;
277
278 rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
279 ASSERT_NE(-1, rc);
280
281 secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
282 gmtime_r(&secs, (struct tm *)&alarm.time);
283
284 alarm.enabled = 1;
285
286 rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
287 if (rc == -1) {
288 ASSERT_EQ(EINVAL, errno);
289 TH_LOG("skip alarms are not supported.");
290 return;
291 }
292
293 rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
294 ASSERT_NE(-1, rc);
295
296 TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
297 alarm.time.tm_mday, alarm.time.tm_mon + 1,
298 alarm.time.tm_year + 1900, alarm.time.tm_hour,
299 alarm.time.tm_min, alarm.time.tm_sec);
300
301 FD_ZERO(&readfds);
302 FD_SET(self->fd, &readfds);
303
304 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
305 ASSERT_NE(-1, rc);
306 ASSERT_NE(0, rc);
307
308 rc = read(self->fd, &data, sizeof(unsigned long));
309 ASSERT_NE(-1, rc);
310
311 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
312 ASSERT_NE(-1, rc);
313
314 new = timegm((struct tm *)&tm);
315 ASSERT_EQ(new, secs);
316 }
317 #endif
318
319 static void __attribute__((constructor))
__constructor_order_last(void)320 __constructor_order_last(void)
321 {
322 if (!__constructor_order)
323 __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
324 }
325
main(int argc,char ** argv)326 int main(int argc, char **argv)
327 {
328 struct stat st;
329
330 switch (argc) {
331 case 2:
332 rtc_file = argv[1];
333 /* FALLTHROUGH */
334 case 1:
335 break;
336 default:
337 fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
338 return 1;
339 }
340
341 if (stat(rtc_file, &st) < 0 || !S_ISCHR(st.st_mode)) {
342 printf("no RTC present\n");
343 return 0;
344 }
345
346 return test_harness_run(argc, argv);
347 }
348