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