xref: /linux/tools/testing/selftests/rtc/rtctest.c (revision 2a6b6c9a226279b4f6668450ddb21ae655558087)
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 FIXTURE(rtc) {
29 	int fd;
30 };
31 
32 FIXTURE_SETUP(rtc) {
33 	self->fd = open(rtc_file, O_RDONLY);
34 }
35 
36 FIXTURE_TEARDOWN(rtc) {
37 	close(self->fd);
38 }
39 
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 
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 
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 TEST_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 
154 TEST_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 
193 TEST_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 
250 TEST_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 
303 TEST_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 
360 TEST_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 
413 int main(int argc, char **argv)
414 {
415 	switch (argc) {
416 	case 2:
417 		rtc_file = argv[1];
418 		/* FALLTHROUGH */
419 	case 1:
420 		break;
421 	default:
422 		fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
423 		return 1;
424 	}
425 
426 	return test_harness_run(argc, argv);
427 }
428