Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[sfrench/cifs-2.6.git] / tools / testing / selftests / rtc / rtctest.c
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
26 FIXTURE(rtc) {
27         int fd;
28 };
29
30 FIXTURE_SETUP(rtc) {
31         self->fd = open(rtc_file, O_RDONLY);
32         ASSERT_NE(-1, self->fd);
33 }
34
35 FIXTURE_TEARDOWN(rtc) {
36         close(self->fd);
37 }
38
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(rtc, uie_read) {
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
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
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
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(rtc, alarm_alm_set_minute) {
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(rtc, alarm_wkalm_set_minute) {
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))
317 __constructor_order_last(void)
318 {
319         if (!__constructor_order)
320                 __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
321 }
322
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 }