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