xref: /linux/tools/testing/selftests/rtc/rtctest.c (revision 3fd6c59042dbba50391e30862beac979491145fe)
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 
28 enum rtc_alarm_state {
29 	RTC_ALARM_UNKNOWN,
30 	RTC_ALARM_ENABLED,
31 	RTC_ALARM_DISABLED,
32 };
33 
FIXTURE(rtc)34 FIXTURE(rtc) {
35 	int fd;
36 };
37 
FIXTURE_SETUP(rtc)38 FIXTURE_SETUP(rtc) {
39 	self->fd = open(rtc_file, O_RDONLY);
40 }
41 
FIXTURE_TEARDOWN(rtc)42 FIXTURE_TEARDOWN(rtc) {
43 	close(self->fd);
44 }
45 
TEST_F(rtc,date_read)46 TEST_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 
rtc_time_to_timestamp(struct rtc_time * rtc_time)63 static 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 
nanosleep_with_retries(long ns)77 static 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 
get_rtc_alarm_state(int fd)91 static 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, &param);
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 
109 TEST_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 
149 TEST_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 
TEST_F(rtc,uie_select)178 TEST_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 
TEST_F(rtc,alarm_alm_set)217 TEST_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 
TEST_F(rtc,alarm_wkalm_set)284 TEST_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 
347 TEST_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 
414 TEST_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 
main(int argc,char ** argv)477 int 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 }
501