xref: /aosp_15_r20/external/linux-kselftest/tools/testing/selftests/rtc/rtctest.c (revision 053f45be4e351dfd5e965df293cd45b779f579ee)
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