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