xref: /openbmc/linux/tools/testing/selftests/rtc/rtctest.c (revision 2aaa36e95ea586ad23edfcc1d474e8b735a4d1c3)
1d8da8665SAlexandre Belloni // SPDX-License-Identifier: GPL-2.0
2a12ab9e1SAlexandre Belloni /*
3d8da8665SAlexandre Belloni  * Real Time Clock Driver Test Program
4a12ab9e1SAlexandre Belloni  *
5d8da8665SAlexandre Belloni  * Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com>
6a12ab9e1SAlexandre Belloni  */
7a12ab9e1SAlexandre Belloni 
8d8da8665SAlexandre Belloni #include <errno.h>
9d8da8665SAlexandre Belloni #include <fcntl.h>
10a12ab9e1SAlexandre Belloni #include <linux/rtc.h>
11d8da8665SAlexandre Belloni #include <stdio.h>
12d8da8665SAlexandre Belloni #include <stdlib.h>
13a12ab9e1SAlexandre Belloni #include <sys/ioctl.h>
14a12ab9e1SAlexandre Belloni #include <sys/time.h>
15a12ab9e1SAlexandre Belloni #include <sys/types.h>
16d8da8665SAlexandre Belloni #include <time.h>
17a12ab9e1SAlexandre Belloni #include <unistd.h>
18a12ab9e1SAlexandre Belloni 
19d8da8665SAlexandre Belloni #include "../kselftest_harness.h"
20a12ab9e1SAlexandre Belloni 
21d8da8665SAlexandre Belloni #define NUM_UIE 3
22d8da8665SAlexandre Belloni #define ALARM_DELTA 3
23*2aaa36e9SMateusz Jończyk #define READ_LOOP_DURATION_SEC 30
24*2aaa36e9SMateusz Jończyk #define READ_LOOP_SLEEP_MS 11
25a12ab9e1SAlexandre Belloni 
26d8da8665SAlexandre Belloni static char *rtc_file = "/dev/rtc0";
27d8da8665SAlexandre Belloni 
28d8da8665SAlexandre Belloni FIXTURE(rtc) {
29d8da8665SAlexandre Belloni 	int fd;
30a12ab9e1SAlexandre Belloni };
31a12ab9e1SAlexandre Belloni 
32d8da8665SAlexandre Belloni FIXTURE_SETUP(rtc) {
33d8da8665SAlexandre Belloni 	self->fd = open(rtc_file, O_RDONLY);
34d8da8665SAlexandre Belloni 	ASSERT_NE(-1, self->fd);
35d8da8665SAlexandre Belloni }
36a12ab9e1SAlexandre Belloni 
37d8da8665SAlexandre Belloni FIXTURE_TEARDOWN(rtc) {
38d8da8665SAlexandre Belloni 	close(self->fd);
39d8da8665SAlexandre Belloni }
40d8da8665SAlexandre Belloni 
41d8da8665SAlexandre Belloni TEST_F(rtc, date_read) {
42d8da8665SAlexandre Belloni 	int rc;
43d8da8665SAlexandre Belloni 	struct rtc_time rtc_tm;
44d8da8665SAlexandre Belloni 
45d8da8665SAlexandre Belloni 	/* Read the RTC time/date */
46d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
47d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
48d8da8665SAlexandre Belloni 
49d8da8665SAlexandre Belloni 	TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
50d8da8665SAlexandre Belloni 	       rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
51d8da8665SAlexandre Belloni 	       rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
52d8da8665SAlexandre Belloni }
53d8da8665SAlexandre Belloni 
54*2aaa36e9SMateusz Jończyk static time_t rtc_time_to_timestamp(struct rtc_time *rtc_time)
55*2aaa36e9SMateusz Jończyk {
56*2aaa36e9SMateusz Jończyk 	struct tm tm_time = {
57*2aaa36e9SMateusz Jończyk 	       .tm_sec = rtc_time->tm_sec,
58*2aaa36e9SMateusz Jończyk 	       .tm_min = rtc_time->tm_min,
59*2aaa36e9SMateusz Jończyk 	       .tm_hour = rtc_time->tm_hour,
60*2aaa36e9SMateusz Jończyk 	       .tm_mday = rtc_time->tm_mday,
61*2aaa36e9SMateusz Jończyk 	       .tm_mon = rtc_time->tm_mon,
62*2aaa36e9SMateusz Jończyk 	       .tm_year = rtc_time->tm_year,
63*2aaa36e9SMateusz Jończyk 	};
64*2aaa36e9SMateusz Jończyk 
65*2aaa36e9SMateusz Jończyk 	return mktime(&tm_time);
66*2aaa36e9SMateusz Jończyk }
67*2aaa36e9SMateusz Jończyk 
68*2aaa36e9SMateusz Jończyk static void nanosleep_with_retries(long ns)
69*2aaa36e9SMateusz Jończyk {
70*2aaa36e9SMateusz Jończyk 	struct timespec req = {
71*2aaa36e9SMateusz Jończyk 		.tv_sec = 0,
72*2aaa36e9SMateusz Jończyk 		.tv_nsec = ns,
73*2aaa36e9SMateusz Jończyk 	};
74*2aaa36e9SMateusz Jończyk 	struct timespec rem;
75*2aaa36e9SMateusz Jończyk 
76*2aaa36e9SMateusz Jończyk 	while (nanosleep(&req, &rem) != 0) {
77*2aaa36e9SMateusz Jończyk 		req.tv_sec = rem.tv_sec;
78*2aaa36e9SMateusz Jończyk 		req.tv_nsec = rem.tv_nsec;
79*2aaa36e9SMateusz Jończyk 	}
80*2aaa36e9SMateusz Jończyk }
81*2aaa36e9SMateusz Jończyk 
82*2aaa36e9SMateusz Jończyk TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
83*2aaa36e9SMateusz Jończyk 	int rc;
84*2aaa36e9SMateusz Jończyk 	long iter_count = 0;
85*2aaa36e9SMateusz Jończyk 	struct rtc_time rtc_tm;
86*2aaa36e9SMateusz Jończyk 	time_t start_rtc_read, prev_rtc_read;
87*2aaa36e9SMateusz Jończyk 
88*2aaa36e9SMateusz Jończyk 	TH_LOG("Continuously reading RTC time for %ds (with %dms breaks after every read).",
89*2aaa36e9SMateusz Jończyk 	       READ_LOOP_DURATION_SEC, READ_LOOP_SLEEP_MS);
90*2aaa36e9SMateusz Jończyk 
91*2aaa36e9SMateusz Jończyk 	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
92*2aaa36e9SMateusz Jończyk 	ASSERT_NE(-1, rc);
93*2aaa36e9SMateusz Jończyk 	start_rtc_read = rtc_time_to_timestamp(&rtc_tm);
94*2aaa36e9SMateusz Jończyk 	prev_rtc_read = start_rtc_read;
95*2aaa36e9SMateusz Jończyk 
96*2aaa36e9SMateusz Jończyk 	do  {
97*2aaa36e9SMateusz Jończyk 		time_t rtc_read;
98*2aaa36e9SMateusz Jończyk 
99*2aaa36e9SMateusz Jończyk 		rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
100*2aaa36e9SMateusz Jończyk 		ASSERT_NE(-1, rc);
101*2aaa36e9SMateusz Jończyk 
102*2aaa36e9SMateusz Jończyk 		rtc_read = rtc_time_to_timestamp(&rtc_tm);
103*2aaa36e9SMateusz Jończyk 		/* Time should not go backwards */
104*2aaa36e9SMateusz Jończyk 		ASSERT_LE(prev_rtc_read, rtc_read);
105*2aaa36e9SMateusz Jończyk 		/* Time should not increase more then 1s at a time */
106*2aaa36e9SMateusz Jończyk 		ASSERT_GE(prev_rtc_read + 1, rtc_read);
107*2aaa36e9SMateusz Jończyk 
108*2aaa36e9SMateusz Jończyk 		/* Sleep 11ms to avoid killing / overheating the RTC */
109*2aaa36e9SMateusz Jończyk 		nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
110*2aaa36e9SMateusz Jończyk 
111*2aaa36e9SMateusz Jończyk 		prev_rtc_read = rtc_read;
112*2aaa36e9SMateusz Jończyk 		iter_count++;
113*2aaa36e9SMateusz Jończyk 	} while (prev_rtc_read <= start_rtc_read + READ_LOOP_DURATION_SEC);
114*2aaa36e9SMateusz Jończyk 
115*2aaa36e9SMateusz Jończyk 	TH_LOG("Performed %ld RTC time reads.", iter_count);
116*2aaa36e9SMateusz Jończyk }
117*2aaa36e9SMateusz Jończyk 
118eff82a26SAlexandre Belloni TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
119d8da8665SAlexandre Belloni 	int i, rc, irq = 0;
120d8da8665SAlexandre Belloni 	unsigned long data;
121d8da8665SAlexandre Belloni 
122d8da8665SAlexandre Belloni 	/* Turn on update interrupts */
123d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_UIE_ON, 0);
124d8da8665SAlexandre Belloni 	if (rc == -1) {
125d8da8665SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
126d8da8665SAlexandre Belloni 		TH_LOG("skip update IRQs not supported.");
127d8da8665SAlexandre Belloni 		return;
128d8da8665SAlexandre Belloni 	}
129d8da8665SAlexandre Belloni 
130d8da8665SAlexandre Belloni 	for (i = 0; i < NUM_UIE; i++) {
131d8da8665SAlexandre Belloni 		/* This read will block */
132d8da8665SAlexandre Belloni 		rc = read(self->fd, &data, sizeof(data));
133d8da8665SAlexandre Belloni 		ASSERT_NE(-1, rc);
134d8da8665SAlexandre Belloni 		irq++;
135d8da8665SAlexandre Belloni 	}
136d8da8665SAlexandre Belloni 
137d8da8665SAlexandre Belloni 	EXPECT_EQ(NUM_UIE, irq);
138d8da8665SAlexandre Belloni 
139d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
140d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
141d8da8665SAlexandre Belloni }
142d8da8665SAlexandre Belloni 
143d8da8665SAlexandre Belloni TEST_F(rtc, uie_select) {
144d8da8665SAlexandre Belloni 	int i, rc, irq = 0;
145d8da8665SAlexandre Belloni 	unsigned long data;
146d8da8665SAlexandre Belloni 
147d8da8665SAlexandre Belloni 	/* Turn on update interrupts */
148d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_UIE_ON, 0);
149d8da8665SAlexandre Belloni 	if (rc == -1) {
150d8da8665SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
151d8da8665SAlexandre Belloni 		TH_LOG("skip update IRQs not supported.");
152d8da8665SAlexandre Belloni 		return;
153d8da8665SAlexandre Belloni 	}
154d8da8665SAlexandre Belloni 
155d8da8665SAlexandre Belloni 	for (i = 0; i < NUM_UIE; i++) {
156d8da8665SAlexandre Belloni 		struct timeval tv = { .tv_sec = 2 };
157d8da8665SAlexandre Belloni 		fd_set readfds;
158d8da8665SAlexandre Belloni 
159d8da8665SAlexandre Belloni 		FD_ZERO(&readfds);
160d8da8665SAlexandre Belloni 		FD_SET(self->fd, &readfds);
161d8da8665SAlexandre Belloni 		/* The select will wait until an RTC interrupt happens. */
162d8da8665SAlexandre Belloni 		rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
163d8da8665SAlexandre Belloni 		ASSERT_NE(-1, rc);
164d8da8665SAlexandre Belloni 		ASSERT_NE(0, rc);
165d8da8665SAlexandre Belloni 
166d8da8665SAlexandre Belloni 		/* This read won't block */
167d8da8665SAlexandre Belloni 		rc = read(self->fd, &data, sizeof(unsigned long));
168d8da8665SAlexandre Belloni 		ASSERT_NE(-1, rc);
169d8da8665SAlexandre Belloni 		irq++;
170d8da8665SAlexandre Belloni 	}
171d8da8665SAlexandre Belloni 
172d8da8665SAlexandre Belloni 	EXPECT_EQ(NUM_UIE, irq);
173d8da8665SAlexandre Belloni 
174d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
175d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
176d8da8665SAlexandre Belloni }
177d8da8665SAlexandre Belloni 
178d8da8665SAlexandre Belloni TEST_F(rtc, alarm_alm_set) {
179d8da8665SAlexandre Belloni 	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
180d8da8665SAlexandre Belloni 	unsigned long data;
181d8da8665SAlexandre Belloni 	struct rtc_time tm;
182d8da8665SAlexandre Belloni 	fd_set readfds;
183d8da8665SAlexandre Belloni 	time_t secs, new;
184d8da8665SAlexandre Belloni 	int rc;
185d8da8665SAlexandre Belloni 
186d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
187d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
188d8da8665SAlexandre Belloni 
189d8da8665SAlexandre Belloni 	secs = timegm((struct tm *)&tm) + ALARM_DELTA;
190d8da8665SAlexandre Belloni 	gmtime_r(&secs, (struct tm *)&tm);
191d8da8665SAlexandre Belloni 
192d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
193d8da8665SAlexandre Belloni 	if (rc == -1) {
194d8da8665SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
195d8da8665SAlexandre Belloni 		TH_LOG("skip alarms are not supported.");
196d8da8665SAlexandre Belloni 		return;
197d8da8665SAlexandre Belloni 	}
198d8da8665SAlexandre Belloni 
199d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
200d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
201d8da8665SAlexandre Belloni 
202d8da8665SAlexandre Belloni 	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
203d8da8665SAlexandre Belloni 	       tm.tm_hour, tm.tm_min, tm.tm_sec);
204d8da8665SAlexandre Belloni 
205d8da8665SAlexandre Belloni 	/* Enable alarm interrupts */
206d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_AIE_ON, 0);
207d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
208d8da8665SAlexandre Belloni 
209d8da8665SAlexandre Belloni 	FD_ZERO(&readfds);
210d8da8665SAlexandre Belloni 	FD_SET(self->fd, &readfds);
211d8da8665SAlexandre Belloni 
212d8da8665SAlexandre Belloni 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
213d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
214fdac9448SAlexandre Belloni 	ASSERT_NE(0, rc);
215d8da8665SAlexandre Belloni 
216d8da8665SAlexandre Belloni 	/* Disable alarm interrupts */
217d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
218d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
219d8da8665SAlexandre Belloni 
220d8da8665SAlexandre Belloni 	rc = read(self->fd, &data, sizeof(unsigned long));
221d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
222d8da8665SAlexandre Belloni 	TH_LOG("data: %lx", data);
223d8da8665SAlexandre Belloni 
224d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
225d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
226d8da8665SAlexandre Belloni 
227d8da8665SAlexandre Belloni 	new = timegm((struct tm *)&tm);
228d8da8665SAlexandre Belloni 	ASSERT_EQ(new, secs);
229d8da8665SAlexandre Belloni }
230d8da8665SAlexandre Belloni 
231d8da8665SAlexandre Belloni TEST_F(rtc, alarm_wkalm_set) {
232d8da8665SAlexandre Belloni 	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
233d8da8665SAlexandre Belloni 	struct rtc_wkalrm alarm = { 0 };
234d8da8665SAlexandre Belloni 	struct rtc_time tm;
235d8da8665SAlexandre Belloni 	unsigned long data;
236d8da8665SAlexandre Belloni 	fd_set readfds;
237d8da8665SAlexandre Belloni 	time_t secs, new;
238d8da8665SAlexandre Belloni 	int rc;
239d8da8665SAlexandre Belloni 
240d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
241d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
242d8da8665SAlexandre Belloni 
243d8da8665SAlexandre Belloni 	secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
244d8da8665SAlexandre Belloni 	gmtime_r(&secs, (struct tm *)&alarm.time);
245d8da8665SAlexandre Belloni 
246d8da8665SAlexandre Belloni 	alarm.enabled = 1;
247d8da8665SAlexandre Belloni 
248d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
249d8da8665SAlexandre Belloni 	if (rc == -1) {
250d8da8665SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
251d8da8665SAlexandre Belloni 		TH_LOG("skip alarms are not supported.");
252d8da8665SAlexandre Belloni 		return;
253d8da8665SAlexandre Belloni 	}
254d8da8665SAlexandre Belloni 
255d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
256d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
257d8da8665SAlexandre Belloni 
258d8da8665SAlexandre Belloni 	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
259d8da8665SAlexandre Belloni 	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
260d8da8665SAlexandre Belloni 	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
261d8da8665SAlexandre Belloni 	       alarm.time.tm_min, alarm.time.tm_sec);
262d8da8665SAlexandre Belloni 
263d8da8665SAlexandre Belloni 	FD_ZERO(&readfds);
264d8da8665SAlexandre Belloni 	FD_SET(self->fd, &readfds);
265d8da8665SAlexandre Belloni 
266d8da8665SAlexandre Belloni 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
267d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
268fdac9448SAlexandre Belloni 	ASSERT_NE(0, rc);
269d8da8665SAlexandre Belloni 
270d8da8665SAlexandre Belloni 	rc = read(self->fd, &data, sizeof(unsigned long));
271d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
272d8da8665SAlexandre Belloni 
273d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
274d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
275d8da8665SAlexandre Belloni 
276d8da8665SAlexandre Belloni 	new = timegm((struct tm *)&tm);
277d8da8665SAlexandre Belloni 	ASSERT_EQ(new, secs);
278d8da8665SAlexandre Belloni }
279d8da8665SAlexandre Belloni 
280eff82a26SAlexandre Belloni TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
2817b302772SAlexandre Belloni 	struct timeval tv = { .tv_sec = 62 };
2827b302772SAlexandre Belloni 	unsigned long data;
2837b302772SAlexandre Belloni 	struct rtc_time tm;
2847b302772SAlexandre Belloni 	fd_set readfds;
2857b302772SAlexandre Belloni 	time_t secs, new;
2867b302772SAlexandre Belloni 	int rc;
2877b302772SAlexandre Belloni 
2887b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
2897b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
2907b302772SAlexandre Belloni 
2917b302772SAlexandre Belloni 	secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
2927b302772SAlexandre Belloni 	gmtime_r(&secs, (struct tm *)&tm);
2937b302772SAlexandre Belloni 
2947b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
2957b302772SAlexandre Belloni 	if (rc == -1) {
2967b302772SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
2977b302772SAlexandre Belloni 		TH_LOG("skip alarms are not supported.");
2987b302772SAlexandre Belloni 		return;
2997b302772SAlexandre Belloni 	}
3007b302772SAlexandre Belloni 
3017b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
3027b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3037b302772SAlexandre Belloni 
3047b302772SAlexandre Belloni 	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
3057b302772SAlexandre Belloni 	       tm.tm_hour, tm.tm_min, tm.tm_sec);
3067b302772SAlexandre Belloni 
3077b302772SAlexandre Belloni 	/* Enable alarm interrupts */
3087b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_AIE_ON, 0);
3097b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3107b302772SAlexandre Belloni 
3117b302772SAlexandre Belloni 	FD_ZERO(&readfds);
3127b302772SAlexandre Belloni 	FD_SET(self->fd, &readfds);
3137b302772SAlexandre Belloni 
3147b302772SAlexandre Belloni 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
3157b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3167b302772SAlexandre Belloni 	ASSERT_NE(0, rc);
3177b302772SAlexandre Belloni 
3187b302772SAlexandre Belloni 	/* Disable alarm interrupts */
3197b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
3207b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3217b302772SAlexandre Belloni 
3227b302772SAlexandre Belloni 	rc = read(self->fd, &data, sizeof(unsigned long));
3237b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3247b302772SAlexandre Belloni 	TH_LOG("data: %lx", data);
3257b302772SAlexandre Belloni 
3267b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
3277b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3287b302772SAlexandre Belloni 
3297b302772SAlexandre Belloni 	new = timegm((struct tm *)&tm);
3307b302772SAlexandre Belloni 	ASSERT_EQ(new, secs);
3317b302772SAlexandre Belloni }
3327b302772SAlexandre Belloni 
333eff82a26SAlexandre Belloni TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
3347b302772SAlexandre Belloni 	struct timeval tv = { .tv_sec = 62 };
3357b302772SAlexandre Belloni 	struct rtc_wkalrm alarm = { 0 };
3367b302772SAlexandre Belloni 	struct rtc_time tm;
3377b302772SAlexandre Belloni 	unsigned long data;
3387b302772SAlexandre Belloni 	fd_set readfds;
3397b302772SAlexandre Belloni 	time_t secs, new;
3407b302772SAlexandre Belloni 	int rc;
3417b302772SAlexandre Belloni 
3427b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
3437b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3447b302772SAlexandre Belloni 
3457b302772SAlexandre Belloni 	secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
3467b302772SAlexandre Belloni 	gmtime_r(&secs, (struct tm *)&alarm.time);
3477b302772SAlexandre Belloni 
3487b302772SAlexandre Belloni 	alarm.enabled = 1;
3497b302772SAlexandre Belloni 
3507b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
3517b302772SAlexandre Belloni 	if (rc == -1) {
3527b302772SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
3537b302772SAlexandre Belloni 		TH_LOG("skip alarms are not supported.");
3547b302772SAlexandre Belloni 		return;
3557b302772SAlexandre Belloni 	}
3567b302772SAlexandre Belloni 
3577b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
3587b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3597b302772SAlexandre Belloni 
3607b302772SAlexandre Belloni 	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
3617b302772SAlexandre Belloni 	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
3627b302772SAlexandre Belloni 	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
3637b302772SAlexandre Belloni 	       alarm.time.tm_min, alarm.time.tm_sec);
3647b302772SAlexandre Belloni 
3657b302772SAlexandre Belloni 	FD_ZERO(&readfds);
3667b302772SAlexandre Belloni 	FD_SET(self->fd, &readfds);
3677b302772SAlexandre Belloni 
3687b302772SAlexandre Belloni 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
3697b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3707b302772SAlexandre Belloni 	ASSERT_NE(0, rc);
3717b302772SAlexandre Belloni 
3727b302772SAlexandre Belloni 	rc = read(self->fd, &data, sizeof(unsigned long));
3737b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3747b302772SAlexandre Belloni 
3757b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
3767b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3777b302772SAlexandre Belloni 
3787b302772SAlexandre Belloni 	new = timegm((struct tm *)&tm);
3797b302772SAlexandre Belloni 	ASSERT_EQ(new, secs);
3807b302772SAlexandre Belloni }
3817b302772SAlexandre Belloni 
382d8da8665SAlexandre Belloni static void __attribute__((constructor))
383d8da8665SAlexandre Belloni __constructor_order_last(void)
384d8da8665SAlexandre Belloni {
385d8da8665SAlexandre Belloni 	if (!__constructor_order)
386d8da8665SAlexandre Belloni 		__constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
387a12ab9e1SAlexandre Belloni }
388a12ab9e1SAlexandre Belloni 
389a12ab9e1SAlexandre Belloni int main(int argc, char **argv)
390a12ab9e1SAlexandre Belloni {
391a12ab9e1SAlexandre Belloni 	switch (argc) {
392a12ab9e1SAlexandre Belloni 	case 2:
393d8da8665SAlexandre Belloni 		rtc_file = argv[1];
394a12ab9e1SAlexandre Belloni 		/* FALLTHROUGH */
395a12ab9e1SAlexandre Belloni 	case 1:
396a12ab9e1SAlexandre Belloni 		break;
397a12ab9e1SAlexandre Belloni 	default:
398d8da8665SAlexandre Belloni 		fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
399a12ab9e1SAlexandre Belloni 		return 1;
400a12ab9e1SAlexandre Belloni 	}
401a12ab9e1SAlexandre Belloni 
402d8da8665SAlexandre Belloni 	return test_harness_run(argc, argv);
403a12ab9e1SAlexandre Belloni }
404