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