at v4.16 403 lines 8.8 kB view raw
1/* 2 * Real Time Clock Driver Test/Example Program 3 * 4 * Compile with: 5 * gcc -s -Wall -Wstrict-prototypes rtctest.c -o rtctest 6 * 7 * Copyright (C) 1996, Paul Gortmaker. 8 * 9 * Released under the GNU General Public License, version 2, 10 * included herein by reference. 11 * 12 */ 13 14#include <stdio.h> 15#include <linux/rtc.h> 16#include <sys/ioctl.h> 17#include <sys/time.h> 18#include <sys/types.h> 19#include <fcntl.h> 20#include <unistd.h> 21#include <stdlib.h> 22#include <errno.h> 23 24#ifndef ARRAY_SIZE 25# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 26#endif 27 28/* 29 * This expects the new RTC class driver framework, working with 30 * clocks that will often not be clones of what the PC-AT had. 31 * Use the command line to specify another RTC if you need one. 32 */ 33static const char default_rtc[] = "/dev/rtc0"; 34 35static struct rtc_time cutoff_dates[] = { 36 { 37 .tm_year = 70, /* 1970 -1900 */ 38 .tm_mday = 1, 39 }, 40 /* signed time_t 19/01/2038 3:14:08 */ 41 { 42 .tm_year = 138, 43 .tm_mday = 19, 44 }, 45 { 46 .tm_year = 138, 47 .tm_mday = 20, 48 }, 49 { 50 .tm_year = 199, /* 2099 -1900 */ 51 .tm_mday = 1, 52 }, 53 { 54 .tm_year = 200, /* 2100 -1900 */ 55 .tm_mday = 1, 56 }, 57 /* unsigned time_t 07/02/2106 7:28:15*/ 58 { 59 .tm_year = 205, 60 .tm_mon = 1, 61 .tm_mday = 7, 62 }, 63 { 64 .tm_year = 206, 65 .tm_mon = 1, 66 .tm_mday = 8, 67 }, 68 /* signed time on 64bit in nanoseconds 12/04/2262 01:47:16*/ 69 { 70 .tm_year = 362, 71 .tm_mon = 3, 72 .tm_mday = 12, 73 }, 74 { 75 .tm_year = 362, /* 2262 -1900 */ 76 .tm_mon = 3, 77 .tm_mday = 13, 78 }, 79}; 80 81static int compare_dates(struct rtc_time *a, struct rtc_time *b) 82{ 83 if (a->tm_year != b->tm_year || 84 a->tm_mon != b->tm_mon || 85 a->tm_mday != b->tm_mday || 86 a->tm_hour != b->tm_hour || 87 a->tm_min != b->tm_min || 88 ((b->tm_sec - a->tm_sec) > 1)) 89 return 1; 90 91 return 0; 92} 93 94int main(int argc, char **argv) 95{ 96 int i, fd, retval, irqcount = 0, dangerous = 0; 97 unsigned long tmp, data; 98 struct rtc_time rtc_tm; 99 const char *rtc = default_rtc; 100 struct timeval start, end, diff; 101 102 switch (argc) { 103 case 3: 104 if (*argv[2] == 'd') 105 dangerous = 1; 106 case 2: 107 rtc = argv[1]; 108 /* FALLTHROUGH */ 109 case 1: 110 break; 111 default: 112 fprintf(stderr, "usage: rtctest [rtcdev] [d]\n"); 113 return 1; 114 } 115 116 fd = open(rtc, O_RDONLY); 117 118 if (fd == -1) { 119 perror(rtc); 120 exit(errno); 121 } 122 123 fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n"); 124 125 /* Turn on update interrupts (one per second) */ 126 retval = ioctl(fd, RTC_UIE_ON, 0); 127 if (retval == -1) { 128 if (errno == EINVAL) { 129 fprintf(stderr, 130 "\n...Update IRQs not supported.\n"); 131 goto test_READ; 132 } 133 perror("RTC_UIE_ON ioctl"); 134 exit(errno); 135 } 136 137 fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading %s:", 138 rtc); 139 fflush(stderr); 140 for (i=1; i<6; i++) { 141 /* This read will block */ 142 retval = read(fd, &data, sizeof(unsigned long)); 143 if (retval == -1) { 144 perror("read"); 145 exit(errno); 146 } 147 fprintf(stderr, " %d",i); 148 fflush(stderr); 149 irqcount++; 150 } 151 152 fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:"); 153 fflush(stderr); 154 for (i=1; i<6; i++) { 155 struct timeval tv = {5, 0}; /* 5 second timeout on select */ 156 fd_set readfds; 157 158 FD_ZERO(&readfds); 159 FD_SET(fd, &readfds); 160 /* The select will wait until an RTC interrupt happens. */ 161 retval = select(fd+1, &readfds, NULL, NULL, &tv); 162 if (retval == -1) { 163 perror("select"); 164 exit(errno); 165 } 166 /* This read won't block unlike the select-less case above. */ 167 retval = read(fd, &data, sizeof(unsigned long)); 168 if (retval == -1) { 169 perror("read"); 170 exit(errno); 171 } 172 fprintf(stderr, " %d",i); 173 fflush(stderr); 174 irqcount++; 175 } 176 177 /* Turn off update interrupts */ 178 retval = ioctl(fd, RTC_UIE_OFF, 0); 179 if (retval == -1) { 180 perror("RTC_UIE_OFF ioctl"); 181 exit(errno); 182 } 183 184test_READ: 185 /* Read the RTC time/date */ 186 retval = ioctl(fd, RTC_RD_TIME, &rtc_tm); 187 if (retval == -1) { 188 perror("RTC_RD_TIME ioctl"); 189 exit(errno); 190 } 191 192 fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n", 193 rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900, 194 rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); 195 196 /* Set the alarm to 5 sec in the future, and check for rollover */ 197 rtc_tm.tm_sec += 5; 198 if (rtc_tm.tm_sec >= 60) { 199 rtc_tm.tm_sec %= 60; 200 rtc_tm.tm_min++; 201 } 202 if (rtc_tm.tm_min == 60) { 203 rtc_tm.tm_min = 0; 204 rtc_tm.tm_hour++; 205 } 206 if (rtc_tm.tm_hour == 24) 207 rtc_tm.tm_hour = 0; 208 209 retval = ioctl(fd, RTC_ALM_SET, &rtc_tm); 210 if (retval == -1) { 211 if (errno == EINVAL) { 212 fprintf(stderr, 213 "\n...Alarm IRQs not supported.\n"); 214 goto test_PIE; 215 } 216 217 perror("RTC_ALM_SET ioctl"); 218 exit(errno); 219 } 220 221 /* Read the current alarm settings */ 222 retval = ioctl(fd, RTC_ALM_READ, &rtc_tm); 223 if (retval == -1) { 224 if (errno == EINVAL) { 225 fprintf(stderr, 226 "\n...EINVAL reading current alarm setting.\n"); 227 goto test_PIE; 228 } 229 perror("RTC_ALM_READ ioctl"); 230 exit(errno); 231 } 232 233 fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n", 234 rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); 235 236 /* Enable alarm interrupts */ 237 retval = ioctl(fd, RTC_AIE_ON, 0); 238 if (retval == -1) { 239 if (errno == EINVAL || errno == EIO) { 240 fprintf(stderr, 241 "\n...Alarm IRQs not supported.\n"); 242 goto test_PIE; 243 } 244 245 perror("RTC_AIE_ON ioctl"); 246 exit(errno); 247 } 248 249 fprintf(stderr, "Waiting 5 seconds for alarm..."); 250 fflush(stderr); 251 /* This blocks until the alarm ring causes an interrupt */ 252 retval = read(fd, &data, sizeof(unsigned long)); 253 if (retval == -1) { 254 perror("read"); 255 exit(errno); 256 } 257 irqcount++; 258 fprintf(stderr, " okay. Alarm rang.\n"); 259 260 /* Disable alarm interrupts */ 261 retval = ioctl(fd, RTC_AIE_OFF, 0); 262 if (retval == -1) { 263 perror("RTC_AIE_OFF ioctl"); 264 exit(errno); 265 } 266 267test_PIE: 268 /* Read periodic IRQ rate */ 269 retval = ioctl(fd, RTC_IRQP_READ, &tmp); 270 if (retval == -1) { 271 /* not all RTCs support periodic IRQs */ 272 if (errno == EINVAL) { 273 fprintf(stderr, "\nNo periodic IRQ support\n"); 274 goto test_DATE; 275 } 276 perror("RTC_IRQP_READ ioctl"); 277 exit(errno); 278 } 279 fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", tmp); 280 281 fprintf(stderr, "Counting 20 interrupts at:"); 282 fflush(stderr); 283 284 /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */ 285 for (tmp=2; tmp<=64; tmp*=2) { 286 287 retval = ioctl(fd, RTC_IRQP_SET, tmp); 288 if (retval == -1) { 289 /* not all RTCs can change their periodic IRQ rate */ 290 if (errno == EINVAL) { 291 fprintf(stderr, 292 "\n...Periodic IRQ rate is fixed\n"); 293 goto test_DATE; 294 } 295 perror("RTC_IRQP_SET ioctl"); 296 exit(errno); 297 } 298 299 fprintf(stderr, "\n%ldHz:\t", tmp); 300 fflush(stderr); 301 302 /* Enable periodic interrupts */ 303 retval = ioctl(fd, RTC_PIE_ON, 0); 304 if (retval == -1) { 305 perror("RTC_PIE_ON ioctl"); 306 exit(errno); 307 } 308 309 for (i=1; i<21; i++) { 310 gettimeofday(&start, NULL); 311 /* This blocks */ 312 retval = read(fd, &data, sizeof(unsigned long)); 313 if (retval == -1) { 314 perror("read"); 315 exit(errno); 316 } 317 gettimeofday(&end, NULL); 318 timersub(&end, &start, &diff); 319 if (diff.tv_sec > 0 || 320 diff.tv_usec > ((1000000L / tmp) * 1.10)) { 321 fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n", 322 diff.tv_sec, diff.tv_usec, 323 (1000000L / tmp)); 324 fflush(stdout); 325 exit(-1); 326 } 327 328 fprintf(stderr, " %d",i); 329 fflush(stderr); 330 irqcount++; 331 } 332 333 /* Disable periodic interrupts */ 334 retval = ioctl(fd, RTC_PIE_OFF, 0); 335 if (retval == -1) { 336 perror("RTC_PIE_OFF ioctl"); 337 exit(errno); 338 } 339 } 340 341test_DATE: 342 if (!dangerous) 343 goto done; 344 345 fprintf(stderr, "\nTesting problematic dates\n"); 346 347 for (i = 0; i < ARRAY_SIZE(cutoff_dates); i++) { 348 struct rtc_time current; 349 350 /* Write the new date in RTC */ 351 retval = ioctl(fd, RTC_SET_TIME, &cutoff_dates[i]); 352 if (retval == -1) { 353 perror("RTC_SET_TIME ioctl"); 354 close(fd); 355 exit(errno); 356 } 357 358 /* Read back */ 359 retval = ioctl(fd, RTC_RD_TIME, &current); 360 if (retval == -1) { 361 perror("RTC_RD_TIME ioctl"); 362 exit(errno); 363 } 364 365 if(compare_dates(&cutoff_dates[i], &current)) { 366 fprintf(stderr,"Setting date %d failed\n", 367 cutoff_dates[i].tm_year + 1900); 368 goto done; 369 } 370 371 cutoff_dates[i].tm_sec += 5; 372 373 /* Write the new alarm in RTC */ 374 retval = ioctl(fd, RTC_ALM_SET, &cutoff_dates[i]); 375 if (retval == -1) { 376 perror("RTC_ALM_SET ioctl"); 377 close(fd); 378 exit(errno); 379 } 380 381 /* Read back */ 382 retval = ioctl(fd, RTC_ALM_READ, &current); 383 if (retval == -1) { 384 perror("RTC_ALM_READ ioctl"); 385 exit(errno); 386 } 387 388 if(compare_dates(&cutoff_dates[i], &current)) { 389 fprintf(stderr,"Setting alarm %d failed\n", 390 cutoff_dates[i].tm_year + 1900); 391 goto done; 392 } 393 394 fprintf(stderr, "Setting year %d is OK \n", 395 cutoff_dates[i].tm_year + 1900); 396 } 397done: 398 fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n"); 399 400 close(fd); 401 402 return 0; 403}