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
TEST_F(rtc,uie_read)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
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 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
TEST_F(rtc,alarm_wkalm_set)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))
__constructor_order_last(void)218 __constructor_order_last(void)
219 {
220 if (!__constructor_order)
221 __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
222 }
223
main(int argc,char ** argv)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 }
239