• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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