linux/tools/testing/selftests/rtc/rtctest.c
<<
>>
Prefs
   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
  24static char *rtc_file = "/dev/rtc0";
  25
  26FIXTURE(rtc) {
  27        int fd;
  28};
  29
  30FIXTURE_SETUP(rtc) {
  31        self->fd = open(rtc_file, O_RDONLY);
  32        ASSERT_NE(-1, self->fd);
  33}
  34
  35FIXTURE_TEARDOWN(rtc) {
  36        close(self->fd);
  37}
  38
  39TEST_F(rtc, date_read) {
  40        int rc;
  41        struct rtc_time rtc_tm;
  42
  43        /* Read the RTC time/date */
  44        rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
  45        ASSERT_NE(-1, rc);
  46
  47        TH_LOG("Current RTC date/time is %02d/%02d/%02d %02d:%02d:%02d.",
  48               rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
  49               rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
  50}
  51
  52TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
  53        int i, rc, irq = 0;
  54        unsigned long data;
  55
  56        /* Turn on update interrupts */
  57        rc = ioctl(self->fd, RTC_UIE_ON, 0);
  58        if (rc == -1) {
  59                ASSERT_EQ(EINVAL, errno);
  60                TH_LOG("skip update IRQs not supported.");
  61                return;
  62        }
  63
  64        for (i = 0; i < NUM_UIE; i++) {
  65                /* This read will block */
  66                rc = read(self->fd, &data, sizeof(data));
  67                ASSERT_NE(-1, rc);
  68                irq++;
  69        }
  70
  71        EXPECT_EQ(NUM_UIE, irq);
  72
  73        rc = ioctl(self->fd, RTC_UIE_OFF, 0);
  74        ASSERT_NE(-1, rc);
  75}
  76
  77TEST_F(rtc, uie_select) {
  78        int i, rc, irq = 0;
  79        unsigned long data;
  80
  81        /* Turn on update interrupts */
  82        rc = ioctl(self->fd, RTC_UIE_ON, 0);
  83        if (rc == -1) {
  84                ASSERT_EQ(EINVAL, errno);
  85                TH_LOG("skip update IRQs not supported.");
  86                return;
  87        }
  88
  89        for (i = 0; i < NUM_UIE; i++) {
  90                struct timeval tv = { .tv_sec = 2 };
  91                fd_set readfds;
  92
  93                FD_ZERO(&readfds);
  94                FD_SET(self->fd, &readfds);
  95                /* The select will wait until an RTC interrupt happens. */
  96                rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
  97                ASSERT_NE(-1, rc);
  98                ASSERT_NE(0, rc);
  99
 100                /* This read won't block */
 101                rc = read(self->fd, &data, sizeof(unsigned long));
 102                ASSERT_NE(-1, rc);
 103                irq++;
 104        }
 105
 106        EXPECT_EQ(NUM_UIE, irq);
 107
 108        rc = ioctl(self->fd, RTC_UIE_OFF, 0);
 109        ASSERT_NE(-1, rc);
 110}
 111
 112TEST_F(rtc, alarm_alm_set) {
 113        struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
 114        unsigned long data;
 115        struct rtc_time tm;
 116        fd_set readfds;
 117        time_t secs, new;
 118        int rc;
 119
 120        rc = ioctl(self->fd, RTC_RD_TIME, &tm);
 121        ASSERT_NE(-1, rc);
 122
 123        secs = timegm((struct tm *)&tm) + ALARM_DELTA;
 124        gmtime_r(&secs, (struct tm *)&tm);
 125
 126        rc = ioctl(self->fd, RTC_ALM_SET, &tm);
 127        if (rc == -1) {
 128                ASSERT_EQ(EINVAL, errno);
 129                TH_LOG("skip alarms are not supported.");
 130                return;
 131        }
 132
 133        rc = ioctl(self->fd, RTC_ALM_READ, &tm);
 134        ASSERT_NE(-1, rc);
 135
 136        TH_LOG("Alarm time now set to %02d:%02d:%02d.",
 137               tm.tm_hour, tm.tm_min, tm.tm_sec);
 138
 139        /* Enable alarm interrupts */
 140        rc = ioctl(self->fd, RTC_AIE_ON, 0);
 141        ASSERT_NE(-1, rc);
 142
 143        FD_ZERO(&readfds);
 144        FD_SET(self->fd, &readfds);
 145
 146        rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
 147        ASSERT_NE(-1, rc);
 148        ASSERT_NE(0, rc);
 149
 150        /* Disable alarm interrupts */
 151        rc = ioctl(self->fd, RTC_AIE_OFF, 0);
 152        ASSERT_NE(-1, rc);
 153
 154        rc = read(self->fd, &data, sizeof(unsigned long));
 155        ASSERT_NE(-1, rc);
 156        TH_LOG("data: %lx", data);
 157
 158        rc = ioctl(self->fd, RTC_RD_TIME, &tm);
 159        ASSERT_NE(-1, rc);
 160
 161        new = timegm((struct tm *)&tm);
 162        ASSERT_EQ(new, secs);
 163}
 164
 165TEST_F(rtc, alarm_wkalm_set) {
 166        struct timeval tv = { .tv_sec = ALARM_DELTA + 2 };
 167        struct rtc_wkalrm alarm = { 0 };
 168        struct rtc_time tm;
 169        unsigned long data;
 170        fd_set readfds;
 171        time_t secs, new;
 172        int rc;
 173
 174        rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
 175        ASSERT_NE(-1, rc);
 176
 177        secs = timegm((struct tm *)&alarm.time) + ALARM_DELTA;
 178        gmtime_r(&secs, (struct tm *)&alarm.time);
 179
 180        alarm.enabled = 1;
 181
 182        rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
 183        if (rc == -1) {
 184                ASSERT_EQ(EINVAL, errno);
 185                TH_LOG("skip alarms are not supported.");
 186                return;
 187        }
 188
 189        rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
 190        ASSERT_NE(-1, rc);
 191
 192        TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
 193               alarm.time.tm_mday, alarm.time.tm_mon + 1,
 194               alarm.time.tm_year + 1900, alarm.time.tm_hour,
 195               alarm.time.tm_min, alarm.time.tm_sec);
 196
 197        FD_ZERO(&readfds);
 198        FD_SET(self->fd, &readfds);
 199
 200        rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
 201        ASSERT_NE(-1, rc);
 202        ASSERT_NE(0, rc);
 203
 204        rc = read(self->fd, &data, sizeof(unsigned long));
 205        ASSERT_NE(-1, rc);
 206
 207        rc = ioctl(self->fd, RTC_RD_TIME, &tm);
 208        ASSERT_NE(-1, rc);
 209
 210        new = timegm((struct tm *)&tm);
 211        ASSERT_EQ(new, secs);
 212}
 213
 214TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
 215        struct timeval tv = { .tv_sec = 62 };
 216        unsigned long data;
 217        struct rtc_time tm;
 218        fd_set readfds;
 219        time_t secs, new;
 220        int rc;
 221
 222        rc = ioctl(self->fd, RTC_RD_TIME, &tm);
 223        ASSERT_NE(-1, rc);
 224
 225        secs = timegm((struct tm *)&tm) + 60 - tm.tm_sec;
 226        gmtime_r(&secs, (struct tm *)&tm);
 227
 228        rc = ioctl(self->fd, RTC_ALM_SET, &tm);
 229        if (rc == -1) {
 230                ASSERT_EQ(EINVAL, errno);
 231                TH_LOG("skip alarms are not supported.");
 232                return;
 233        }
 234
 235        rc = ioctl(self->fd, RTC_ALM_READ, &tm);
 236        ASSERT_NE(-1, rc);
 237
 238        TH_LOG("Alarm time now set to %02d:%02d:%02d.",
 239               tm.tm_hour, tm.tm_min, tm.tm_sec);
 240
 241        /* Enable alarm interrupts */
 242        rc = ioctl(self->fd, RTC_AIE_ON, 0);
 243        ASSERT_NE(-1, rc);
 244
 245        FD_ZERO(&readfds);
 246        FD_SET(self->fd, &readfds);
 247
 248        rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
 249        ASSERT_NE(-1, rc);
 250        ASSERT_NE(0, rc);
 251
 252        /* Disable alarm interrupts */
 253        rc = ioctl(self->fd, RTC_AIE_OFF, 0);
 254        ASSERT_NE(-1, rc);
 255
 256        rc = read(self->fd, &data, sizeof(unsigned long));
 257        ASSERT_NE(-1, rc);
 258        TH_LOG("data: %lx", data);
 259
 260        rc = ioctl(self->fd, RTC_RD_TIME, &tm);
 261        ASSERT_NE(-1, rc);
 262
 263        new = timegm((struct tm *)&tm);
 264        ASSERT_EQ(new, secs);
 265}
 266
 267TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
 268        struct timeval tv = { .tv_sec = 62 };
 269        struct rtc_wkalrm alarm = { 0 };
 270        struct rtc_time tm;
 271        unsigned long data;
 272        fd_set readfds;
 273        time_t secs, new;
 274        int rc;
 275
 276        rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
 277        ASSERT_NE(-1, rc);
 278
 279        secs = timegm((struct tm *)&alarm.time) + 60 - alarm.time.tm_sec;
 280        gmtime_r(&secs, (struct tm *)&alarm.time);
 281
 282        alarm.enabled = 1;
 283
 284        rc = ioctl(self->fd, RTC_WKALM_SET, &alarm);
 285        if (rc == -1) {
 286                ASSERT_EQ(EINVAL, errno);
 287                TH_LOG("skip alarms are not supported.");
 288                return;
 289        }
 290
 291        rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
 292        ASSERT_NE(-1, rc);
 293
 294        TH_LOG("Alarm time now set to %02d/%02d/%02d %02d:%02d:%02d.",
 295               alarm.time.tm_mday, alarm.time.tm_mon + 1,
 296               alarm.time.tm_year + 1900, alarm.time.tm_hour,
 297               alarm.time.tm_min, alarm.time.tm_sec);
 298
 299        FD_ZERO(&readfds);
 300        FD_SET(self->fd, &readfds);
 301
 302        rc = select(self->fd + 1, &readfds, NULL, NULL, &tv);
 303        ASSERT_NE(-1, rc);
 304        ASSERT_NE(0, rc);
 305
 306        rc = read(self->fd, &data, sizeof(unsigned long));
 307        ASSERT_NE(-1, rc);
 308
 309        rc = ioctl(self->fd, RTC_RD_TIME, &tm);
 310        ASSERT_NE(-1, rc);
 311
 312        new = timegm((struct tm *)&tm);
 313        ASSERT_EQ(new, secs);
 314}
 315
 316static void __attribute__((constructor))
 317__constructor_order_last(void)
 318{
 319        if (!__constructor_order)
 320                __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD;
 321}
 322
 323int main(int argc, char **argv)
 324{
 325        switch (argc) {
 326        case 2:
 327                rtc_file = argv[1];
 328                /* FALLTHROUGH */
 329        case 1:
 330                break;
 331        default:
 332                fprintf(stderr, "usage: %s [rtcdev]\n", argv[0]);
 333                return 1;
 334        }
 335
 336        return test_harness_run(argc, argv);
 337}
 338
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.