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