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