Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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
 24static char *rtc_file = "/dev/rtc0";
 25
 26FIXTURE(rtc) {
 27	int fd;
 28};
 29
 30FIXTURE_SETUP(rtc) {
 31	self->fd = open(rtc_file, O_RDONLY);
 32	ASSERT_NE(-1, self->fd);
 33}
 34
 35FIXTURE_TEARDOWN(rtc) {
 36	close(self->fd);
 37}
 38
 39TEST_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
 52TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
 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
 77TEST_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
112TEST_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	ASSERT_NE(0, rc);
149
150	/* Disable alarm interrupts */
151	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
152	ASSERT_NE(-1, rc);
153
154	rc = read(self->fd, &data, sizeof(unsigned long));
155	ASSERT_NE(-1, rc);
156	TH_LOG("data: %lx", data);
157
158	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
159	ASSERT_NE(-1, rc);
160
161	new = timegm((struct tm *)&tm);
162	ASSERT_EQ(new, secs);
163}
164
165TEST_F(rtc, alarm_wkalm_set) {
166	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
167	struct rtc_wkalrm alarm = { 0 };
168	struct rtc_time tm;
169	unsigned long data;
170	fd_set readfds;
171	time_t secs, new;
172	int rc;
173
174	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
175	ASSERT_NE(-1, rc);
176
177	secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
178	gmtime_r(&secs, (struct tm *)&alarm.time);
179
180	alarm.enabled = 1;
181
182	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
183	if (rc == -1) {
184		ASSERT_EQ(EINVAL, errno);
185		TH_LOG("skip alarms are not supported.");
186		return;
187	}
188
189	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
190	ASSERT_NE(-1, rc);
191
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);
196
197	FD_ZERO(&readfds);
198	FD_SET(self->fd, &readfds);
199
200	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
201	ASSERT_NE(-1, rc);
202	ASSERT_NE(0, rc);
203
204	rc = read(self->fd, &data, sizeof(unsigned long));
205	ASSERT_NE(-1, rc);
206
207	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
208	ASSERT_NE(-1, rc);
209
210	new = timegm((struct tm *)&tm);
211	ASSERT_EQ(new, secs);
212}
213
214TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
215	struct timeval tv = { .tv_sec = 62 };
216	unsigned long data;
217	struct rtc_time tm;
218	fd_set readfds;
219	time_t secs, new;
220	int rc;
221
222	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
223	ASSERT_NE(-1, rc);
224
225	secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
226	gmtime_r(&secs, (struct tm *)&tm);
227
228	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
229	if (rc == -1) {
230		ASSERT_EQ(EINVAL, errno);
231		TH_LOG("skip alarms are not supported.");
232		return;
233	}
234
235	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
236	ASSERT_NE(-1, rc);
237
238	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
239	       tm.tm_hour, tm.tm_min, tm.tm_sec);
240
241	/* Enable alarm interrupts */
242	rc = ioctl(self->fd, RTC_AIE_ON, 0);
243	ASSERT_NE(-1, rc);
244
245	FD_ZERO(&readfds);
246	FD_SET(self->fd, &readfds);
247
248	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
249	ASSERT_NE(-1, rc);
250	ASSERT_NE(0, rc);
251
252	/* Disable alarm interrupts */
253	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
254	ASSERT_NE(-1, rc);
255
256	rc = read(self->fd, &data, sizeof(unsigned long));
257	ASSERT_NE(-1, rc);
258	TH_LOG("data: %lx", data);
259
260	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
261	ASSERT_NE(-1, rc);
262
263	new = timegm((struct tm *)&tm);
264	ASSERT_EQ(new, secs);
265}
266
267TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
268	struct timeval tv = { .tv_sec = 62 };
269	struct rtc_wkalrm alarm = { 0 };
270	struct rtc_time tm;
271	unsigned long data;
272	fd_set readfds;
273	time_t secs, new;
274	int rc;
275
276	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
277	ASSERT_NE(-1, rc);
278
279	secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
280	gmtime_r(&secs, (struct tm *)&alarm.time);
281
282	alarm.enabled = 1;
283
284	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
285	if (rc == -1) {
286		ASSERT_EQ(EINVAL, errno);
287		TH_LOG("skip alarms are not supported.");
288		return;
289	}
290
291	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
292	ASSERT_NE(-1, rc);
293
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);
298
299	FD_ZERO(&readfds);
300	FD_SET(self->fd, &readfds);
301
302	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
303	ASSERT_NE(-1, rc);
304	ASSERT_NE(0, rc);
305
306	rc = read(self->fd, &data, sizeof(unsigned long));
307	ASSERT_NE(-1, rc);
308
309	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
310	ASSERT_NE(-1, rc);
311
312	new = timegm((struct tm *)&tm);
313	ASSERT_EQ(new, secs);
314}
315
316static void __attribute__((constructor))
317__constructor_order_last(void)
318{
319	if (!__constructor_order)
320		__constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
321}
322
323int main(int argc, char **argv)
324{
325	switch (argc) {
326	case 2:
327		rtc_file = argv[1];
328		/* FALLTHROUGH */
329	case 1:
330		break;
331	default:
332		fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
333		return 1;
334	}
335
336	return test_harness_run(argc, argv);
337}