Commit | Line | Data |
---|---|---|
4a5fd815 PB |
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 | */ | |
30 | static const char default_rtc[] = "/dev/rtc0"; | |
31 | ||
32 | ||
33 | int 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; | |
0b63accf | 39 | struct timeval start, end, diff; |
4a5fd815 PB |
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) { | |
e21a47ff | 64 | if (errno == EINVAL) { |
4a5fd815 PB |
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 | ||
120 | test_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 == ENOTTY) { | |
148 | fprintf(stderr, | |
149 | "\n...Alarm IRQs not supported.\n"); | |
150 | goto test_PIE; | |
151 | } | |
152 | perror("RTC_ALM_SET ioctl"); | |
153 | exit(errno); | |
154 | } | |
155 | ||
156 | /* Read the current alarm settings */ | |
157 | retval = ioctl(fd, RTC_ALM_READ, &rtc_tm); | |
158 | if (retval == -1) { | |
159 | perror("RTC_ALM_READ ioctl"); | |
160 | exit(errno); | |
161 | } | |
162 | ||
163 | fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n", | |
164 | rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); | |
165 | ||
166 | /* Enable alarm interrupts */ | |
167 | retval = ioctl(fd, RTC_AIE_ON, 0); | |
168 | if (retval == -1) { | |
169 | perror("RTC_AIE_ON ioctl"); | |
170 | exit(errno); | |
171 | } | |
172 | ||
173 | fprintf(stderr, "Waiting 5 seconds for alarm..."); | |
174 | fflush(stderr); | |
175 | /* This blocks until the alarm ring causes an interrupt */ | |
176 | retval = read(fd, &data, sizeof(unsigned long)); | |
177 | if (retval == -1) { | |
178 | perror("read"); | |
179 | exit(errno); | |
180 | } | |
181 | irqcount++; | |
182 | fprintf(stderr, " okay. Alarm rang.\n"); | |
183 | ||
184 | /* Disable alarm interrupts */ | |
185 | retval = ioctl(fd, RTC_AIE_OFF, 0); | |
186 | if (retval == -1) { | |
187 | perror("RTC_AIE_OFF ioctl"); | |
188 | exit(errno); | |
189 | } | |
190 | ||
191 | test_PIE: | |
192 | /* Read periodic IRQ rate */ | |
193 | retval = ioctl(fd, RTC_IRQP_READ, &tmp); | |
194 | if (retval == -1) { | |
195 | /* not all RTCs support periodic IRQs */ | |
196 | if (errno == ENOTTY) { | |
197 | fprintf(stderr, "\nNo periodic IRQ support\n"); | |
198 | goto done; | |
199 | } | |
200 | perror("RTC_IRQP_READ ioctl"); | |
201 | exit(errno); | |
202 | } | |
203 | fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", tmp); | |
204 | ||
205 | fprintf(stderr, "Counting 20 interrupts at:"); | |
206 | fflush(stderr); | |
207 | ||
208 | /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */ | |
209 | for (tmp=2; tmp<=64; tmp*=2) { | |
210 | ||
211 | retval = ioctl(fd, RTC_IRQP_SET, tmp); | |
212 | if (retval == -1) { | |
213 | /* not all RTCs can change their periodic IRQ rate */ | |
214 | if (errno == ENOTTY) { | |
215 | fprintf(stderr, | |
216 | "\n...Periodic IRQ rate is fixed\n"); | |
217 | goto done; | |
218 | } | |
219 | perror("RTC_IRQP_SET ioctl"); | |
220 | exit(errno); | |
221 | } | |
222 | ||
223 | fprintf(stderr, "\n%ldHz:\t", tmp); | |
224 | fflush(stderr); | |
225 | ||
226 | /* Enable periodic interrupts */ | |
227 | retval = ioctl(fd, RTC_PIE_ON, 0); | |
228 | if (retval == -1) { | |
229 | perror("RTC_PIE_ON ioctl"); | |
230 | exit(errno); | |
231 | } | |
232 | ||
233 | for (i=1; i<21; i++) { | |
0b63accf | 234 | gettimeofday(&start, NULL); |
4a5fd815 PB |
235 | /* This blocks */ |
236 | retval = read(fd, &data, sizeof(unsigned long)); | |
237 | if (retval == -1) { | |
238 | perror("read"); | |
239 | exit(errno); | |
240 | } | |
0b63accf PB |
241 | gettimeofday(&end, NULL); |
242 | timersub(&end, &start, &diff); | |
243 | if (diff.tv_sec > 0 || | |
244 | diff.tv_usec > ((1000000L / tmp) * 1.10)) { | |
245 | fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n", | |
246 | diff.tv_sec, diff.tv_usec, | |
247 | (1000000L / tmp)); | |
248 | fflush(stdout); | |
249 | exit(-1); | |
250 | } | |
251 | ||
4a5fd815 PB |
252 | fprintf(stderr, " %d",i); |
253 | fflush(stderr); | |
254 | irqcount++; | |
255 | } | |
256 | ||
257 | /* Disable periodic interrupts */ | |
258 | retval = ioctl(fd, RTC_PIE_OFF, 0); | |
259 | if (retval == -1) { | |
260 | perror("RTC_PIE_OFF ioctl"); | |
261 | exit(errno); | |
262 | } | |
263 | } | |
264 | ||
265 | done: | |
266 | fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n"); | |
267 | ||
268 | close(fd); | |
269 | ||
270 | return 0; | |
271 | } |