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 25/* 26 * This expects the new RTC class driver framework, working with 27 * clocks that will often not be clones of what the PC-AT had. 28 * Use the command line to specify another RTC if you need one. 29 */ 30static const char default_rtc[] = "/dev/rtc0"; 31 32 33int main(int argc, char **argv) 34{ 35 int i, fd, retval, irqcount = 0; 36 unsigned long tmp, data; 37 struct rtc_time rtc_tm; 38 const char *rtc = default_rtc; 39 struct timeval start, end, diff; 40 41 switch (argc) { 42 case 2: 43 rtc = argv[1]; 44 /* FALLTHROUGH */ 45 case 1: 46 break; 47 default: 48 fprintf(stderr, "usage: rtctest [rtcdev]\n"); 49 return 1; 50 } 51 52 fd = open(rtc, O_RDONLY); 53 54 if (fd == -1) { 55 perror(rtc); 56 exit(errno); 57 } 58 59 fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n"); 60 61 /* Turn on update interrupts (one per second) */ 62 retval = ioctl(fd, RTC_UIE_ON, 0); 63 if (retval == -1) { 64 if (errno == EINVAL) { 65 fprintf(stderr, 66 "\n...Update IRQs not supported.\n"); 67 goto test_READ; 68 } 69 perror("RTC_UIE_ON ioctl"); 70 exit(errno); 71 } 72 73 fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading %s:", 74 rtc); 75 fflush(stderr); 76 for (i=1; i<6; i++) { 77 /* This read will block */ 78 retval = read(fd, &data, sizeof(unsigned long)); 79 if (retval == -1) { 80 perror("read"); 81 exit(errno); 82 } 83 fprintf(stderr, " %d",i); 84 fflush(stderr); 85 irqcount++; 86 } 87 88 fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:"); 89 fflush(stderr); 90 for (i=1; i<6; i++) { 91 struct timeval tv = {5, 0}; /* 5 second timeout on select */ 92 fd_set readfds; 93 94 FD_ZERO(&readfds); 95 FD_SET(fd, &readfds); 96 /* The select will wait until an RTC interrupt happens. */ 97 retval = select(fd+1, &readfds, NULL, NULL, &tv); 98 if (retval == -1) { 99 perror("select"); 100 exit(errno); 101 } 102 /* This read won't block unlike the select-less case above. */ 103 retval = read(fd, &data, sizeof(unsigned long)); 104 if (retval == -1) { 105 perror("read"); 106 exit(errno); 107 } 108 fprintf(stderr, " %d",i); 109 fflush(stderr); 110 irqcount++; 111 } 112 113 /* Turn off update interrupts */ 114 retval = ioctl(fd, RTC_UIE_OFF, 0); 115 if (retval == -1) { 116 perror("RTC_UIE_OFF ioctl"); 117 exit(errno); 118 } 119 120test_READ: 121 /* Read the RTC time/date */ 122 retval = ioctl(fd, RTC_RD_TIME, &rtc_tm); 123 if (retval == -1) { 124 perror("RTC_RD_TIME ioctl"); 125 exit(errno); 126 } 127 128 fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n", 129 rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900, 130 rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); 131 132 /* Set the alarm to 5 sec in the future, and check for rollover */ 133 rtc_tm.tm_sec += 5; 134 if (rtc_tm.tm_sec >= 60) { 135 rtc_tm.tm_sec %= 60; 136 rtc_tm.tm_min++; 137 } 138 if (rtc_tm.tm_min == 60) { 139 rtc_tm.tm_min = 0; 140 rtc_tm.tm_hour++; 141 } 142 if (rtc_tm.tm_hour == 24) 143 rtc_tm.tm_hour = 0; 144 145 retval = ioctl(fd, RTC_ALM_SET, &rtc_tm); 146 if (retval == -1) { 147 if (errno == EINVAL) { 148 fprintf(stderr, 149 "\n...Alarm IRQs not supported.\n"); 150 goto test_PIE; 151 } 152 153 perror("RTC_ALM_SET ioctl"); 154 exit(errno); 155 } 156 157 /* Read the current alarm settings */ 158 retval = ioctl(fd, RTC_ALM_READ, &rtc_tm); 159 if (retval == -1) { 160 perror("RTC_ALM_READ ioctl"); 161 exit(errno); 162 } 163 164 fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n", 165 rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); 166 167 /* Enable alarm interrupts */ 168 retval = ioctl(fd, RTC_AIE_ON, 0); 169 if (retval == -1) { 170 if (errno == EINVAL) { 171 fprintf(stderr, 172 "\n...Alarm IRQs not supported.\n"); 173 goto test_PIE; 174 } 175 176 perror("RTC_AIE_ON ioctl"); 177 exit(errno); 178 } 179 180 fprintf(stderr, "Waiting 5 seconds for alarm..."); 181 fflush(stderr); 182 /* This blocks until the alarm ring causes an interrupt */ 183 retval = read(fd, &data, sizeof(unsigned long)); 184 if (retval == -1) { 185 perror("read"); 186 exit(errno); 187 } 188 irqcount++; 189 fprintf(stderr, " okay. Alarm rang.\n"); 190 191 /* Disable alarm interrupts */ 192 retval = ioctl(fd, RTC_AIE_OFF, 0); 193 if (retval == -1) { 194 perror("RTC_AIE_OFF ioctl"); 195 exit(errno); 196 } 197 198test_PIE: 199 /* Read periodic IRQ rate */ 200 retval = ioctl(fd, RTC_IRQP_READ, &tmp); 201 if (retval == -1) { 202 /* not all RTCs support periodic IRQs */ 203 if (errno == EINVAL) { 204 fprintf(stderr, "\nNo periodic IRQ support\n"); 205 goto done; 206 } 207 perror("RTC_IRQP_READ ioctl"); 208 exit(errno); 209 } 210 fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", tmp); 211 212 fprintf(stderr, "Counting 20 interrupts at:"); 213 fflush(stderr); 214 215 /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */ 216 for (tmp=2; tmp<=64; tmp*=2) { 217 218 retval = ioctl(fd, RTC_IRQP_SET, tmp); 219 if (retval == -1) { 220 /* not all RTCs can change their periodic IRQ rate */ 221 if (errno == EINVAL) { 222 fprintf(stderr, 223 "\n...Periodic IRQ rate is fixed\n"); 224 goto done; 225 } 226 perror("RTC_IRQP_SET ioctl"); 227 exit(errno); 228 } 229 230 fprintf(stderr, "\n%ldHz:\t", tmp); 231 fflush(stderr); 232 233 /* Enable periodic interrupts */ 234 retval = ioctl(fd, RTC_PIE_ON, 0); 235 if (retval == -1) { 236 perror("RTC_PIE_ON ioctl"); 237 exit(errno); 238 } 239 240 for (i=1; i<21; i++) { 241 gettimeofday(&start, NULL); 242 /* This blocks */ 243 retval = read(fd, &data, sizeof(unsigned long)); 244 if (retval == -1) { 245 perror("read"); 246 exit(errno); 247 } 248 gettimeofday(&end, NULL); 249 timersub(&end, &start, &diff); 250 if (diff.tv_sec > 0 || 251 diff.tv_usec > ((1000000L / tmp) * 1.10)) { 252 fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n", 253 diff.tv_sec, diff.tv_usec, 254 (1000000L / tmp)); 255 fflush(stdout); 256 exit(-1); 257 } 258 259 fprintf(stderr, " %d",i); 260 fflush(stderr); 261 irqcount++; 262 } 263 264 /* Disable periodic interrupts */ 265 retval = ioctl(fd, RTC_PIE_OFF, 0); 266 if (retval == -1) { 267 perror("RTC_PIE_OFF ioctl"); 268 exit(errno); 269 } 270 } 271 272done: 273 fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n"); 274 275 close(fd); 276 277 return 0; 278} 279