Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/Documentation/rtc.txt
Parent Directory | Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 6 months ago) by niro
File MIME type: text/plain
File size: 8251 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 6 months ago) by niro
File MIME type: text/plain
File size: 8251 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | |
2 | Real Time Clock Driver for Linux |
3 | ================================ |
4 | |
5 | All PCs (even Alpha machines) have a Real Time Clock built into them. |
6 | Usually they are built into the chipset of the computer, but some may |
7 | actually have a Motorola MC146818 (or clone) on the board. This is the |
8 | clock that keeps the date and time while your computer is turned off. |
9 | |
10 | However it can also be used to generate signals from a slow 2Hz to a |
11 | relatively fast 8192Hz, in increments of powers of two. These signals |
12 | are reported by interrupt number 8. (Oh! So *that* is what IRQ 8 is |
13 | for...) It can also function as a 24hr alarm, raising IRQ 8 when the |
14 | alarm goes off. The alarm can also be programmed to only check any |
15 | subset of the three programmable values, meaning that it could be set to |
16 | ring on the 30th second of the 30th minute of every hour, for example. |
17 | The clock can also be set to generate an interrupt upon every clock |
18 | update, thus generating a 1Hz signal. |
19 | |
20 | The interrupts are reported via /dev/rtc (major 10, minor 135, read only |
21 | character device) in the form of an unsigned long. The low byte contains |
22 | the type of interrupt (update-done, alarm-rang, or periodic) that was |
23 | raised, and the remaining bytes contain the number of interrupts since |
24 | the last read. Status information is reported through the pseudo-file |
25 | /proc/driver/rtc if the /proc filesystem was enabled. The driver has |
26 | built in locking so that only one process is allowed to have the /dev/rtc |
27 | interface open at a time. |
28 | |
29 | A user process can monitor these interrupts by doing a read(2) or a |
30 | select(2) on /dev/rtc -- either will block/stop the user process until |
31 | the next interrupt is received. This is useful for things like |
32 | reasonably high frequency data acquisition where one doesn't want to |
33 | burn up 100% CPU by polling gettimeofday etc. etc. |
34 | |
35 | At high frequencies, or under high loads, the user process should check |
36 | the number of interrupts received since the last read to determine if |
37 | there has been any interrupt "pileup" so to speak. Just for reference, a |
38 | typical 486-33 running a tight read loop on /dev/rtc will start to suffer |
39 | occasional interrupt pileup (i.e. > 1 IRQ event since last read) for |
40 | frequencies above 1024Hz. So you really should check the high bytes |
41 | of the value you read, especially at frequencies above that of the |
42 | normal timer interrupt, which is 100Hz. |
43 | |
44 | Programming and/or enabling interrupt frequencies greater than 64Hz is |
45 | only allowed by root. This is perhaps a bit conservative, but we don't want |
46 | an evil user generating lots of IRQs on a slow 386sx-16, where it might have |
47 | a negative impact on performance. Note that the interrupt handler is only |
48 | a few lines of code to minimize any possibility of this effect. |
49 | |
50 | Also, if the kernel time is synchronized with an external source, the |
51 | kernel will write the time back to the CMOS clock every 11 minutes. In |
52 | the process of doing this, the kernel briefly turns off RTC periodic |
53 | interrupts, so be aware of this if you are doing serious work. If you |
54 | don't synchronize the kernel time with an external source (via ntp or |
55 | whatever) then the kernel will keep its hands off the RTC, allowing you |
56 | exclusive access to the device for your applications. |
57 | |
58 | The alarm and/or interrupt frequency are programmed into the RTC via |
59 | various ioctl(2) calls as listed in ./include/linux/rtc.h |
60 | Rather than write 50 pages describing the ioctl() and so on, it is |
61 | perhaps more useful to include a small test program that demonstrates |
62 | how to use them, and demonstrates the features of the driver. This is |
63 | probably a lot more useful to people interested in writing applications |
64 | that will be using this driver. |
65 | |
66 | Paul Gortmaker |
67 | |
68 | -------------------- 8< ---------------- 8< ----------------------------- |
69 | |
70 | /* |
71 | * Real Time Clock Driver Test/Example Program |
72 | * |
73 | * Compile with: |
74 | * gcc -s -Wall -Wstrict-prototypes rtctest.c -o rtctest |
75 | * |
76 | * Copyright (C) 1996, Paul Gortmaker. |
77 | * |
78 | * Released under the GNU General Public License, version 2, |
79 | * included herein by reference. |
80 | * |
81 | */ |
82 | |
83 | #include <stdio.h> |
84 | #include <linux/rtc.h> |
85 | #include <sys/ioctl.h> |
86 | #include <sys/time.h> |
87 | #include <sys/types.h> |
88 | #include <fcntl.h> |
89 | #include <unistd.h> |
90 | #include <errno.h> |
91 | |
92 | int main(void) { |
93 | |
94 | int i, fd, retval, irqcount = 0; |
95 | unsigned long tmp, data; |
96 | struct rtc_time rtc_tm; |
97 | |
98 | fd = open ("/dev/rtc", O_RDONLY); |
99 | |
100 | if (fd == -1) { |
101 | perror("/dev/rtc"); |
102 | exit(errno); |
103 | } |
104 | |
105 | fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n"); |
106 | |
107 | /* Turn on update interrupts (one per second) */ |
108 | retval = ioctl(fd, RTC_UIE_ON, 0); |
109 | if (retval == -1) { |
110 | perror("ioctl"); |
111 | exit(errno); |
112 | } |
113 | |
114 | fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading /dev/rtc:"); |
115 | fflush(stderr); |
116 | for (i=1; i<6; i++) { |
117 | /* This read will block */ |
118 | retval = read(fd, &data, sizeof(unsigned long)); |
119 | if (retval == -1) { |
120 | perror("read"); |
121 | exit(errno); |
122 | } |
123 | fprintf(stderr, " %d",i); |
124 | fflush(stderr); |
125 | irqcount++; |
126 | } |
127 | |
128 | fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:"); |
129 | fflush(stderr); |
130 | for (i=1; i<6; i++) { |
131 | struct timeval tv = {5, 0}; /* 5 second timeout on select */ |
132 | fd_set readfds; |
133 | |
134 | FD_ZERO(&readfds); |
135 | FD_SET(fd, &readfds); |
136 | /* The select will wait until an RTC interrupt happens. */ |
137 | retval = select(fd+1, &readfds, NULL, NULL, &tv); |
138 | if (retval == -1) { |
139 | perror("select"); |
140 | exit(errno); |
141 | } |
142 | /* This read won't block unlike the select-less case above. */ |
143 | retval = read(fd, &data, sizeof(unsigned long)); |
144 | if (retval == -1) { |
145 | perror("read"); |
146 | exit(errno); |
147 | } |
148 | fprintf(stderr, " %d",i); |
149 | fflush(stderr); |
150 | irqcount++; |
151 | } |
152 | |
153 | /* Turn off update interrupts */ |
154 | retval = ioctl(fd, RTC_UIE_OFF, 0); |
155 | if (retval == -1) { |
156 | perror("ioctl"); |
157 | exit(errno); |
158 | } |
159 | |
160 | /* Read the RTC time/date */ |
161 | retval = ioctl(fd, RTC_RD_TIME, &rtc_tm); |
162 | if (retval == -1) { |
163 | perror("ioctl"); |
164 | exit(errno); |
165 | } |
166 | |
167 | fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n", |
168 | rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900, |
169 | rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); |
170 | |
171 | /* Set the alarm to 5 sec in the future, and check for rollover */ |
172 | rtc_tm.tm_sec += 5; |
173 | if (rtc_tm.tm_sec >= 60) { |
174 | rtc_tm.tm_sec %= 60; |
175 | rtc_tm.tm_min++; |
176 | } |
177 | if (rtc_tm.tm_min == 60) { |
178 | rtc_tm.tm_min = 0; |
179 | rtc_tm.tm_hour++; |
180 | } |
181 | if (rtc_tm.tm_hour == 24) |
182 | rtc_tm.tm_hour = 0; |
183 | |
184 | retval = ioctl(fd, RTC_ALM_SET, &rtc_tm); |
185 | if (retval == -1) { |
186 | perror("ioctl"); |
187 | exit(errno); |
188 | } |
189 | |
190 | /* Read the current alarm settings */ |
191 | retval = ioctl(fd, RTC_ALM_READ, &rtc_tm); |
192 | if (retval == -1) { |
193 | perror("ioctl"); |
194 | exit(errno); |
195 | } |
196 | |
197 | fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n", |
198 | rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); |
199 | |
200 | /* Enable alarm interrupts */ |
201 | retval = ioctl(fd, RTC_AIE_ON, 0); |
202 | if (retval == -1) { |
203 | perror("ioctl"); |
204 | exit(errno); |
205 | } |
206 | |
207 | fprintf(stderr, "Waiting 5 seconds for alarm..."); |
208 | fflush(stderr); |
209 | /* This blocks until the alarm ring causes an interrupt */ |
210 | retval = read(fd, &data, sizeof(unsigned long)); |
211 | if (retval == -1) { |
212 | perror("read"); |
213 | exit(errno); |
214 | } |
215 | irqcount++; |
216 | fprintf(stderr, " okay. Alarm rang.\n"); |
217 | |
218 | /* Disable alarm interrupts */ |
219 | retval = ioctl(fd, RTC_AIE_OFF, 0); |
220 | if (retval == -1) { |
221 | perror("ioctl"); |
222 | exit(errno); |
223 | } |
224 | |
225 | /* Read periodic IRQ rate */ |
226 | retval = ioctl(fd, RTC_IRQP_READ, &tmp); |
227 | if (retval == -1) { |
228 | perror("ioctl"); |
229 | exit(errno); |
230 | } |
231 | fprintf(stderr, "\nPeriodic IRQ rate was %ldHz.\n", tmp); |
232 | |
233 | fprintf(stderr, "Counting 20 interrupts at:"); |
234 | fflush(stderr); |
235 | |
236 | /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */ |
237 | for (tmp=2; tmp<=64; tmp*=2) { |
238 | |
239 | retval = ioctl(fd, RTC_IRQP_SET, tmp); |
240 | if (retval == -1) { |
241 | perror("ioctl"); |
242 | exit(errno); |
243 | } |
244 | |
245 | fprintf(stderr, "\n%ldHz:\t", tmp); |
246 | fflush(stderr); |
247 | |
248 | /* Enable periodic interrupts */ |
249 | retval = ioctl(fd, RTC_PIE_ON, 0); |
250 | if (retval == -1) { |
251 | perror("ioctl"); |
252 | exit(errno); |
253 | } |
254 | |
255 | for (i=1; i<21; i++) { |
256 | /* This blocks */ |
257 | retval = read(fd, &data, sizeof(unsigned long)); |
258 | if (retval == -1) { |
259 | perror("read"); |
260 | exit(errno); |
261 | } |
262 | fprintf(stderr, " %d",i); |
263 | fflush(stderr); |
264 | irqcount++; |
265 | } |
266 | |
267 | /* Disable periodic interrupts */ |
268 | retval = ioctl(fd, RTC_PIE_OFF, 0); |
269 | if (retval == -1) { |
270 | perror("ioctl"); |
271 | exit(errno); |
272 | } |
273 | } |
274 | |
275 | fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n"); |
276 | fprintf(stderr, "\nTyping \"cat /proc/interrupts\" will show %d more events on IRQ 8.\n\n", |
277 | irqcount); |
278 | |
279 | close(fd); |
280 | return 0; |
281 | |
282 | } /* end main */ |