summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandre Belloni <alexandre.belloni@bootlin.com>2019-01-10 23:24:59 +0100
committerAlexandre Belloni <alexandre.belloni@bootlin.com>2019-01-10 23:24:59 +0100
commit33ef4aa1c92b0c92a351284d93d1ac5570de9cc7 (patch)
treec45e9c85193241d33395dee579fb79f654380b84
parent8d7cc54ec886e9cd27f4097ed1e68ea9bc5fe456 (diff)
downloadrtc-tools-33ef4aa1c92b0c92a351284d93d1ac5570de9cc7.tar.gz
rtc-range: new tool
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
-rw-r--r--rtc-range.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/rtc-range.c b/rtc-range.c
new file mode 100644
index 0000000..5fc4273
--- /dev/null
+++ b/rtc-range.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock Driver range test
+ *
+ * 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>
+
+static char *rtc_file = "/dev/rtc0";
+
+#define IOCTL(f, r, d, rc) rc = ioctl(f, r, d); \
+if (rc) { \
+ fprintf(stderr, "KO %s returned %d (line %d)\n", #r, errno, __LINE__); \
+ continue; \
+}
+
+#define ISODATEFMT "%04d-%02d-%02d %02d:%02d:%02d"
+#define ISODATE(tm) tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+ tm.tm_hour, tm.tm_min, tm.tm_sec
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+static struct {
+ struct rtc_time tm;
+ struct rtc_time expected;
+} dates [] = {
+ { /* 2000 is a leap year */
+ .tm = { .tm_year = 100, .tm_mon = 1, .tm_mday = 28,
+ .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
+ .expected = { .tm_year = 100, .tm_mon = 1, .tm_mday = 29,
+ .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }
+ },
+ { /* signed 32bit time_t overflow */
+ .tm = { .tm_year = 138, .tm_mday = 19,
+ .tm_hour = 3, .tm_min = 14, .tm_sec = 7 },
+ .expected = { .tm_year = 138, .tm_mday = 19,
+ .tm_hour = 3, .tm_min = 14, .tm_sec = 8 }
+ },
+ { /* 2069 to 2070 */
+ .tm = { .tm_year = 169, .tm_mon = 11, .tm_mday = 31,
+ .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
+ .expected = { .tm_year = 170, .tm_mday = 1,
+ .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }
+ },
+ { /* 2099 to 2100 */
+ .tm = { .tm_year = 199, .tm_mon = 11, .tm_mday = 31,
+ .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
+ .expected = { .tm_year = 200, .tm_mday = 1,
+ .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }
+ },
+ { /* 2100 is not a leap year */
+ .tm = { .tm_year = 200, .tm_mon = 1, .tm_mday = 28,
+ .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
+ .expected = { .tm_year = 200, .tm_mon = 2, .tm_mday = 1,
+ .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }
+ },
+ { /* unsigned 32bit time_t overflow */
+ .tm = { .tm_year = 206, .tm_mon = 1, .tm_mday = 7,
+ .tm_hour = 6, .tm_min = 28, .tm_sec = 15 },
+ .expected = { .tm_year = 206, .tm_mon = 1, .tm_mday = 7,
+ .tm_hour = 6, .tm_min = 28, .tm_sec = 16 }
+ },
+ { /* ktime_t overflow */
+ .tm = { .tm_year = 362, .tm_mon = 3, .tm_mday = 11,
+ .tm_hour = 23, .tm_min = 47, .tm_sec = 16 },
+ .expected = { .tm_year = 362, .tm_mon = 3, .tm_mday = 11,
+ .tm_hour = 23, .tm_min = 47, .tm_sec = 17 }
+ },
+};
+
+static int compare_dates(struct rtc_time *a, struct rtc_time *b)
+{
+ if (a->tm_year != b->tm_year ||
+ a->tm_mon != b->tm_mon ||
+ a->tm_mday != b->tm_mday ||
+ a->tm_hour != b->tm_hour ||
+ a->tm_min != b->tm_min ||
+ a->tm_sec != b->tm_sec)
+ return 1;
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int fd, i, rc;
+
+ switch (argc) {
+ case 2:
+ rtc_file = argv[1];
+ /* FALLTHROUGH */
+ case 1:
+ break;
+ default:
+ fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
+ return 1;
+ }
+
+ fd = open(rtc_file, O_RDONLY);
+ if (fd == -1) {
+ perror(rtc_file);
+ exit(errno);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dates); i++) {
+ struct rtc_time tm;
+
+ printf("\nTesting " ISODATEFMT ".\n", ISODATE(dates[i].tm));
+
+ IOCTL(fd, RTC_SET_TIME, &dates[i].tm, rc);
+
+ IOCTL(fd, RTC_RD_TIME, &tm, rc);
+
+ rc = compare_dates(&dates[i].tm, &tm);
+ if (rc) {
+ printf("KO Read back " ISODATEFMT ".\n", ISODATE(tm));
+ continue;
+ }
+
+ /*
+ * We can't rely on alarms to work and because update interrupts
+ * are implemented using alarms, they are not usable either
+ */
+ sleep(1);
+
+ IOCTL(fd, RTC_RD_TIME, &tm, rc);
+
+ rc = compare_dates(&dates[i].expected, &tm);
+ if (rc) {
+ printf("KO Expected " ISODATEFMT ".\n",
+ ISODATE(dates[i].expected));
+ printf(" Got " ISODATEFMT ".\n", ISODATE(tm));
+ continue;
+ }
+
+ printf("OK\n");
+ continue;
+
+ /*
+ * Test alarms note: this will always fail the ktime_t overflow
+ * because it is stored internally in a ktime_t
+ */
+ IOCTL(fd, RTC_SET_TIME, &dates[i].tm, rc);
+
+ IOCTL(fd, RTC_WKALM_SET, &dates[i].tm, rc);
+
+ IOCTL(fd, RTC_WKALM_RD, &dates[i].tm, rc);
+
+ rc = compare_dates(&dates[i].tm, &tm);
+ if (rc) {
+ printf("KO ALM Read back " ISODATEFMT ".\n",
+ ISODATE(tm));
+ continue;
+ }
+ }
+
+ close(fd);
+}