cregit-Linux how code gets into the kernel

Release 4.18 tools/testing/selftests/rtc/rtctest.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Real Time Clock Driver Test Program
 *
 * Copyright (c) 2018 Alexandre Belloni <alexandre.belloni@bootlin.com>
 */

#include <errno.h>
#include <fcntl.h>
#include <linux/rtc.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include "../kselftest_harness.h"


#define NUM_UIE 3

#define ALARM_DELTA 3


static char *rtc_file = "/dev/rtc0";


FIXTURE(rtc) 
{
	int fd;
};


FIXTURE_SETUP(rtc) 
{
	self->fd = open(rtc_file, O_RDONLY);
	ASSERT_NE(-1, self->fd);
}


FIXTURE_TEARDOWN(rtc) 
{
	close(self->fd);
}


TEST_F(rtc, date_read) 
{
	int rc;
	struct rtc_time rtc_tm;

	/* Read the RTC time/date */
	rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
	ASSERT_NE(-1, rc);

	TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
	       rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
	       rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
}


TEST_F(rtc, uie_read) 
{
	int i, rc, irq = 0;
	unsigned long data;

	/* Turn on update interrupts */
	rc = ioctl(self->fd, RTC_UIE_ON, 0);
	if (rc == -1) {
		ASSERT_EQ(EINVAL, errno);
		TH_LOG("skip update IRQs not supported.");
		return;
	}

	for (i = 0; i < NUM_UIE; i++) {
		/* This read will block */
		rc = read(self->fd, &data, sizeof(data));
		ASSERT_NE(-1, rc);
		irq++;
	}

	EXPECT_EQ(NUM_UIE, irq);

	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
	ASSERT_NE(-1, rc);
}


TEST_F(rtc, uie_select) 
{
	int i, rc, irq = 0;
	unsigned long data;

	/* Turn on update interrupts */
	rc = ioctl(self->fd, RTC_UIE_ON, 0);
	if (rc == -1) {
		ASSERT_EQ(EINVAL, errno);
		TH_LOG("skip update IRQs not supported.");
		return;
	}

	for (i = 0; i < NUM_UIE; i++) {
		struct timeval tv = { .tv_sec = 2 };
		fd_set readfds;

		FD_ZERO(&readfds);
		FD_SET(self->fd, &readfds);
		/* The select will wait until an RTC interrupt happens. */
		rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
		ASSERT_NE(-1, rc);
		ASSERT_NE(0, rc);

		/* This read won't block */
		rc = read(self->fd, &data, sizeof(unsigned long));
		ASSERT_NE(-1, rc);
		irq++;
	}

	EXPECT_EQ(NUM_UIE, irq);

	rc = ioctl(self->fd, RTC_UIE_OFF, 0);
	ASSERT_NE(-1, rc);
}


TEST_F(rtc, alarm_alm_set) 
{
	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
	unsigned long data;
	struct rtc_time tm;
	fd_set readfds;
	time_t secs, new;
	int rc;

	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
	ASSERT_NE(-1, rc);

	secs = timegm((struct tm *)&tm) + ALARM_DELTA;
	gmtime_r(&secs, (struct tm *)&tm);

	rc = ioctl(self->fd, RTC_ALM_SET, &tm);
	if (rc == -1) {
		ASSERT_EQ(EINVAL, errno);
		TH_LOG("skip alarms are not supported.");
		return;
	}

	rc = ioctl(self->fd, RTC_ALM_READ, &tm);
	ASSERT_NE(-1, rc);

	TH_LOG("Alarm time now set to %02d:%02d:%02d.",
	       tm.tm_hour, tm.tm_min, tm.tm_sec);

	/* Enable alarm interrupts */
	rc = ioctl(self->fd, RTC_AIE_ON, 0);
	ASSERT_NE(-1, rc);

	FD_ZERO(&readfds);
	FD_SET(self->fd, &readfds);

	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
	ASSERT_NE(-1, rc);
	EXPECT_NE(0, rc);

	/* Disable alarm interrupts */
	rc = ioctl(self->fd, RTC_AIE_OFF, 0);
	ASSERT_NE(-1, rc);

	if (rc == 0)
		return;

	rc = read(self->fd, &data, sizeof(unsigned long));
	ASSERT_NE(-1, rc);
	TH_LOG("data: %lx", data);

	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
	ASSERT_NE(-1, rc);

	new = timegm((struct tm *)&tm);
	ASSERT_EQ(new, secs);
}


TEST_F(rtc, alarm_wkalm_set) 
{
	struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
	struct rtc_wkalrm alarm = { 0 };
	struct rtc_time tm;
	unsigned long data;
	fd_set readfds;
	time_t secs, new;
	int rc;

	rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
	ASSERT_NE(-1, rc);

	secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
	gmtime_r(&secs, (struct tm *)&alarm.time);

	alarm.enabled = 1;

	rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
	if (rc == -1) {
		ASSERT_EQ(EINVAL, errno);
		TH_LOG("skip alarms are not supported.");
		return;
	}

	rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
	ASSERT_NE(-1, rc);

	TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
	       alarm.time.tm_mday, alarm.time.tm_mon + 1,
	       alarm.time.tm_year + 1900, alarm.time.tm_hour,
	       alarm.time.tm_min, alarm.time.tm_sec);

	FD_ZERO(&readfds);
	FD_SET(self->fd, &readfds);

	rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
	ASSERT_NE(-1, rc);
	EXPECT_NE(0, rc);

	rc = read(self->fd, &data, sizeof(unsigned long));
	ASSERT_NE(-1, rc);

	rc = ioctl(self->fd, RTC_RD_TIME, &tm);
	ASSERT_NE(-1, rc);

	new = timegm((struct tm *)&tm);
	ASSERT_EQ(new, secs);
}

static void __attribute__((constructor))

__constructor_order_last(void)
{
	if (!__constructor_order)
		__constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
}


int main(int argc, char **argv) { switch (argc) { case 2: rtc_file = argv[1]; /* FALLTHROUGH */ case 1: break; default: fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]); return 1; } return test_harness_run(argc, argv); }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni3865.52%133.33%
Benjamin Gaignard1525.86%133.33%
Prarit Bhargava58.62%133.33%
Total58100.00%3100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni76460.83%120.00%
Prarit Bhargava35428.18%120.00%
Benjamin Gaignard13010.35%120.00%
Andrey Smirnov40.32%120.00%
Lukáš Doktor40.32%120.00%
Total1256100.00%5100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.