File Index
Files and directories
Sign in
FILE

rtctest.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/rtc/rtctest.c C
  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#define READ_LOOP_DURATION_SEC 30
 24#define READ_LOOP_SLEEP_MS 11
 25
 26static char *rtc_file = "/dev/rtc0";
 27
 28enum rtc_alarm_state {
 29	RTC_ALARM_UNKNOWN,
 30	RTC_ALARM_ENABLED,
 31	RTC_ALARM_DISABLED,
 32	RTC_ALARM_RES_MINUTE,
 33};
 34
 35FIXTURE(rtc) {
 36	int fd;
 37};
 38
 39FIXTURE_SETUP(rtc) {
 40	self->fd = open(rtc_file, O_RDONLY);
 41}
 42
 43FIXTURE_TEARDOWN(rtc) {
 44	close(self->fd);
 45}
 46
 47TEST_F(rtc, date_read) {
 48	int rc;
 49	struct rtc_time rtc_tm;
 50
 51	if (self->fd == -1 && errno == ENOENT)
 52		SKIP(return, "Skipping test since %s does not exist", rtc_file);
 53	ASSERT_NE(-1, self->fd);
 54
 55	/* Read the RTC time/date */
 56	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
 57	ASSERT_NE(-1, rc);
 58
 59	TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
 60	       rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
 61	       rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
 62}
 63
 64static time_t rtc_time_to_timestamp(struct rtc_time *rtc_time)
 65{
 66	struct tm tm_time = {
 67	       .tm_sec = rtc_time->tm_sec,
 68	       .tm_min = rtc_time->tm_min,
 69	       .tm_hour = rtc_time->tm_hour,
 70	       .tm_mday = rtc_time->tm_mday,
 71	       .tm_mon = rtc_time->tm_mon,
 72	       .tm_year = rtc_time->tm_year,
 73	};
 74
 75	return mktime(&tm_time);
 76}
 77
 78static void nanosleep_with_retries(long ns)
 79{
 80	struct timespec req = {
 81		.tv_sec = 0,
 82		.tv_nsec = ns,
 83	};
 84	struct timespec rem;
 85
 86	while (nanosleep(&req, &rem) != 0) {
 87		req.tv_sec = rem.tv_sec;
 88		req.tv_nsec = rem.tv_nsec;
 89	}
 90}
 91
 92static enum rtc_alarm_state get_rtc_alarm_state(int fd, int need_seconds)
 93{
 94	struct rtc_param param = { 0 };
 95	int rc;
 96
 97	/* Validate kernel reflects unsupported RTC alarm state */
 98	param.param = RTC_PARAM_FEATURES;
 99	param.index = 0;
100	rc = ioctl(fd, RTC_PARAM_GET, &param);
101	if (rc < 0)
102		return RTC_ALARM_UNKNOWN;
103
104	if ((param.uvalue & _BITUL(RTC_FEATURE_ALARM)) == 0)
105		return RTC_ALARM_DISABLED;
106
107	/* Check if alarm has desired granularity */
108	if (need_seconds && (param.uvalue & _BITUL(RTC_FEATURE_ALARM_RES_MINUTE)))
109		return RTC_ALARM_RES_MINUTE;
110
111	return RTC_ALARM_ENABLED;
112}
113
114TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
115	int rc;
116	long iter_count = 0;
117	struct rtc_time rtc_tm;
118	time_t start_rtc_read, prev_rtc_read;
119
120	if (self->fd == -1 && errno == ENOENT)
121		SKIP(return, "Skipping test since %s does not exist", rtc_file);
122	ASSERT_NE(-1, self->fd);
123
124	TH_LOG("Continuously reading RTC time for %ds (with %dms breaks after every read).",
125	       READ_LOOP_DURATION_SEC, READ_LOOP_SLEEP_MS);
126
127	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
128	ASSERT_NE(-1, rc);
129	start_rtc_read = rtc_time_to_timestamp(&rtc_tm);
130	prev_rtc_read = start_rtc_read;
131
132	do  {
133		time_t rtc_read;
134
135		rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
136		ASSERT_NE(-1, rc);
137
138		rtc_read = rtc_time_to_timestamp(&rtc_tm);
139		/* Time should not go backwards */
140		ASSERT_LE(prev_rtc_read, rtc_read);
141		/* Time should not increase more then 1s at a time */
142		ASSERT_GE(prev_rtc_read + 1, rtc_read);
143
144		/* Sleep 11ms to avoid killing / overheating the RTC */
145		nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
146
147		prev_rtc_read = rtc_read;
148		iter_count++;
149	} while (prev_rtc_read <= start_rtc_read + READ_LOOP_DURATION_SEC);
150
151	TH_LOG("Performed %ld RTC time reads.", iter_count);
152}
153
154TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
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		/* This read will block */
172		rc = read(self->fd, &data, sizeof(data));
173		ASSERT_NE(-1, rc);
174		irq++;
175	}
176
177	EXPECT_EQ(NUM_UIE, irq);
178
179	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
180	ASSERT_NE(-1, rc);
181}
182
183TEST_F(rtc, uie_select) {
184	int i, rc, irq = 0;
185	unsigned long data;
186
187	if (self->fd == -1 && errno == ENOENT)
188		SKIP(return, "Skipping test since %s does not exist", rtc_file);
189	ASSERT_NE(-1, self->fd);
190
191	/* Turn on update interrupts */
192	rc = ioctl(self->fd, RTC_UIE_ON, 0);
193	if (rc == -1) {
194		ASSERT_EQ(EINVAL, errno);
195		TH_LOG("skip update IRQs not supported.");
196		return;
197	}
198
199	for (i = 0; i < NUM_UIE; i++) {
200		struct timeval tv = { .tv_sec = 2 };
201		fd_set readfds;
202
203		FD_ZERO(&readfds);
204		FD_SET(self->fd, &readfds);
205		/* The select will wait until an RTC interrupt happens. */
206		rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
207		ASSERT_NE(-1, rc);
208		ASSERT_NE(0, rc);
209
210		/* This read won't block */
211		rc = read(self->fd, &data, sizeof(unsigned long));
212		ASSERT_NE(-1, rc);
213		irq++;
214	}
215
216	EXPECT_EQ(NUM_UIE, irq);
217
218	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
219	ASSERT_NE(-1, rc);
220}
221
222TEST_F(rtc, alarm_alm_set) {
223	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
224	unsigned long data;
225	struct rtc_time tm;
226	fd_set readfds;
227	time_t secs, new;
228	int rc;
229	enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
230
231	if (self->fd == -1 && errno == ENOENT)
232		SKIP(return, "Skipping test since %s does not exist", rtc_file);
233	ASSERT_NE(-1, self->fd);
234
235	alarm_state = get_rtc_alarm_state(self->fd, 1);
236	if (alarm_state == RTC_ALARM_DISABLED)
237		SKIP(return, "Skipping test since alarms are not supported.");
238	if (alarm_state == RTC_ALARM_RES_MINUTE)
239		SKIP(return, "Skipping test since alarms has only minute granularity.");
240
241	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
242	ASSERT_NE(-1, rc);
243
244	secs = timegm((struct tm *)&tm) + ALARM_DELTA;
245	gmtime_r(&secs, (struct tm *)&tm);
246
247	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
248	if (rc == -1) {
249		/*
250		 * Report error if rtc alarm was enabled. Fallback to check ioctl
251		 * error number if rtc alarm state is unknown.
252		 */
253		ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
254		ASSERT_EQ(EINVAL, errno);
255		TH_LOG("skip alarms are not supported.");
256		return;
257	}
258
259	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
260	ASSERT_NE(-1, rc);
261
262	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
263	       tm.tm_hour, tm.tm_min, tm.tm_sec);
264
265	/* Enable alarm interrupts */
266	rc = ioctl(self->fd, RTC_AIE_ON, 0);
267	ASSERT_NE(-1, rc);
268
269	FD_ZERO(&readfds);
270	FD_SET(self->fd, &readfds);
271
272	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
273	ASSERT_NE(-1, rc);
274	ASSERT_NE(0, rc);
275
276	/* Disable alarm interrupts */
277	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
278	ASSERT_NE(-1, rc);
279
280	rc = read(self->fd, &data, sizeof(unsigned long));
281	ASSERT_NE(-1, rc);
282	TH_LOG("data: %lx", data);
283
284	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
285	ASSERT_NE(-1, rc);
286
287	new = timegm((struct tm *)&tm);
288	ASSERT_EQ(new, secs);
289}
290
291TEST_F(rtc, alarm_wkalm_set) {
292	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
293	struct rtc_wkalrm alarm = { 0 };
294	struct rtc_time tm;
295	unsigned long data;
296	fd_set readfds;
297	time_t secs, new;
298	int rc;
299	enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
300
301	if (self->fd == -1 && errno == ENOENT)
302		SKIP(return, "Skipping test since %s does not exist", rtc_file);
303	ASSERT_NE(-1, self->fd);
304
305	alarm_state = get_rtc_alarm_state(self->fd, 1);
306	if (alarm_state == RTC_ALARM_DISABLED)
307		SKIP(return, "Skipping test since alarms are not supported.");
308	if (alarm_state == RTC_ALARM_RES_MINUTE)
309		SKIP(return, "Skipping test since alarms has only minute granularity.");
310
311	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
312	ASSERT_NE(-1, rc);
313
314	secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
315	gmtime_r(&secs, (struct tm *)&alarm.time);
316
317	alarm.enabled = 1;
318
319	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
320	if (rc == -1) {
321		/*
322		 * Report error if rtc alarm was enabled. Fallback to check ioctl
323		 * error number if rtc alarm state is unknown.
324		 */
325		ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
326		ASSERT_EQ(EINVAL, errno);
327		TH_LOG("skip alarms are not supported.");
328		return;
329	}
330
331	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
332	ASSERT_NE(-1, rc);
333
334	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
335	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
336	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
337	       alarm.time.tm_min, alarm.time.tm_sec);
338
339	FD_ZERO(&readfds);
340	FD_SET(self->fd, &readfds);
341
342	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
343	ASSERT_NE(-1, rc);
344	ASSERT_NE(0, rc);
345
346	rc = read(self->fd, &data, sizeof(unsigned long));
347	ASSERT_NE(-1, rc);
348
349	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
350	ASSERT_NE(-1, rc);
351
352	new = timegm((struct tm *)&tm);
353	ASSERT_EQ(new, secs);
354}
355
356TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
357	struct timeval tv = { .tv_sec = 62 };
358	unsigned long data;
359	struct rtc_time tm;
360	fd_set readfds;
361	time_t secs, new;
362	int rc;
363	enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
364
365	if (self->fd == -1 && errno == ENOENT)
366		SKIP(return, "Skipping test since %s does not exist", rtc_file);
367	ASSERT_NE(-1, self->fd);
368
369	alarm_state = get_rtc_alarm_state(self->fd, 0);
370	if (alarm_state == RTC_ALARM_DISABLED)
371		SKIP(return, "Skipping test since alarms are not supported.");
372
373	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
374	ASSERT_NE(-1, rc);
375
376	secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
377	gmtime_r(&secs, (struct tm *)&tm);
378
379	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
380	if (rc == -1) {
381		/*
382		 * Report error if rtc alarm was enabled. Fallback to check ioctl
383		 * error number if rtc alarm state is unknown.
384		 */
385		ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
386		ASSERT_EQ(EINVAL, errno);
387		TH_LOG("skip alarms are not supported.");
388		return;
389	}
390
391	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
392	ASSERT_NE(-1, rc);
393
394	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
395	       tm.tm_hour, tm.tm_min, tm.tm_sec);
396
397	/* Enable alarm interrupts */
398	rc = ioctl(self->fd, RTC_AIE_ON, 0);
399	ASSERT_NE(-1, rc);
400
401	FD_ZERO(&readfds);
402	FD_SET(self->fd, &readfds);
403
404	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
405	ASSERT_NE(-1, rc);
406	ASSERT_NE(0, rc);
407
408	/* Disable alarm interrupts */
409	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
410	ASSERT_NE(-1, rc);
411
412	rc = read(self->fd, &data, sizeof(unsigned long));
413	ASSERT_NE(-1, rc);
414	TH_LOG("data: %lx", data);
415
416	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
417	ASSERT_NE(-1, rc);
418
419	new = timegm((struct tm *)&tm);
420	ASSERT_EQ(new, secs);
421}
422
423TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
424	struct timeval tv = { .tv_sec = 62 };
425	struct rtc_wkalrm alarm = { 0 };
426	struct rtc_time tm;
427	unsigned long data;
428	fd_set readfds;
429	time_t secs, new;
430	int rc;
431	enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
432
433	if (self->fd == -1 && errno == ENOENT)
434		SKIP(return, "Skipping test since %s does not exist", rtc_file);
435	ASSERT_NE(-1, self->fd);
436
437	alarm_state = get_rtc_alarm_state(self->fd, 0);
438	if (alarm_state == RTC_ALARM_DISABLED)
439		SKIP(return, "Skipping test since alarms are not supported.");
440
441	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
442	ASSERT_NE(-1, rc);
443
444	secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
445	gmtime_r(&secs, (struct tm *)&alarm.time);
446
447	alarm.enabled = 1;
448
449	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
450	if (rc == -1) {
451		/*
452		 * Report error if rtc alarm was enabled. Fallback to check ioctl
453		 * error number if rtc alarm state is unknown.
454		 */
455		ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
456		ASSERT_EQ(EINVAL, errno);
457		TH_LOG("skip alarms are not supported.");
458		return;
459	}
460
461	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
462	ASSERT_NE(-1, rc);
463
464	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
465	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
466	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
467	       alarm.time.tm_min, alarm.time.tm_sec);
468
469	FD_ZERO(&readfds);
470	FD_SET(self->fd, &readfds);
471
472	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
473	ASSERT_NE(-1, rc);
474	ASSERT_NE(0, rc);
475
476	rc = read(self->fd, &data, sizeof(unsigned long));
477	ASSERT_NE(-1, rc);
478
479	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
480	ASSERT_NE(-1, rc);
481
482	new = timegm((struct tm *)&tm);
483	ASSERT_EQ(new, secs);
484}
485
486int main(int argc, char **argv)
487{
488	int ret = -1;
489
490	switch (argc) {
491	case 2:
492		rtc_file = argv[1];
493		/* FALLTHROUGH */
494	case 1:
495		break;
496	default:
497		fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
498		return 1;
499	}
500
501	/* Run the test if rtc_file is accessible */
502	if (access(rtc_file, R_OK) == 0)
503		ret = test_harness_run(argc, argv);
504	else
505		ksft_exit_skip("[SKIP]: Cannot access rtc file %s - Exiting\n",
506						rtc_file);
507
508	return ret;
509}
~/ Read-only file preview.