]> Git Repo - J-linux.git/blob - tools/testing/selftests/rtc/rtctest.c
Merge tag 'kbuild-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[J-linux.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 <[email protected]>
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 #define READ_LOOP_DURATION_SEC 30
24 #define READ_LOOP_SLEEP_MS 11
25
26 static char *rtc_file = "/dev/rtc0";
27
28 FIXTURE(rtc) {
29         int fd;
30 };
31
32 FIXTURE_SETUP(rtc) {
33         self->fd = open(rtc_file, O_RDONLY);
34 }
35
36 FIXTURE_TEARDOWN(rtc) {
37         close(self->fd);
38 }
39
40 TEST_F(rtc, date_read) {
41         int rc;
42         struct rtc_time rtc_tm;
43
44         if (self->fd == -1 && errno == ENOENT)
45                 SKIP(return, "Skipping test since %s does not exist", rtc_file);
46         ASSERT_NE(-1, self->fd);
47
48         /* Read the RTC time/date */
49         rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
50         ASSERT_NE(-1, rc);
51
52         TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
53                rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
54                rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
55 }
56
57 static time_t rtc_time_to_timestamp(struct rtc_time *rtc_time)
58 {
59         struct tm tm_time = {
60                .tm_sec = rtc_time->tm_sec,
61                .tm_min = rtc_time->tm_min,
62                .tm_hour = rtc_time->tm_hour,
63                .tm_mday = rtc_time->tm_mday,
64                .tm_mon = rtc_time->tm_mon,
65                .tm_year = rtc_time->tm_year,
66         };
67
68         return mktime(&tm_time);
69 }
70
71 static void nanosleep_with_retries(long ns)
72 {
73         struct timespec req = {
74                 .tv_sec = 0,
75                 .tv_nsec = ns,
76         };
77         struct timespec rem;
78
79         while (nanosleep(&req, &rem) != 0) {
80                 req.tv_sec = rem.tv_sec;
81                 req.tv_nsec = rem.tv_nsec;
82         }
83 }
84
85 TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
86         int rc;
87         long iter_count = 0;
88         struct rtc_time rtc_tm;
89         time_t start_rtc_read, prev_rtc_read;
90
91         if (self->fd == -1 && errno == ENOENT)
92                 SKIP(return, "Skipping test since %s does not exist", rtc_file);
93         ASSERT_NE(-1, self->fd);
94
95         TH_LOG("Continuously reading RTC time for %ds (with %dms breaks after every read).",
96                READ_LOOP_DURATION_SEC, READ_LOOP_SLEEP_MS);
97
98         rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
99         ASSERT_NE(-1, rc);
100         start_rtc_read = rtc_time_to_timestamp(&rtc_tm);
101         prev_rtc_read = start_rtc_read;
102
103         do  {
104                 time_t rtc_read;
105
106                 rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
107                 ASSERT_NE(-1, rc);
108
109                 rtc_read = rtc_time_to_timestamp(&rtc_tm);
110                 /* Time should not go backwards */
111                 ASSERT_LE(prev_rtc_read, rtc_read);
112                 /* Time should not increase more then 1s at a time */
113                 ASSERT_GE(prev_rtc_read + 1, rtc_read);
114
115                 /* Sleep 11ms to avoid killing / overheating the RTC */
116                 nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
117
118                 prev_rtc_read = rtc_read;
119                 iter_count++;
120         } while (prev_rtc_read <= start_rtc_read + READ_LOOP_DURATION_SEC);
121
122         TH_LOG("Performed %ld RTC time reads.", iter_count);
123 }
124
125 TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
126         int i, rc, irq = 0;
127         unsigned long data;
128
129         if (self->fd == -1 && errno == ENOENT)
130                 SKIP(return, "Skipping test since %s does not exist", rtc_file);
131         ASSERT_NE(-1, self->fd);
132
133         /* Turn on update interrupts */
134         rc = ioctl(self->fd, RTC_UIE_ON, 0);
135         if (rc == -1) {
136                 ASSERT_EQ(EINVAL, errno);
137                 TH_LOG("skip update IRQs not supported.");
138                 return;
139         }
140
141         for (i = 0; i < NUM_UIE; i++) {
142                 /* This read will block */
143                 rc = read(self->fd, &data, sizeof(data));
144                 ASSERT_NE(-1, rc);
145                 irq++;
146         }
147
148         EXPECT_EQ(NUM_UIE, irq);
149
150         rc = ioctl(self->fd, RTC_UIE_OFF, 0);
151         ASSERT_NE(-1, rc);
152 }
153
154 TEST_F(rtc, uie_select) {
155         int i, rc, irq = 0;
156         unsigned long data;
157
158         if (self->fd == -1 && errno == ENOENT)
159                 SKIP(return, "Skipping test since %s does not exist", rtc_file);
160         ASSERT_NE(-1, self->fd);
161
162         /* Turn on update interrupts */
163         rc = ioctl(self->fd, RTC_UIE_ON, 0);
164         if (rc == -1) {
165                 ASSERT_EQ(EINVAL, errno);
166                 TH_LOG("skip update IRQs not supported.");
167                 return;
168         }
169
170         for (i = 0; i < NUM_UIE; i++) {
171                 struct timeval tv = { .tv_sec = 2 };
172                 fd_set readfds;
173
174                 FD_ZERO(&readfds);
175                 FD_SET(self->fd, &readfds);
176                 /* The select will wait until an RTC interrupt happens. */
177                 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
178                 ASSERT_NE(-1, rc);
179                 ASSERT_NE(0, rc);
180
181                 /* This read won't block */
182                 rc = read(self->fd, &data, sizeof(unsigned long));
183                 ASSERT_NE(-1, rc);
184                 irq++;
185         }
186
187         EXPECT_EQ(NUM_UIE, irq);
188
189         rc = ioctl(self->fd, RTC_UIE_OFF, 0);
190         ASSERT_NE(-1, rc);
191 }
192
193 TEST_F(rtc, alarm_alm_set) {
194         struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
195         unsigned long data;
196         struct rtc_time tm;
197         fd_set readfds;
198         time_t secs, new;
199         int rc;
200
201         if (self->fd == -1 && errno == ENOENT)
202                 SKIP(return, "Skipping test since %s does not exist", rtc_file);
203         ASSERT_NE(-1, self->fd);
204
205         rc = ioctl(self->fd, RTC_RD_TIME, &tm);
206         ASSERT_NE(-1, rc);
207
208         secs = timegm((struct tm *)&tm) + ALARM_DELTA;
209         gmtime_r(&secs, (struct tm *)&tm);
210
211         rc = ioctl(self->fd, RTC_ALM_SET, &tm);
212         if (rc == -1) {
213                 ASSERT_EQ(EINVAL, errno);
214                 TH_LOG("skip alarms are not supported.");
215                 return;
216         }
217
218         rc = ioctl(self->fd, RTC_ALM_READ, &tm);
219         ASSERT_NE(-1, rc);
220
221         TH_LOG("Alarm time now set to %02d:%02d:%02d.",
222                tm.tm_hour, tm.tm_min, tm.tm_sec);
223
224         /* Enable alarm interrupts */
225         rc = ioctl(self->fd, RTC_AIE_ON, 0);
226         ASSERT_NE(-1, rc);
227
228         FD_ZERO(&readfds);
229         FD_SET(self->fd, &readfds);
230
231         rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
232         ASSERT_NE(-1, rc);
233         ASSERT_NE(0, rc);
234
235         /* Disable alarm interrupts */
236         rc = ioctl(self->fd, RTC_AIE_OFF, 0);
237         ASSERT_NE(-1, rc);
238
239         rc = read(self->fd, &data, sizeof(unsigned long));
240         ASSERT_NE(-1, rc);
241         TH_LOG("data: %lx", data);
242
243         rc = ioctl(self->fd, RTC_RD_TIME, &tm);
244         ASSERT_NE(-1, rc);
245
246         new = timegm((struct tm *)&tm);
247         ASSERT_EQ(new, secs);
248 }
249
250 TEST_F(rtc, alarm_wkalm_set) {
251         struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
252         struct rtc_wkalrm alarm = { 0 };
253         struct rtc_time tm;
254         unsigned long data;
255         fd_set readfds;
256         time_t secs, new;
257         int rc;
258
259         if (self->fd == -1 && errno == ENOENT)
260                 SKIP(return, "Skipping test since %s does not exist", rtc_file);
261         ASSERT_NE(-1, self->fd);
262
263         rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
264         ASSERT_NE(-1, rc);
265
266         secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
267         gmtime_r(&secs, (struct tm *)&alarm.time);
268
269         alarm.enabled = 1;
270
271         rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
272         if (rc == -1) {
273                 ASSERT_EQ(EINVAL, errno);
274                 TH_LOG("skip alarms are not supported.");
275                 return;
276         }
277
278         rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
279         ASSERT_NE(-1, rc);
280
281         TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
282                alarm.time.tm_mday, alarm.time.tm_mon + 1,
283                alarm.time.tm_year + 1900, alarm.time.tm_hour,
284                alarm.time.tm_min, alarm.time.tm_sec);
285
286         FD_ZERO(&readfds);
287         FD_SET(self->fd, &readfds);
288
289         rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
290         ASSERT_NE(-1, rc);
291         ASSERT_NE(0, rc);
292
293         rc = read(self->fd, &data, sizeof(unsigned long));
294         ASSERT_NE(-1, rc);
295
296         rc = ioctl(self->fd, RTC_RD_TIME, &tm);
297         ASSERT_NE(-1, rc);
298
299         new = timegm((struct tm *)&tm);
300         ASSERT_EQ(new, secs);
301 }
302
303 TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
304         struct timeval tv = { .tv_sec = 62 };
305         unsigned long data;
306         struct rtc_time tm;
307         fd_set readfds;
308         time_t secs, new;
309         int rc;
310
311         if (self->fd == -1 && errno == ENOENT)
312                 SKIP(return, "Skipping test since %s does not exist", rtc_file);
313         ASSERT_NE(-1, self->fd);
314
315         rc = ioctl(self->fd, RTC_RD_TIME, &tm);
316         ASSERT_NE(-1, rc);
317
318         secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
319         gmtime_r(&secs, (struct tm *)&tm);
320
321         rc = ioctl(self->fd, RTC_ALM_SET, &tm);
322         if (rc == -1) {
323                 ASSERT_EQ(EINVAL, errno);
324                 TH_LOG("skip alarms are not supported.");
325                 return;
326         }
327
328         rc = ioctl(self->fd, RTC_ALM_READ, &tm);
329         ASSERT_NE(-1, rc);
330
331         TH_LOG("Alarm time now set to %02d:%02d:%02d.",
332                tm.tm_hour, tm.tm_min, tm.tm_sec);
333
334         /* Enable alarm interrupts */
335         rc = ioctl(self->fd, RTC_AIE_ON, 0);
336         ASSERT_NE(-1, rc);
337
338         FD_ZERO(&readfds);
339         FD_SET(self->fd, &readfds);
340
341         rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
342         ASSERT_NE(-1, rc);
343         ASSERT_NE(0, rc);
344
345         /* Disable alarm interrupts */
346         rc = ioctl(self->fd, RTC_AIE_OFF, 0);
347         ASSERT_NE(-1, rc);
348
349         rc = read(self->fd, &data, sizeof(unsigned long));
350         ASSERT_NE(-1, rc);
351         TH_LOG("data: %lx", data);
352
353         rc = ioctl(self->fd, RTC_RD_TIME, &tm);
354         ASSERT_NE(-1, rc);
355
356         new = timegm((struct tm *)&tm);
357         ASSERT_EQ(new, secs);
358 }
359
360 TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
361         struct timeval tv = { .tv_sec = 62 };
362         struct rtc_wkalrm alarm = { 0 };
363         struct rtc_time tm;
364         unsigned long data;
365         fd_set readfds;
366         time_t secs, new;
367         int rc;
368
369         if (self->fd == -1 && errno == ENOENT)
370                 SKIP(return, "Skipping test since %s does not exist", rtc_file);
371         ASSERT_NE(-1, self->fd);
372
373         rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
374         ASSERT_NE(-1, rc);
375
376         secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
377         gmtime_r(&secs, (struct tm *)&alarm.time);
378
379         alarm.enabled = 1;
380
381         rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
382         if (rc == -1) {
383                 ASSERT_EQ(EINVAL, errno);
384                 TH_LOG("skip alarms are not supported.");
385                 return;
386         }
387
388         rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
389         ASSERT_NE(-1, rc);
390
391         TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
392                alarm.time.tm_mday, alarm.time.tm_mon + 1,
393                alarm.time.tm_year + 1900, alarm.time.tm_hour,
394                alarm.time.tm_min, alarm.time.tm_sec);
395
396         FD_ZERO(&readfds);
397         FD_SET(self->fd, &readfds);
398
399         rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
400         ASSERT_NE(-1, rc);
401         ASSERT_NE(0, rc);
402
403         rc = read(self->fd, &data, sizeof(unsigned long));
404         ASSERT_NE(-1, rc);
405
406         rc = ioctl(self->fd, RTC_RD_TIME, &tm);
407         ASSERT_NE(-1, rc);
408
409         new = timegm((struct tm *)&tm);
410         ASSERT_EQ(new, secs);
411 }
412
413 static void __attribute__((constructor))
414 __constructor_order_last(void)
415 {
416         if (!__constructor_order)
417                 __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
418 }
419
420 int main(int argc, char **argv)
421 {
422         switch (argc) {
423         case 2:
424                 rtc_file = argv[1];
425                 /* FALLTHROUGH */
426         case 1:
427                 break;
428         default:
429                 fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
430                 return 1;
431         }
432
433         return test_harness_run(argc, argv);
434 }
This page took 0.054558 seconds and 4 git commands to generate.