]>
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) { | |
cd26fca2 | 147 | if (errno == EINVAL) { |
4a5fd815 PB |
148 | fprintf(stderr, |
149 | "\n...Alarm IRQs not supported.\n"); | |
150 | goto test_PIE; | |
151 | } | |
cd26fca2 | 152 | |
4a5fd815 PB |
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) { | |
cd26fca2 AS |
170 | if (errno == EINVAL) { |
171 | fprintf(stderr, | |
172 | "\n...Alarm IRQs not supported.\n"); | |
173 | goto test_PIE; | |
174 | } | |
175 | ||
4a5fd815 PB |
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 | ||
198 | test_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 == ENOTTY) { | |
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 == ENOTTY) { | |
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++) { | |
0b63accf | 241 | gettimeofday(&start, NULL); |
4a5fd815 PB |
242 | /* This blocks */ |
243 | retval = read(fd, &data, sizeof(unsigned long)); | |
244 | if (retval == -1) { | |
245 | perror("read"); | |
246 | exit(errno); | |
247 | } | |
0b63accf PB |
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 | ||
4a5fd815 PB |
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 | ||
272 | done: | |
273 | fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n"); | |
274 | ||
275 | close(fd); | |
276 | ||
277 | return 0; | |
278 | } |