brintos

brintos / linux-shallow public Read only

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