Merge branch 'ras-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[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         EXPECT_NE(0, rc);
149
150         /* Disable alarm interrupts */
151         rc = ioctl(self->fd, RTC_AIE_OFF, 0);
152         ASSERT_NE(-1, rc);
153
154         if (rc == 0)
155                 return;
156
157         rc = read(self->fd, &data, sizeof(unsigned long));
158         ASSERT_NE(-1, rc);
159         TH_LOG("data: %lx", data);
160
161         rc = ioctl(self->fd, RTC_RD_TIME, &tm);
162         ASSERT_NE(-1, rc);
163
164         new = timegm((struct tm *)&tm);
165         ASSERT_EQ(new, secs);
166 }
167
168 TEST_F(rtc, alarm_wkalm_set) {
169         struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
170         struct rtc_wkalrm alarm = { 0 };
171         struct rtc_time tm;
172         unsigned long data;
173         fd_set readfds;
174         time_t secs, new;
175         int rc;
176
177         rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
178         ASSERT_NE(-1, rc);
179
180         secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
181         gmtime_r(&secs, (struct tm *)&alarm.time);
182
183         alarm.enabled = 1;
184
185         rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
186         if (rc == -1) {
187                 ASSERT_EQ(EINVAL, errno);
188                 TH_LOG("skip alarms are not supported.");
189                 return;
190         }
191
192         rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
193         ASSERT_NE(-1, rc);
194
195         TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
196                alarm.time.tm_mday, alarm.time.tm_mon + 1,
197                alarm.time.tm_year + 1900, alarm.time.tm_hour,
198                alarm.time.tm_min, alarm.time.tm_sec);
199
200         FD_ZERO(&readfds);
201         FD_SET(self->fd, &readfds);
202
203         rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
204         ASSERT_NE(-1, rc);
205         EXPECT_NE(0, rc);
206
207         rc = read(self->fd, &data, sizeof(unsigned long));
208         ASSERT_NE(-1, rc);
209
210         rc = ioctl(self->fd, RTC_RD_TIME, &tm);
211         ASSERT_NE(-1, rc);
212
213         new = timegm((struct tm *)&tm);
214         ASSERT_EQ(new, secs);
215 }
216
217 static void __attribute__((constructor))
218 __constructor_order_last(void)
219 {
220         if (!__constructor_order)
221                 __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
222 }
223
224 int main(int argc, char **argv)
225 {
226         switch (argc) {
227         case 2:
228                 rtc_file = argv[1];
229                 /* FALLTHROUGH */
230         case 1:
231                 break;
232         default:
233                 fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
234                 return 1;
235         }
236
237         return test_harness_run(argc, argv);
238 }