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/time.h>
15 #include <sys/types.h>
16 #include <time.h>
17 #include <unistd.h>
18
19 #include "../kselftest_harness.h"
20
21 #define NUM_UIE 3
22 #define ALARM_DELTA 3
23 #define READ_LOOP_DURATION_SEC 30
24 #define READ_LOOP_SLEEP_MS 11
25
26 static char *rtc_file = "/dev/rtc0";
27
FIXTURE(rtc)28 FIXTURE(rtc) {
29 int fd;
30 };
31
FIXTURE_SETUP(rtc)32 FIXTURE_SETUP(rtc) {
33 self->fd = open(rtc_file, O_RDONLY);
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 if (self->fd == -1 && errno == ENOENT)
45 SKIP(return, "Skipping test since %s does not exist", rtc_file);
46 ASSERT_NE(-1, self->fd);
47
48 /* Read the RTC time/date */
49 rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
50 ASSERT_NE(-1, rc);
51
52 TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
53 rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
54 rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
55 }
56
rtc_time_to_timestamp(struct rtc_time * rtc_time)57 static time_t rtc_time_to_timestamp(struct rtc_time *rtc_time)
58 {
59 struct tm tm_time = {
60 .tm_sec = rtc_time->tm_sec,
61 .tm_min = rtc_time->tm_min,
62 .tm_hour = rtc_time->tm_hour,
63 .tm_mday = rtc_time->tm_mday,
64 .tm_mon = rtc_time->tm_mon,
65 .tm_year = rtc_time->tm_year,
66 };
67
68 return mktime(&tm_time);
69 }
70
nanosleep_with_retries(long ns)71 static void nanosleep_with_retries(long ns)
72 {
73 struct timespec req = {
74 .tv_sec = 0,
75 .tv_nsec = ns,
76 };
77 struct timespec rem;
78
79 while (nanosleep(&req, &rem) != 0) {
80 req.tv_sec = rem.tv_sec;
81 req.tv_nsec = rem.tv_nsec;
82 }
83 }
84
85 TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
86 int rc;
87 long iter_count = 0;
88 struct rtc_time rtc_tm;
89 time_t start_rtc_read, prev_rtc_read;
90
91 if (self->fd == -1 && errno == ENOENT)
92 SKIP(return, "Skipping test since %s does not exist", rtc_file);
93 ASSERT_NE(-1, self->fd);
94
95 TH_LOG("Continuously reading RTC time for %ds (with %dms breaks after every read).",
96 READ_LOOP_DURATION_SEC, READ_LOOP_SLEEP_MS);
97
98 rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
99 ASSERT_NE(-1, rc);
100 start_rtc_read = rtc_time_to_timestamp(&rtc_tm);
101 prev_rtc_read = start_rtc_read;
102
103 do {
104 time_t rtc_read;
105
106 rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
107 ASSERT_NE(-1, rc);
108
109 rtc_read = rtc_time_to_timestamp(&rtc_tm);
110 /* Time should not go backwards */
111 ASSERT_LE(prev_rtc_read, rtc_read);
112 /* Time should not increase more then 1s at a time */
113 ASSERT_GE(prev_rtc_read + 1, rtc_read);
114
115 /* Sleep 11ms to avoid killing / overheating the RTC */
116 nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
117
118 prev_rtc_read = rtc_read;
119 iter_count++;
120 } while (prev_rtc_read <= start_rtc_read + READ_LOOP_DURATION_SEC);
121
122 TH_LOG("Performed %ld RTC time reads.", iter_count);
123 }
124
125 #ifndef __ANDROID__ // b/31578457
126 TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
127 int i, rc, irq = 0;
128 unsigned long data;
129
130 if (self->fd == -1 && errno == ENOENT)
131 SKIP(return, "Skipping test since %s does not exist", rtc_file);
132 ASSERT_NE(-1, self->fd);
133
134 /* Turn on update interrupts */
135 rc = ioctl(self->fd, RTC_UIE_ON, 0);
136 if (rc == -1) {
137 ASSERT_EQ(EINVAL, errno);
138 TH_LOG("skip update IRQs not supported.");
139 return;
140 }
141
142 for (i = 0; i < NUM_UIE; i++) {
143 /* This read will block */
144 rc = read(self->fd, &data, sizeof(data));
145 ASSERT_NE(-1, rc);
146 irq++;
147 }
148
149 EXPECT_EQ(NUM_UIE, irq);
150
151 rc = ioctl(self->fd, RTC_UIE_OFF, 0);
152 ASSERT_NE(-1, rc);
153 }
154
TEST_F(rtc,uie_select)155 TEST_F(rtc, uie_select) {
156 int i, rc, irq = 0;
157 unsigned long data;
158
159 if (self->fd == -1 && errno == ENOENT)
160 SKIP(return, "Skipping test since %s does not exist", rtc_file);
161 ASSERT_NE(-1, self->fd);
162
163 /* Turn on update interrupts */
164 rc = ioctl(self->fd, RTC_UIE_ON, 0);
165 if (rc == -1) {
166 ASSERT_EQ(EINVAL, errno);
167 TH_LOG("skip update IRQs not supported.");
168 return;
169 }
170
171 for (i = 0; i < NUM_UIE; i++) {
172 struct timeval tv = { .tv_sec = 2 };
173 fd_set readfds;
174
175 FD_ZERO(&readfds);
176 FD_SET(self->fd, &readfds);
177 /* The select will wait until an RTC interrupt happens. */
178 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
179 ASSERT_NE(-1, rc);
180 ASSERT_NE(0, rc);
181
182 /* This read won't block */
183 rc = read(self->fd, &data, sizeof(unsigned long));
184 ASSERT_NE(-1, rc);
185 irq++;
186 }
187
188 EXPECT_EQ(NUM_UIE, irq);
189
190 rc = ioctl(self->fd, RTC_UIE_OFF, 0);
191 ASSERT_NE(-1, rc);
192 }
193
TEST_F(rtc,alarm_alm_set)194 TEST_F(rtc, alarm_alm_set) {
195 struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
196 unsigned long data;
197 struct rtc_time tm;
198 fd_set readfds;
199 time_t secs, new;
200 int rc;
201
202 if (self->fd == -1 && errno == ENOENT)
203 SKIP(return, "Skipping test since %s does not exist", rtc_file);
204 ASSERT_NE(-1, self->fd);
205
206 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
207 ASSERT_NE(-1, rc);
208
209 secs = timegm((struct tm *)&tm) + ALARM_DELTA;
210 gmtime_r(&secs, (struct tm *)&tm);
211
212 rc = ioctl(self->fd, RTC_ALM_SET, &tm);
213 if (rc == -1) {
214 ASSERT_EQ(EINVAL, errno);
215 TH_LOG("skip alarms are not supported.");
216 return;
217 }
218
219 rc = ioctl(self->fd, RTC_ALM_READ, &tm);
220 ASSERT_NE(-1, rc);
221
222 TH_LOG("Alarm time now set to %02d:%02d:%02d.",
223 tm.tm_hour, tm.tm_min, tm.tm_sec);
224
225 /* Enable alarm interrupts */
226 rc = ioctl(self->fd, RTC_AIE_ON, 0);
227 ASSERT_NE(-1, rc);
228
229 FD_ZERO(&readfds);
230 FD_SET(self->fd, &readfds);
231
232 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
233 ASSERT_NE(-1, rc);
234 ASSERT_NE(0, rc);
235
236 /* Disable alarm interrupts */
237 rc = ioctl(self->fd, RTC_AIE_OFF, 0);
238 ASSERT_NE(-1, rc);
239
240 rc = read(self->fd, &data, sizeof(unsigned long));
241 ASSERT_NE(-1, rc);
242 TH_LOG("data: %lx", data);
243
244 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
245 ASSERT_NE(-1, rc);
246
247 new = timegm((struct tm *)&tm);
248 ASSERT_EQ(new, secs);
249 }
250
TEST_F(rtc,alarm_wkalm_set)251 TEST_F(rtc, alarm_wkalm_set) {
252 struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
253 struct rtc_wkalrm alarm = { 0 };
254 struct rtc_time tm;
255 unsigned long data;
256 fd_set readfds;
257 time_t secs, new;
258 int rc;
259
260 if (self->fd == -1 && errno == ENOENT)
261 SKIP(return, "Skipping test since %s does not exist", rtc_file);
262 ASSERT_NE(-1, self->fd);
263
264 rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
265 ASSERT_NE(-1, rc);
266
267 secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
268 gmtime_r(&secs, (struct tm *)&alarm.time);
269
270 alarm.enabled = 1;
271
272 rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
273 if (rc == -1) {
274 ASSERT_EQ(EINVAL, errno);
275 TH_LOG("skip alarms are not supported.");
276 return;
277 }
278
279 rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
280 ASSERT_NE(-1, rc);
281
282 TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
283 alarm.time.tm_mday, alarm.time.tm_mon + 1,
284 alarm.time.tm_year + 1900, alarm.time.tm_hour,
285 alarm.time.tm_min, alarm.time.tm_sec);
286
287 FD_ZERO(&readfds);
288 FD_SET(self->fd, &readfds);
289
290 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
291 ASSERT_NE(-1, rc);
292 ASSERT_NE(0, rc);
293
294 rc = read(self->fd, &data, sizeof(unsigned long));
295 ASSERT_NE(-1, rc);
296
297 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
298 ASSERT_NE(-1, rc);
299
300 new = timegm((struct tm *)&tm);
301 ASSERT_EQ(new, secs);
302 }
303
304 TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
305 struct timeval tv = { .tv_sec = 62 };
306 unsigned long data;
307 struct rtc_time tm;
308 fd_set readfds;
309 time_t secs, new;
310 int rc;
311
312 if (self->fd == -1 && errno == ENOENT)
313 SKIP(return, "Skipping test since %s does not exist", rtc_file);
314 ASSERT_NE(-1, self->fd);
315
316 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
317 ASSERT_NE(-1, rc);
318
319 secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
320 gmtime_r(&secs, (struct tm *)&tm);
321
322 rc = ioctl(self->fd, RTC_ALM_SET, &tm);
323 if (rc == -1) {
324 ASSERT_EQ(EINVAL, errno);
325 TH_LOG("skip alarms are not supported.");
326 return;
327 }
328
329 rc = ioctl(self->fd, RTC_ALM_READ, &tm);
330 ASSERT_NE(-1, rc);
331
332 TH_LOG("Alarm time now set to %02d:%02d:%02d.",
333 tm.tm_hour, tm.tm_min, tm.tm_sec);
334
335 /* Enable alarm interrupts */
336 rc = ioctl(self->fd, RTC_AIE_ON, 0);
337 ASSERT_NE(-1, rc);
338
339 FD_ZERO(&readfds);
340 FD_SET(self->fd, &readfds);
341
342 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
343 ASSERT_NE(-1, rc);
344 ASSERT_NE(0, rc);
345
346 /* Disable alarm interrupts */
347 rc = ioctl(self->fd, RTC_AIE_OFF, 0);
348 ASSERT_NE(-1, rc);
349
350 rc = read(self->fd, &data, sizeof(unsigned long));
351 ASSERT_NE(-1, rc);
352 TH_LOG("data: %lx", data);
353
354 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
355 ASSERT_NE(-1, rc);
356
357 new = timegm((struct tm *)&tm);
358 ASSERT_EQ(new, secs);
359 }
360
361 TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
362 struct timeval tv = { .tv_sec = 62 };
363 struct rtc_wkalrm alarm = { 0 };
364 struct rtc_time tm;
365 unsigned long data;
366 fd_set readfds;
367 time_t secs, new;
368 int rc;
369
370 if (self->fd == -1 && errno == ENOENT)
371 SKIP(return, "Skipping test since %s does not exist", rtc_file);
372 ASSERT_NE(-1, self->fd);
373
374 rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
375 ASSERT_NE(-1, rc);
376
377 secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
378 gmtime_r(&secs, (struct tm *)&alarm.time);
379
380 alarm.enabled = 1;
381
382 rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
383 if (rc == -1) {
384 ASSERT_EQ(EINVAL, errno);
385 TH_LOG("skip alarms are not supported.");
386 return;
387 }
388
389 rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
390 ASSERT_NE(-1, rc);
391
392 TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
393 alarm.time.tm_mday, alarm.time.tm_mon + 1,
394 alarm.time.tm_year + 1900, alarm.time.tm_hour,
395 alarm.time.tm_min, alarm.time.tm_sec);
396
397 FD_ZERO(&readfds);
398 FD_SET(self->fd, &readfds);
399
400 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
401 ASSERT_NE(-1, rc);
402 ASSERT_NE(0, rc);
403
404 rc = read(self->fd, &data, sizeof(unsigned long));
405 ASSERT_NE(-1, rc);
406
407 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
408 ASSERT_NE(-1, rc);
409
410 new = timegm((struct tm *)&tm);
411 ASSERT_EQ(new, secs);
412 }
413 #endif
414
main(int argc,char ** argv)415 int main(int argc, char **argv)
416 {
417 int ret = -1;
418
419 switch (argc) {
420 case 2:
421 rtc_file = argv[1];
422 /* FALLTHROUGH */
423 case 1:
424 break;
425 default:
426 fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
427 return 1;
428 }
429
430 /* Run the test if rtc_file is accessible */
431 if (access(rtc_file, R_OK) == 0)
432 ret = test_harness_run(argc, argv);
433 else
434 ksft_exit_skip("[SKIP]: Cannot access rtc file %s - Exiting\n",
435 rtc_file);
436
437 return ret;
438 }
439