123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- // 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);
- ASSERT_NE(0, rc);
- /* Disable alarm interrupts */
- rc = ioctl(self->fd, RTC_AIE_OFF, 0);
- ASSERT_NE(-1, rc);
- 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);
- ASSERT_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);
- }
- TEST_F(rtc, alarm_alm_set_minute) {
- struct timeval tv = { .tv_sec = 62 };
- 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) + 60 - tm.tm_sec;
- 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);
- ASSERT_NE(0, rc);
- /* Disable alarm interrupts */
- rc = ioctl(self->fd, RTC_AIE_OFF, 0);
- ASSERT_NE(-1, rc);
- 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_minute) {
- struct timeval tv = { .tv_sec = 62 };
- 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) + 60 - alarm.time.tm_sec;
- 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);
- ASSERT_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);
- }
|