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 <[email protected]>
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
232aaa36e9SMateusz Jończyk #define READ_LOOP_DURATION_SEC 30
242aaa36e9SMateusz Jończyk #define READ_LOOP_SLEEP_MS 11
25a12ab9e1SAlexandre Belloni 
26d8da8665SAlexandre Belloni static char *rtc_file = "/dev/rtc0";
27d8da8665SAlexandre Belloni 
282a027d6bSJoseph Jang enum rtc_alarm_state {
292a027d6bSJoseph Jang 	RTC_ALARM_UNKNOWN,
302a027d6bSJoseph Jang 	RTC_ALARM_ENABLED,
312a027d6bSJoseph Jang 	RTC_ALARM_DISABLED,
32*0cd73ab4SWolfram Sang 	RTC_ALARM_RES_MINUTE,
332a027d6bSJoseph Jang };
342a027d6bSJoseph Jang 
FIXTURE(rtc)35d8da8665SAlexandre Belloni FIXTURE(rtc) {
36d8da8665SAlexandre Belloni 	int fd;
37a12ab9e1SAlexandre Belloni };
38a12ab9e1SAlexandre Belloni 
FIXTURE_SETUP(rtc)39d8da8665SAlexandre Belloni FIXTURE_SETUP(rtc) {
40d8da8665SAlexandre Belloni 	self->fd = open(rtc_file, O_RDONLY);
41d8da8665SAlexandre Belloni }
42a12ab9e1SAlexandre Belloni 
FIXTURE_TEARDOWN(rtc)43d8da8665SAlexandre Belloni FIXTURE_TEARDOWN(rtc) {
44d8da8665SAlexandre Belloni 	close(self->fd);
45d8da8665SAlexandre Belloni }
46d8da8665SAlexandre Belloni 
TEST_F(rtc,date_read)47d8da8665SAlexandre Belloni TEST_F(rtc, date_read) {
48d8da8665SAlexandre Belloni 	int rc;
49d8da8665SAlexandre Belloni 	struct rtc_time rtc_tm;
50d8da8665SAlexandre Belloni 
5135eee9a3SAlexandre Belloni 	if (self->fd == -1 && errno == ENOENT)
5235eee9a3SAlexandre Belloni 		SKIP(return, "Skipping test since %s does not exist", rtc_file);
5335eee9a3SAlexandre Belloni 	ASSERT_NE(-1, self->fd);
5435eee9a3SAlexandre Belloni 
55d8da8665SAlexandre Belloni 	/* Read the RTC time/date */
56d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
57d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
58d8da8665SAlexandre Belloni 
59d8da8665SAlexandre Belloni 	TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
60d8da8665SAlexandre Belloni 	       rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
61d8da8665SAlexandre Belloni 	       rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
62d8da8665SAlexandre Belloni }
63d8da8665SAlexandre Belloni 
rtc_time_to_timestamp(struct rtc_time * rtc_time)642aaa36e9SMateusz Jończyk static time_t rtc_time_to_timestamp(struct rtc_time *rtc_time)
652aaa36e9SMateusz Jończyk {
662aaa36e9SMateusz Jończyk 	struct tm tm_time = {
672aaa36e9SMateusz Jończyk 	       .tm_sec = rtc_time->tm_sec,
682aaa36e9SMateusz Jończyk 	       .tm_min = rtc_time->tm_min,
692aaa36e9SMateusz Jończyk 	       .tm_hour = rtc_time->tm_hour,
702aaa36e9SMateusz Jończyk 	       .tm_mday = rtc_time->tm_mday,
712aaa36e9SMateusz Jończyk 	       .tm_mon = rtc_time->tm_mon,
722aaa36e9SMateusz Jończyk 	       .tm_year = rtc_time->tm_year,
732aaa36e9SMateusz Jończyk 	};
742aaa36e9SMateusz Jończyk 
752aaa36e9SMateusz Jończyk 	return mktime(&tm_time);
762aaa36e9SMateusz Jończyk }
772aaa36e9SMateusz Jończyk 
nanosleep_with_retries(long ns)782aaa36e9SMateusz Jończyk static void nanosleep_with_retries(long ns)
792aaa36e9SMateusz Jończyk {
802aaa36e9SMateusz Jończyk 	struct timespec req = {
812aaa36e9SMateusz Jończyk 		.tv_sec = 0,
822aaa36e9SMateusz Jończyk 		.tv_nsec = ns,
832aaa36e9SMateusz Jończyk 	};
842aaa36e9SMateusz Jończyk 	struct timespec rem;
852aaa36e9SMateusz Jończyk 
862aaa36e9SMateusz Jończyk 	while (nanosleep(&req, &rem) != 0) {
872aaa36e9SMateusz Jończyk 		req.tv_sec = rem.tv_sec;
882aaa36e9SMateusz Jończyk 		req.tv_nsec = rem.tv_nsec;
892aaa36e9SMateusz Jończyk 	}
902aaa36e9SMateusz Jończyk }
912aaa36e9SMateusz Jończyk 
get_rtc_alarm_state(int fd,int need_seconds)92*0cd73ab4SWolfram Sang static enum rtc_alarm_state get_rtc_alarm_state(int fd, int need_seconds)
932a027d6bSJoseph Jang {
942a027d6bSJoseph Jang 	struct rtc_param param = { 0 };
952a027d6bSJoseph Jang 	int rc;
962a027d6bSJoseph Jang 
972a027d6bSJoseph Jang 	/* Validate kernel reflects unsupported RTC alarm state */
982a027d6bSJoseph Jang 	param.param = RTC_PARAM_FEATURES;
992a027d6bSJoseph Jang 	param.index = 0;
1002a027d6bSJoseph Jang 	rc = ioctl(fd, RTC_PARAM_GET, &param);
1012a027d6bSJoseph Jang 	if (rc < 0)
1022a027d6bSJoseph Jang 		return RTC_ALARM_UNKNOWN;
1032a027d6bSJoseph Jang 
1042a027d6bSJoseph Jang 	if ((param.uvalue & _BITUL(RTC_FEATURE_ALARM)) == 0)
1052a027d6bSJoseph Jang 		return RTC_ALARM_DISABLED;
1062a027d6bSJoseph Jang 
107*0cd73ab4SWolfram Sang 	/* Check if alarm has desired granularity */
108*0cd73ab4SWolfram Sang 	if (need_seconds && (param.uvalue & _BITUL(RTC_FEATURE_ALARM_RES_MINUTE)))
109*0cd73ab4SWolfram Sang 		return RTC_ALARM_RES_MINUTE;
110*0cd73ab4SWolfram Sang 
1112a027d6bSJoseph Jang 	return RTC_ALARM_ENABLED;
1122a027d6bSJoseph Jang }
1132a027d6bSJoseph Jang 
1142aaa36e9SMateusz Jończyk TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
1152aaa36e9SMateusz Jończyk 	int rc;
1162aaa36e9SMateusz Jończyk 	long iter_count = 0;
1172aaa36e9SMateusz Jończyk 	struct rtc_time rtc_tm;
1182aaa36e9SMateusz Jończyk 	time_t start_rtc_read, prev_rtc_read;
1192aaa36e9SMateusz Jończyk 
12035eee9a3SAlexandre Belloni 	if (self->fd == -1 && errno == ENOENT)
12135eee9a3SAlexandre Belloni 		SKIP(return, "Skipping test since %s does not exist", rtc_file);
12235eee9a3SAlexandre Belloni 	ASSERT_NE(-1, self->fd);
12335eee9a3SAlexandre Belloni 
1242aaa36e9SMateusz Jończyk 	TH_LOG("Continuously reading RTC time for %ds (with %dms breaks after every read).",
1252aaa36e9SMateusz Jończyk 	       READ_LOOP_DURATION_SEC, READ_LOOP_SLEEP_MS);
1262aaa36e9SMateusz Jończyk 
1272aaa36e9SMateusz Jończyk 	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
1282aaa36e9SMateusz Jończyk 	ASSERT_NE(-1, rc);
1292aaa36e9SMateusz Jończyk 	start_rtc_read = rtc_time_to_timestamp(&rtc_tm);
1302aaa36e9SMateusz Jończyk 	prev_rtc_read = start_rtc_read;
1312aaa36e9SMateusz Jończyk 
1322aaa36e9SMateusz Jończyk 	do  {
1332aaa36e9SMateusz Jończyk 		time_t rtc_read;
1342aaa36e9SMateusz Jończyk 
1352aaa36e9SMateusz Jończyk 		rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
1362aaa36e9SMateusz Jończyk 		ASSERT_NE(-1, rc);
1372aaa36e9SMateusz Jończyk 
1382aaa36e9SMateusz Jończyk 		rtc_read = rtc_time_to_timestamp(&rtc_tm);
1392aaa36e9SMateusz Jończyk 		/* Time should not go backwards */
1402aaa36e9SMateusz Jończyk 		ASSERT_LE(prev_rtc_read, rtc_read);
1412aaa36e9SMateusz Jończyk 		/* Time should not increase more then 1s at a time */
1422aaa36e9SMateusz Jończyk 		ASSERT_GE(prev_rtc_read + 1, rtc_read);
1432aaa36e9SMateusz Jończyk 
1442aaa36e9SMateusz Jończyk 		/* Sleep 11ms to avoid killing / overheating the RTC */
1452aaa36e9SMateusz Jończyk 		nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
1462aaa36e9SMateusz Jończyk 
1472aaa36e9SMateusz Jończyk 		prev_rtc_read = rtc_read;
1482aaa36e9SMateusz Jończyk 		iter_count++;
1492aaa36e9SMateusz Jończyk 	} while (prev_rtc_read <= start_rtc_read + READ_LOOP_DURATION_SEC);
1502aaa36e9SMateusz Jończyk 
1512aaa36e9SMateusz Jończyk 	TH_LOG("Performed %ld RTC time reads.", iter_count);
1522aaa36e9SMateusz Jończyk }
1532aaa36e9SMateusz Jończyk 
154eff82a26SAlexandre Belloni TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
155d8da8665SAlexandre Belloni 	int i, rc, irq = 0;
156d8da8665SAlexandre Belloni 	unsigned long data;
157d8da8665SAlexandre Belloni 
15835eee9a3SAlexandre Belloni 	if (self->fd == -1 && errno == ENOENT)
15935eee9a3SAlexandre Belloni 		SKIP(return, "Skipping test since %s does not exist", rtc_file);
16035eee9a3SAlexandre Belloni 	ASSERT_NE(-1, self->fd);
16135eee9a3SAlexandre Belloni 
162d8da8665SAlexandre Belloni 	/* Turn on update interrupts */
163d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_UIE_ON, 0);
164d8da8665SAlexandre Belloni 	if (rc == -1) {
165d8da8665SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
166d8da8665SAlexandre Belloni 		TH_LOG("skip update IRQs not supported.");
167d8da8665SAlexandre Belloni 		return;
168d8da8665SAlexandre Belloni 	}
169d8da8665SAlexandre Belloni 
170d8da8665SAlexandre Belloni 	for (i = 0; i < NUM_UIE; i++) {
171d8da8665SAlexandre Belloni 		/* This read will block */
172d8da8665SAlexandre Belloni 		rc = read(self->fd, &data, sizeof(data));
173d8da8665SAlexandre Belloni 		ASSERT_NE(-1, rc);
174d8da8665SAlexandre Belloni 		irq++;
175d8da8665SAlexandre Belloni 	}
176d8da8665SAlexandre Belloni 
177d8da8665SAlexandre Belloni 	EXPECT_EQ(NUM_UIE, irq);
178d8da8665SAlexandre Belloni 
179d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
180d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
181d8da8665SAlexandre Belloni }
182d8da8665SAlexandre Belloni 
TEST_F(rtc,uie_select)183d8da8665SAlexandre Belloni TEST_F(rtc, uie_select) {
184d8da8665SAlexandre Belloni 	int i, rc, irq = 0;
185d8da8665SAlexandre Belloni 	unsigned long data;
186d8da8665SAlexandre Belloni 
18735eee9a3SAlexandre Belloni 	if (self->fd == -1 && errno == ENOENT)
18835eee9a3SAlexandre Belloni 		SKIP(return, "Skipping test since %s does not exist", rtc_file);
18935eee9a3SAlexandre Belloni 	ASSERT_NE(-1, self->fd);
19035eee9a3SAlexandre Belloni 
191d8da8665SAlexandre Belloni 	/* Turn on update interrupts */
192d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_UIE_ON, 0);
193d8da8665SAlexandre Belloni 	if (rc == -1) {
194d8da8665SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
195d8da8665SAlexandre Belloni 		TH_LOG("skip update IRQs not supported.");
196d8da8665SAlexandre Belloni 		return;
197d8da8665SAlexandre Belloni 	}
198d8da8665SAlexandre Belloni 
199d8da8665SAlexandre Belloni 	for (i = 0; i < NUM_UIE; i++) {
200d8da8665SAlexandre Belloni 		struct timeval tv = { .tv_sec = 2 };
201d8da8665SAlexandre Belloni 		fd_set readfds;
202d8da8665SAlexandre Belloni 
203d8da8665SAlexandre Belloni 		FD_ZERO(&readfds);
204d8da8665SAlexandre Belloni 		FD_SET(self->fd, &readfds);
205d8da8665SAlexandre Belloni 		/* The select will wait until an RTC interrupt happens. */
206d8da8665SAlexandre Belloni 		rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
207d8da8665SAlexandre Belloni 		ASSERT_NE(-1, rc);
208d8da8665SAlexandre Belloni 		ASSERT_NE(0, rc);
209d8da8665SAlexandre Belloni 
210d8da8665SAlexandre Belloni 		/* This read won't block */
211d8da8665SAlexandre Belloni 		rc = read(self->fd, &data, sizeof(unsigned long));
212d8da8665SAlexandre Belloni 		ASSERT_NE(-1, rc);
213d8da8665SAlexandre Belloni 		irq++;
214d8da8665SAlexandre Belloni 	}
215d8da8665SAlexandre Belloni 
216d8da8665SAlexandre Belloni 	EXPECT_EQ(NUM_UIE, irq);
217d8da8665SAlexandre Belloni 
218d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
219d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
220d8da8665SAlexandre Belloni }
221d8da8665SAlexandre Belloni 
TEST_F(rtc,alarm_alm_set)222d8da8665SAlexandre Belloni TEST_F(rtc, alarm_alm_set) {
223d8da8665SAlexandre Belloni 	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
224d8da8665SAlexandre Belloni 	unsigned long data;
225d8da8665SAlexandre Belloni 	struct rtc_time tm;
226d8da8665SAlexandre Belloni 	fd_set readfds;
227d8da8665SAlexandre Belloni 	time_t secs, new;
228d8da8665SAlexandre Belloni 	int rc;
2292a027d6bSJoseph Jang 	enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
230d8da8665SAlexandre Belloni 
23135eee9a3SAlexandre Belloni 	if (self->fd == -1 && errno == ENOENT)
23235eee9a3SAlexandre Belloni 		SKIP(return, "Skipping test since %s does not exist", rtc_file);
23335eee9a3SAlexandre Belloni 	ASSERT_NE(-1, self->fd);
23435eee9a3SAlexandre Belloni 
235*0cd73ab4SWolfram Sang 	alarm_state = get_rtc_alarm_state(self->fd, 1);
2362a027d6bSJoseph Jang 	if (alarm_state == RTC_ALARM_DISABLED)
2372a027d6bSJoseph Jang 		SKIP(return, "Skipping test since alarms are not supported.");
238*0cd73ab4SWolfram Sang 	if (alarm_state == RTC_ALARM_RES_MINUTE)
239*0cd73ab4SWolfram Sang 		SKIP(return, "Skipping test since alarms has only minute granularity.");
2402a027d6bSJoseph Jang 
241d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
242d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
243d8da8665SAlexandre Belloni 
244d8da8665SAlexandre Belloni 	secs = timegm((struct tm *)&tm) + ALARM_DELTA;
245d8da8665SAlexandre Belloni 	gmtime_r(&secs, (struct tm *)&tm);
246d8da8665SAlexandre Belloni 
247d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
248d8da8665SAlexandre Belloni 	if (rc == -1) {
2492a027d6bSJoseph Jang 		/*
2502a027d6bSJoseph Jang 		 * Report error if rtc alarm was enabled. Fallback to check ioctl
2512a027d6bSJoseph Jang 		 * error number if rtc alarm state is unknown.
2522a027d6bSJoseph Jang 		 */
2532a027d6bSJoseph Jang 		ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
254d8da8665SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
255d8da8665SAlexandre Belloni 		TH_LOG("skip alarms are not supported.");
256d8da8665SAlexandre Belloni 		return;
257d8da8665SAlexandre Belloni 	}
258d8da8665SAlexandre Belloni 
259d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
260d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
261d8da8665SAlexandre Belloni 
262d8da8665SAlexandre Belloni 	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
263d8da8665SAlexandre Belloni 	       tm.tm_hour, tm.tm_min, tm.tm_sec);
264d8da8665SAlexandre Belloni 
265d8da8665SAlexandre Belloni 	/* Enable alarm interrupts */
266d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_AIE_ON, 0);
267d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
268d8da8665SAlexandre Belloni 
269d8da8665SAlexandre Belloni 	FD_ZERO(&readfds);
270d8da8665SAlexandre Belloni 	FD_SET(self->fd, &readfds);
271d8da8665SAlexandre Belloni 
272d8da8665SAlexandre Belloni 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
273d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
274fdac9448SAlexandre Belloni 	ASSERT_NE(0, rc);
275d8da8665SAlexandre Belloni 
276d8da8665SAlexandre Belloni 	/* Disable alarm interrupts */
277d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
278d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
279d8da8665SAlexandre Belloni 
280d8da8665SAlexandre Belloni 	rc = read(self->fd, &data, sizeof(unsigned long));
281d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
282d8da8665SAlexandre Belloni 	TH_LOG("data: %lx", data);
283d8da8665SAlexandre Belloni 
284d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
285d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
286d8da8665SAlexandre Belloni 
287d8da8665SAlexandre Belloni 	new = timegm((struct tm *)&tm);
288d8da8665SAlexandre Belloni 	ASSERT_EQ(new, secs);
289d8da8665SAlexandre Belloni }
290d8da8665SAlexandre Belloni 
TEST_F(rtc,alarm_wkalm_set)291d8da8665SAlexandre Belloni TEST_F(rtc, alarm_wkalm_set) {
292d8da8665SAlexandre Belloni 	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
293d8da8665SAlexandre Belloni 	struct rtc_wkalrm alarm = { 0 };
294d8da8665SAlexandre Belloni 	struct rtc_time tm;
295d8da8665SAlexandre Belloni 	unsigned long data;
296d8da8665SAlexandre Belloni 	fd_set readfds;
297d8da8665SAlexandre Belloni 	time_t secs, new;
298d8da8665SAlexandre Belloni 	int rc;
2992a027d6bSJoseph Jang 	enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
300d8da8665SAlexandre Belloni 
30135eee9a3SAlexandre Belloni 	if (self->fd == -1 && errno == ENOENT)
30235eee9a3SAlexandre Belloni 		SKIP(return, "Skipping test since %s does not exist", rtc_file);
30335eee9a3SAlexandre Belloni 	ASSERT_NE(-1, self->fd);
30435eee9a3SAlexandre Belloni 
305*0cd73ab4SWolfram Sang 	alarm_state = get_rtc_alarm_state(self->fd, 1);
3062a027d6bSJoseph Jang 	if (alarm_state == RTC_ALARM_DISABLED)
3072a027d6bSJoseph Jang 		SKIP(return, "Skipping test since alarms are not supported.");
308*0cd73ab4SWolfram Sang 	if (alarm_state == RTC_ALARM_RES_MINUTE)
309*0cd73ab4SWolfram Sang 		SKIP(return, "Skipping test since alarms has only minute granularity.");
3102a027d6bSJoseph Jang 
311d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
312d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
313d8da8665SAlexandre Belloni 
314d8da8665SAlexandre Belloni 	secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
315d8da8665SAlexandre Belloni 	gmtime_r(&secs, (struct tm *)&alarm.time);
316d8da8665SAlexandre Belloni 
317d8da8665SAlexandre Belloni 	alarm.enabled = 1;
318d8da8665SAlexandre Belloni 
319d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
320d8da8665SAlexandre Belloni 	if (rc == -1) {
3212a027d6bSJoseph Jang 		/*
3222a027d6bSJoseph Jang 		 * Report error if rtc alarm was enabled. Fallback to check ioctl
3232a027d6bSJoseph Jang 		 * error number if rtc alarm state is unknown.
3242a027d6bSJoseph Jang 		 */
3252a027d6bSJoseph Jang 		ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
326d8da8665SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
327d8da8665SAlexandre Belloni 		TH_LOG("skip alarms are not supported.");
328d8da8665SAlexandre Belloni 		return;
329d8da8665SAlexandre Belloni 	}
330d8da8665SAlexandre Belloni 
331d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
332d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
333d8da8665SAlexandre Belloni 
334d8da8665SAlexandre Belloni 	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
335d8da8665SAlexandre Belloni 	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
336d8da8665SAlexandre Belloni 	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
337d8da8665SAlexandre Belloni 	       alarm.time.tm_min, alarm.time.tm_sec);
338d8da8665SAlexandre Belloni 
339d8da8665SAlexandre Belloni 	FD_ZERO(&readfds);
340d8da8665SAlexandre Belloni 	FD_SET(self->fd, &readfds);
341d8da8665SAlexandre Belloni 
342d8da8665SAlexandre Belloni 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
343d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
344fdac9448SAlexandre Belloni 	ASSERT_NE(0, rc);
345d8da8665SAlexandre Belloni 
346d8da8665SAlexandre Belloni 	rc = read(self->fd, &data, sizeof(unsigned long));
347d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
348d8da8665SAlexandre Belloni 
349d8da8665SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
350d8da8665SAlexandre Belloni 	ASSERT_NE(-1, rc);
351d8da8665SAlexandre Belloni 
352d8da8665SAlexandre Belloni 	new = timegm((struct tm *)&tm);
353d8da8665SAlexandre Belloni 	ASSERT_EQ(new, secs);
354d8da8665SAlexandre Belloni }
355d8da8665SAlexandre Belloni 
356eff82a26SAlexandre Belloni TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
3577b302772SAlexandre Belloni 	struct timeval tv = { .tv_sec = 62 };
3587b302772SAlexandre Belloni 	unsigned long data;
3597b302772SAlexandre Belloni 	struct rtc_time tm;
3607b302772SAlexandre Belloni 	fd_set readfds;
3617b302772SAlexandre Belloni 	time_t secs, new;
3627b302772SAlexandre Belloni 	int rc;
3632a027d6bSJoseph Jang 	enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
3647b302772SAlexandre Belloni 
36535eee9a3SAlexandre Belloni 	if (self->fd == -1 && errno == ENOENT)
36635eee9a3SAlexandre Belloni 		SKIP(return, "Skipping test since %s does not exist", rtc_file);
36735eee9a3SAlexandre Belloni 	ASSERT_NE(-1, self->fd);
36835eee9a3SAlexandre Belloni 
369*0cd73ab4SWolfram Sang 	alarm_state = get_rtc_alarm_state(self->fd, 0);
3702a027d6bSJoseph Jang 	if (alarm_state == RTC_ALARM_DISABLED)
3712a027d6bSJoseph Jang 		SKIP(return, "Skipping test since alarms are not supported.");
3722a027d6bSJoseph Jang 
3737b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
3747b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3757b302772SAlexandre Belloni 
3767b302772SAlexandre Belloni 	secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
3777b302772SAlexandre Belloni 	gmtime_r(&secs, (struct tm *)&tm);
3787b302772SAlexandre Belloni 
3797b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
3807b302772SAlexandre Belloni 	if (rc == -1) {
3812a027d6bSJoseph Jang 		/*
3822a027d6bSJoseph Jang 		 * Report error if rtc alarm was enabled. Fallback to check ioctl
3832a027d6bSJoseph Jang 		 * error number if rtc alarm state is unknown.
3842a027d6bSJoseph Jang 		 */
3852a027d6bSJoseph Jang 		ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
3867b302772SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
3877b302772SAlexandre Belloni 		TH_LOG("skip alarms are not supported.");
3887b302772SAlexandre Belloni 		return;
3897b302772SAlexandre Belloni 	}
3907b302772SAlexandre Belloni 
3917b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
3927b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
3937b302772SAlexandre Belloni 
3947b302772SAlexandre Belloni 	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
3957b302772SAlexandre Belloni 	       tm.tm_hour, tm.tm_min, tm.tm_sec);
3967b302772SAlexandre Belloni 
3977b302772SAlexandre Belloni 	/* Enable alarm interrupts */
3987b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_AIE_ON, 0);
3997b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4007b302772SAlexandre Belloni 
4017b302772SAlexandre Belloni 	FD_ZERO(&readfds);
4027b302772SAlexandre Belloni 	FD_SET(self->fd, &readfds);
4037b302772SAlexandre Belloni 
4047b302772SAlexandre Belloni 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
4057b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4067b302772SAlexandre Belloni 	ASSERT_NE(0, rc);
4077b302772SAlexandre Belloni 
4087b302772SAlexandre Belloni 	/* Disable alarm interrupts */
4097b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
4107b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4117b302772SAlexandre Belloni 
4127b302772SAlexandre Belloni 	rc = read(self->fd, &data, sizeof(unsigned long));
4137b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4147b302772SAlexandre Belloni 	TH_LOG("data: %lx", data);
4157b302772SAlexandre Belloni 
4167b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
4177b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4187b302772SAlexandre Belloni 
4197b302772SAlexandre Belloni 	new = timegm((struct tm *)&tm);
4207b302772SAlexandre Belloni 	ASSERT_EQ(new, secs);
4217b302772SAlexandre Belloni }
4227b302772SAlexandre Belloni 
423eff82a26SAlexandre Belloni TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
4247b302772SAlexandre Belloni 	struct timeval tv = { .tv_sec = 62 };
4257b302772SAlexandre Belloni 	struct rtc_wkalrm alarm = { 0 };
4267b302772SAlexandre Belloni 	struct rtc_time tm;
4277b302772SAlexandre Belloni 	unsigned long data;
4287b302772SAlexandre Belloni 	fd_set readfds;
4297b302772SAlexandre Belloni 	time_t secs, new;
4307b302772SAlexandre Belloni 	int rc;
4312a027d6bSJoseph Jang 	enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
4327b302772SAlexandre Belloni 
43335eee9a3SAlexandre Belloni 	if (self->fd == -1 && errno == ENOENT)
43435eee9a3SAlexandre Belloni 		SKIP(return, "Skipping test since %s does not exist", rtc_file);
43535eee9a3SAlexandre Belloni 	ASSERT_NE(-1, self->fd);
43635eee9a3SAlexandre Belloni 
437*0cd73ab4SWolfram Sang 	alarm_state = get_rtc_alarm_state(self->fd, 0);
4382a027d6bSJoseph Jang 	if (alarm_state == RTC_ALARM_DISABLED)
4392a027d6bSJoseph Jang 		SKIP(return, "Skipping test since alarms are not supported.");
4402a027d6bSJoseph Jang 
4417b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
4427b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4437b302772SAlexandre Belloni 
4447b302772SAlexandre Belloni 	secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
4457b302772SAlexandre Belloni 	gmtime_r(&secs, (struct tm *)&alarm.time);
4467b302772SAlexandre Belloni 
4477b302772SAlexandre Belloni 	alarm.enabled = 1;
4487b302772SAlexandre Belloni 
4497b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
4507b302772SAlexandre Belloni 	if (rc == -1) {
4512a027d6bSJoseph Jang 		/*
4522a027d6bSJoseph Jang 		 * Report error if rtc alarm was enabled. Fallback to check ioctl
4532a027d6bSJoseph Jang 		 * error number if rtc alarm state is unknown.
4542a027d6bSJoseph Jang 		 */
4552a027d6bSJoseph Jang 		ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
4567b302772SAlexandre Belloni 		ASSERT_EQ(EINVAL, errno);
4577b302772SAlexandre Belloni 		TH_LOG("skip alarms are not supported.");
4587b302772SAlexandre Belloni 		return;
4597b302772SAlexandre Belloni 	}
4607b302772SAlexandre Belloni 
4617b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
4627b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4637b302772SAlexandre Belloni 
4647b302772SAlexandre Belloni 	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
4657b302772SAlexandre Belloni 	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
4667b302772SAlexandre Belloni 	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
4677b302772SAlexandre Belloni 	       alarm.time.tm_min, alarm.time.tm_sec);
4687b302772SAlexandre Belloni 
4697b302772SAlexandre Belloni 	FD_ZERO(&readfds);
4707b302772SAlexandre Belloni 	FD_SET(self->fd, &readfds);
4717b302772SAlexandre Belloni 
4727b302772SAlexandre Belloni 	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
4737b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4747b302772SAlexandre Belloni 	ASSERT_NE(0, rc);
4757b302772SAlexandre Belloni 
4767b302772SAlexandre Belloni 	rc = read(self->fd, &data, sizeof(unsigned long));
4777b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4787b302772SAlexandre Belloni 
4797b302772SAlexandre Belloni 	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
4807b302772SAlexandre Belloni 	ASSERT_NE(-1, rc);
4817b302772SAlexandre Belloni 
4827b302772SAlexandre Belloni 	new = timegm((struct tm *)&tm);
4837b302772SAlexandre Belloni 	ASSERT_EQ(new, secs);
4847b302772SAlexandre Belloni }
4857b302772SAlexandre Belloni 
main(int argc,char ** argv)486a12ab9e1SAlexandre Belloni int main(int argc, char **argv)
487a12ab9e1SAlexandre Belloni {
4881ad99987SJoseph Jang 	int ret = -1;
4891ad99987SJoseph Jang 
490a12ab9e1SAlexandre Belloni 	switch (argc) {
491a12ab9e1SAlexandre Belloni 	case 2:
492d8da8665SAlexandre Belloni 		rtc_file = argv[1];
493a12ab9e1SAlexandre Belloni 		/* FALLTHROUGH */
494a12ab9e1SAlexandre Belloni 	case 1:
495a12ab9e1SAlexandre Belloni 		break;
496a12ab9e1SAlexandre Belloni 	default:
497d8da8665SAlexandre Belloni 		fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
498a12ab9e1SAlexandre Belloni 		return 1;
499a12ab9e1SAlexandre Belloni 	}
500a12ab9e1SAlexandre Belloni 
5011ad99987SJoseph Jang 	/* Run the test if rtc_file is accessible */
5021ad99987SJoseph Jang 	if (access(rtc_file, R_OK) == 0)
5031ad99987SJoseph Jang 		ret = test_harness_run(argc, argv);
5041ad99987SJoseph Jang 	else
5051ad99987SJoseph Jang 		ksft_exit_skip("[SKIP]: Cannot access rtc file %s - Exiting\n",
5061ad99987SJoseph Jang 						rtc_file);
5071ad99987SJoseph Jang 
5081ad99987SJoseph Jang 	return ret;
509a12ab9e1SAlexandre Belloni }
510