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