1 // SPDX-License-Identifier: GPL-2.0
3 * Real Time Clock Driver Test Program
5 * Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com>
10 #include <linux/rtc.h>
13 #include <sys/ioctl.h>
15 #include <sys/types.h>
19 #include "../kselftest_harness.h"
24 static char *rtc_file = "/dev/rtc0";
31 self->fd = open(rtc_file, O_RDONLY);
32 ASSERT_NE(-1, self->fd);
35 FIXTURE_TEARDOWN(rtc) {
39 TEST_F(rtc, date_read) {
41 struct rtc_time rtc_tm;
43 /* Read the RTC time/date */
44 rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
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);
52 TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
56 /* Turn on update interrupts */
57 rc = ioctl(self->fd, RTC_UIE_ON, 0);
59 ASSERT_EQ(EINVAL, errno);
60 TH_LOG("skip update IRQs not supported.");
64 for (i = 0; i < NUM_UIE; i++) {
65 /* This read will block */
66 rc = read(self->fd, &data, sizeof(data));
71 EXPECT_EQ(NUM_UIE, irq);
73 rc = ioctl(self->fd, RTC_UIE_OFF, 0);
77 TEST_F(rtc, uie_select) {
81 /* Turn on update interrupts */
82 rc = ioctl(self->fd, RTC_UIE_ON, 0);
84 ASSERT_EQ(EINVAL, errno);
85 TH_LOG("skip update IRQs not supported.");
89 for (i = 0; i < NUM_UIE; i++) {
90 struct timeval tv = { .tv_sec = 2 };
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);
100 /* This read won't block */
101 rc = read(self->fd, &data, sizeof(unsigned long));
106 EXPECT_EQ(NUM_UIE, irq);
108 rc = ioctl(self->fd, RTC_UIE_OFF, 0);
112 TEST_F(rtc, alarm_alm_set) {
113 struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
120 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
123 secs = timegm((struct tm *)&tm) + ALARM_DELTA;
124 gmtime_r(&secs, (struct tm *)&tm);
126 rc = ioctl(self->fd, RTC_ALM_SET, &tm);
128 ASSERT_EQ(EINVAL, errno);
129 TH_LOG("skip alarms are not supported.");
133 rc = ioctl(self->fd, RTC_ALM_READ, &tm);
136 TH_LOG("Alarm time now set to %02d:%02d:%02d.",
137 tm.tm_hour, tm.tm_min, tm.tm_sec);
139 /* Enable alarm interrupts */
140 rc = ioctl(self->fd, RTC_AIE_ON, 0);
144 FD_SET(self->fd, &readfds);
146 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
150 /* Disable alarm interrupts */
151 rc = ioctl(self->fd, RTC_AIE_OFF, 0);
154 rc = read(self->fd, &data, sizeof(unsigned long));
156 TH_LOG("data: %lx", data);
158 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
161 new = timegm((struct tm *)&tm);
162 ASSERT_EQ(new, secs);
165 TEST_F(rtc, alarm_wkalm_set) {
166 struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
167 struct rtc_wkalrm alarm = { 0 };
174 rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
177 secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
178 gmtime_r(&secs, (struct tm *)&alarm.time);
182 rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
184 ASSERT_EQ(EINVAL, errno);
185 TH_LOG("skip alarms are not supported.");
189 rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
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);
198 FD_SET(self->fd, &readfds);
200 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
204 rc = read(self->fd, &data, sizeof(unsigned long));
207 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
210 new = timegm((struct tm *)&tm);
211 ASSERT_EQ(new, secs);
214 TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
215 struct timeval tv = { .tv_sec = 62 };
222 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
225 secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
226 gmtime_r(&secs, (struct tm *)&tm);
228 rc = ioctl(self->fd, RTC_ALM_SET, &tm);
230 ASSERT_EQ(EINVAL, errno);
231 TH_LOG("skip alarms are not supported.");
235 rc = ioctl(self->fd, RTC_ALM_READ, &tm);
238 TH_LOG("Alarm time now set to %02d:%02d:%02d.",
239 tm.tm_hour, tm.tm_min, tm.tm_sec);
241 /* Enable alarm interrupts */
242 rc = ioctl(self->fd, RTC_AIE_ON, 0);
246 FD_SET(self->fd, &readfds);
248 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
252 /* Disable alarm interrupts */
253 rc = ioctl(self->fd, RTC_AIE_OFF, 0);
256 rc = read(self->fd, &data, sizeof(unsigned long));
258 TH_LOG("data: %lx", data);
260 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
263 new = timegm((struct tm *)&tm);
264 ASSERT_EQ(new, secs);
267 TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
268 struct timeval tv = { .tv_sec = 62 };
269 struct rtc_wkalrm alarm = { 0 };
276 rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
279 secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
280 gmtime_r(&secs, (struct tm *)&alarm.time);
284 rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
286 ASSERT_EQ(EINVAL, errno);
287 TH_LOG("skip alarms are not supported.");
291 rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
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);
300 FD_SET(self->fd, &readfds);
302 rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
306 rc = read(self->fd, &data, sizeof(unsigned long));
309 rc = ioctl(self->fd, RTC_RD_TIME, &tm);
312 new = timegm((struct tm *)&tm);
313 ASSERT_EQ(new, secs);
316 static void __attribute__((constructor))
317 __constructor_order_last(void)
319 if (!__constructor_order)
320 __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
323 int main(int argc, char **argv)
332 fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
336 return test_harness_run(argc, argv);