1
2
3
4
5
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
24static char *rtc_file = "/dev/rtc0";
25
26FIXTURE(rtc) {
27 int fd;
28};
29
30FIXTURE_SETUP(rtc) {
31 self->fd = open(rtc_file, O_RDONLY);
32 ASSERT_NE(-1, self->fd);
33}
34
35FIXTURE_TEARDOWN(rtc) {
36 close(self->fd);
37}
38
39TEST_F(rtc, date_read) {
40 int rc;
41 struct rtc_time rtc_tm;
42
43
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
52TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
53 int i, rc, irq = 0;
54 unsigned long data;
55
56
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
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
77TEST_F(rtc, uie_select) {
78 int i, rc, irq = 0;
79 unsigned long data;
80
81
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
96 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
97 ASSERT_NE(-1, rc);
98 ASSERT_NE(0, rc);
99
100
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
112TEST_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
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
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
165TEST_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
214TEST_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
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
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
267TEST_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
316static void __attribute__((constructor))
317__constructor_order_last(void)
318{
319 if (!__constructor_order)
320 __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
321}
322
323int main(int argc, char **argv)
324{
325 switch (argc) {
326 case 2:
327 rtc_file = argv[1];
328
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