• 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 
24 static char *rtc_file = "/dev/rtc0";
25 
FIXTURE(rtc)26 FIXTURE(rtc) {
27 	int fd;
28 };
29 
FIXTURE_SETUP(rtc)30 FIXTURE_SETUP(rtc) {
31 	self->fd = open(rtc_file, O_RDONLY);
32 	ASSERT_NE(-1, self->fd);
33 }
34 
FIXTURE_TEARDOWN(rtc)35 FIXTURE_TEARDOWN(rtc) {
36 	close(self->fd);
37 }
38 
TEST_F(rtc,date_read)39 TEST_F(rtc, date_read) {
40 	int rc;
41 	struct rtc_time rtc_tm;
42 
43 	/* Read the RTC time/date */
44 	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
45 	ASSERT_NE(-1, rc);
46 
47 	TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
48 	       rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
49 	       rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
50 }
51 
52 #ifndef __ANDROID__ // b/31578457
53 TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
54 	int i, rc, irq = 0;
55 	unsigned long data;
56 
57 	/* Turn on update interrupts */
58 	rc = ioctl(self->fd, RTC_UIE_ON, 0);
59 	if (rc == -1) {
60 		ASSERT_EQ(EINVAL, errno);
61 		TH_LOG("skip update IRQs not supported.");
62 		return;
63 	}
64 
65 	for (i = 0; i < NUM_UIE; i++) {
66 		/* This read will block */
67 		rc = read(self->fd, &data, sizeof(data));
68 		ASSERT_NE(-1, rc);
69 		irq++;
70 	}
71 
72 	EXPECT_EQ(NUM_UIE, irq);
73 
74 	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
75 	ASSERT_NE(-1, rc);
76 }
77 
TEST_F(rtc,uie_select)78 TEST_F(rtc, uie_select) {
79 	int i, rc, irq = 0;
80 	unsigned long data;
81 
82 	/* Turn on update interrupts */
83 	rc = ioctl(self->fd, RTC_UIE_ON, 0);
84 	if (rc == -1) {
85 		ASSERT_EQ(EINVAL, errno);
86 		TH_LOG("skip update IRQs not supported.");
87 		return;
88 	}
89 
90 	for (i = 0; i < NUM_UIE; i++) {
91 		struct timeval tv = { .tv_sec = 2 };
92 		fd_set readfds;
93 
94 		FD_ZERO(&readfds);
95 		FD_SET(self->fd, &readfds);
96 		/* The select will wait until an RTC interrupt happens. */
97 		rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
98 		ASSERT_NE(-1, rc);
99 		ASSERT_NE(0, rc);
100 
101 		/* This read won't block */
102 		rc = read(self->fd, &data, sizeof(unsigned long));
103 		ASSERT_NE(-1, rc);
104 		irq++;
105 	}
106 
107 	EXPECT_EQ(NUM_UIE, irq);
108 
109 	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
110 	ASSERT_NE(-1, rc);
111 }
112 
TEST_F(rtc,alarm_alm_set)113 TEST_F(rtc, alarm_alm_set) {
114 	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
115 	unsigned long data;
116 	struct rtc_time tm;
117 	fd_set readfds;
118 	time_t secs, new;
119 	int rc;
120 
121 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
122 	ASSERT_NE(-1, rc);
123 
124 	secs = timegm((struct tm *)&tm) + ALARM_DELTA;
125 	gmtime_r(&secs, (struct tm *)&tm);
126 
127 	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
128 	if (rc == -1) {
129 		ASSERT_EQ(EINVAL, errno);
130 		TH_LOG("skip alarms are not supported.");
131 		return;
132 	}
133 
134 	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
135 	ASSERT_NE(-1, rc);
136 
137 	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
138 	       tm.tm_hour, tm.tm_min, tm.tm_sec);
139 
140 	/* Enable alarm interrupts */
141 	rc = ioctl(self->fd, RTC_AIE_ON, 0);
142 	ASSERT_NE(-1, rc);
143 
144 	FD_ZERO(&readfds);
145 	FD_SET(self->fd, &readfds);
146 
147 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
148 	ASSERT_NE(-1, rc);
149 	ASSERT_NE(0, rc);
150 
151 	/* Disable alarm interrupts */
152 	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
153 	ASSERT_NE(-1, rc);
154 
155 	rc = read(self->fd, &data, sizeof(unsigned long));
156 	ASSERT_NE(-1, rc);
157 	TH_LOG("data: %lx", data);
158 
159 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
160 	ASSERT_NE(-1, rc);
161 
162 	new = timegm((struct tm *)&tm);
163 	ASSERT_EQ(new, secs);
164 }
165 
TEST_F(rtc,alarm_wkalm_set)166 TEST_F(rtc, alarm_wkalm_set) {
167 	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
168 	struct rtc_wkalrm alarm = { 0 };
169 	struct rtc_time tm;
170 	unsigned long data;
171 	fd_set readfds;
172 	time_t secs, new;
173 	int rc;
174 
175 	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
176 	ASSERT_NE(-1, rc);
177 
178 	secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
179 	gmtime_r(&secs, (struct tm *)&alarm.time);
180 
181 	alarm.enabled = 1;
182 
183 	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
184 	if (rc == -1) {
185 		ASSERT_EQ(EINVAL, errno);
186 		TH_LOG("skip alarms are not supported.");
187 		return;
188 	}
189 
190 	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
191 	ASSERT_NE(-1, rc);
192 
193 	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
194 	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
195 	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
196 	       alarm.time.tm_min, alarm.time.tm_sec);
197 
198 	FD_ZERO(&readfds);
199 	FD_SET(self->fd, &readfds);
200 
201 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
202 	ASSERT_NE(-1, rc);
203 	ASSERT_NE(0, rc);
204 
205 	rc = read(self->fd, &data, sizeof(unsigned long));
206 	ASSERT_NE(-1, rc);
207 
208 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
209 	ASSERT_NE(-1, rc);
210 
211 	new = timegm((struct tm *)&tm);
212 	ASSERT_EQ(new, secs);
213 }
214 
215 TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
216 	struct timeval tv = { .tv_sec = 62 };
217 	unsigned long data;
218 	struct rtc_time tm;
219 	fd_set readfds;
220 	time_t secs, new;
221 	int rc;
222 
223 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
224 	ASSERT_NE(-1, rc);
225 
226 	secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
227 	gmtime_r(&secs, (struct tm *)&tm);
228 
229 	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
230 	if (rc == -1) {
231 		ASSERT_EQ(EINVAL, errno);
232 		TH_LOG("skip alarms are not supported.");
233 		return;
234 	}
235 
236 	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
237 	ASSERT_NE(-1, rc);
238 
239 	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
240 	       tm.tm_hour, tm.tm_min, tm.tm_sec);
241 
242 	/* Enable alarm interrupts */
243 	rc = ioctl(self->fd, RTC_AIE_ON, 0);
244 	ASSERT_NE(-1, rc);
245 
246 	FD_ZERO(&readfds);
247 	FD_SET(self->fd, &readfds);
248 
249 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
250 	ASSERT_NE(-1, rc);
251 	ASSERT_NE(0, rc);
252 
253 	/* Disable alarm interrupts */
254 	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
255 	ASSERT_NE(-1, rc);
256 
257 	rc = read(self->fd, &data, sizeof(unsigned long));
258 	ASSERT_NE(-1, rc);
259 	TH_LOG("data: %lx", data);
260 
261 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
262 	ASSERT_NE(-1, rc);
263 
264 	new = timegm((struct tm *)&tm);
265 	ASSERT_EQ(new, secs);
266 }
267 
268 TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
269 	struct timeval tv = { .tv_sec = 62 };
270 	struct rtc_wkalrm alarm = { 0 };
271 	struct rtc_time tm;
272 	unsigned long data;
273 	fd_set readfds;
274 	time_t secs, new;
275 	int rc;
276 
277 	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
278 	ASSERT_NE(-1, rc);
279 
280 	secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
281 	gmtime_r(&secs, (struct tm *)&alarm.time);
282 
283 	alarm.enabled = 1;
284 
285 	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
286 	if (rc == -1) {
287 		ASSERT_EQ(EINVAL, errno);
288 		TH_LOG("skip alarms are not supported.");
289 		return;
290 	}
291 
292 	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
293 	ASSERT_NE(-1, rc);
294 
295 	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
296 	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
297 	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
298 	       alarm.time.tm_min, alarm.time.tm_sec);
299 
300 	FD_ZERO(&readfds);
301 	FD_SET(self->fd, &readfds);
302 
303 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
304 	ASSERT_NE(-1, rc);
305 	ASSERT_NE(0, rc);
306 
307 	rc = read(self->fd, &data, sizeof(unsigned long));
308 	ASSERT_NE(-1, rc);
309 
310 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
311 	ASSERT_NE(-1, rc);
312 
313 	new = timegm((struct tm *)&tm);
314 	ASSERT_EQ(new, secs);
315 }
316 #endif
317 
318 static void __attribute__((constructor))
__constructor_order_last(void)319 __constructor_order_last(void)
320 {
321 	if (!__constructor_order)
322 		__constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
323 }
324 
main(int argc,char ** argv)325 int main(int argc, char **argv)
326 {
327 	switch (argc) {
328 	case 2:
329 		rtc_file = argv[1];
330 		/* FALLTHROUGH */
331 	case 1:
332 		break;
333 	default:
334 		fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
335 		return 1;
336 	}
337 
338 	return test_harness_run(argc, argv);
339 }
340