]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | High Precision Event Timer Driver for Linux |
2 | ||
8a0d4900 RD |
3 | The High Precision Event Timer (HPET) hardware is the future replacement |
4 | for the 8254 and Real Time Clock (RTC) periodic timer functionality. | |
fad6a94e | 5 | Each HPET can have up to 32 timers. It is possible to configure the |
8a0d4900 RD |
6 | first two timers as legacy replacements for 8254 and RTC periodic timers. |
7 | A specification done by Intel and Microsoft can be found at | |
8 | <http://www.intel.com/hardwaredesign/hpetspec.htm>. | |
9 | ||
10 | The driver supports detection of HPET driver allocation and initialization | |
11 | of the HPET before the driver module_init routine is called. This enables | |
12 | platform code which uses timer 0 or 1 as the main timer to intercept HPET | |
13 | initialization. An example of this initialization can be found in | |
1da177e4 LT |
14 | arch/i386/kernel/time_hpet.c. |
15 | ||
8a0d4900 RD |
16 | The driver provides two APIs which are very similar to the API found in |
17 | the rtc.c driver. There is a user space API and a kernel space API. | |
18 | An example user space program is provided below. | |
1da177e4 LT |
19 | |
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <unistd.h> | |
23 | #include <fcntl.h> | |
24 | #include <string.h> | |
25 | #include <memory.h> | |
26 | #include <malloc.h> | |
27 | #include <time.h> | |
28 | #include <ctype.h> | |
29 | #include <sys/types.h> | |
30 | #include <sys/wait.h> | |
31 | #include <signal.h> | |
32 | #include <fcntl.h> | |
33 | #include <errno.h> | |
34 | #include <sys/time.h> | |
35 | #include <linux/hpet.h> | |
36 | ||
37 | ||
38 | extern void hpet_open_close(int, const char **); | |
39 | extern void hpet_info(int, const char **); | |
40 | extern void hpet_poll(int, const char **); | |
41 | extern void hpet_fasync(int, const char **); | |
42 | extern void hpet_read(int, const char **); | |
43 | ||
44 | #include <sys/poll.h> | |
45 | #include <sys/ioctl.h> | |
46 | #include <signal.h> | |
47 | ||
48 | struct hpet_command { | |
49 | char *command; | |
50 | void (*func)(int argc, const char ** argv); | |
51 | } hpet_command[] = { | |
52 | { | |
53 | "open-close", | |
54 | hpet_open_close | |
55 | }, | |
56 | { | |
57 | "info", | |
58 | hpet_info | |
59 | }, | |
60 | { | |
61 | "poll", | |
62 | hpet_poll | |
63 | }, | |
64 | { | |
65 | "fasync", | |
66 | hpet_fasync | |
67 | }, | |
68 | }; | |
69 | ||
70 | int | |
71 | main(int argc, const char ** argv) | |
72 | { | |
73 | int i; | |
74 | ||
75 | argc--; | |
76 | argv++; | |
77 | ||
78 | if (!argc) { | |
79 | fprintf(stderr, "-hpet: requires command\n"); | |
80 | return -1; | |
81 | } | |
82 | ||
83 | ||
84 | for (i = 0; i < (sizeof (hpet_command) / sizeof (hpet_command[0])); i++) | |
85 | if (!strcmp(argv[0], hpet_command[i].command)) { | |
86 | argc--; | |
87 | argv++; | |
88 | fprintf(stderr, "-hpet: executing %s\n", | |
89 | hpet_command[i].command); | |
90 | hpet_command[i].func(argc, argv); | |
91 | return 0; | |
92 | } | |
93 | ||
94 | fprintf(stderr, "do_hpet: command %s not implemented\n", argv[0]); | |
95 | ||
96 | return -1; | |
97 | } | |
98 | ||
99 | void | |
100 | hpet_open_close(int argc, const char **argv) | |
101 | { | |
102 | int fd; | |
103 | ||
104 | if (argc != 1) { | |
105 | fprintf(stderr, "hpet_open_close: device-name\n"); | |
106 | return; | |
107 | } | |
108 | ||
109 | fd = open(argv[0], O_RDONLY); | |
110 | if (fd < 0) | |
111 | fprintf(stderr, "hpet_open_close: open failed\n"); | |
112 | else | |
113 | close(fd); | |
114 | ||
115 | return; | |
116 | } | |
117 | ||
118 | void | |
119 | hpet_info(int argc, const char **argv) | |
120 | { | |
121 | } | |
122 | ||
123 | void | |
124 | hpet_poll(int argc, const char **argv) | |
125 | { | |
126 | unsigned long freq; | |
127 | int iterations, i, fd; | |
128 | struct pollfd pfd; | |
129 | struct hpet_info info; | |
130 | struct timeval stv, etv; | |
131 | struct timezone tz; | |
132 | long usec; | |
133 | ||
134 | if (argc != 3) { | |
135 | fprintf(stderr, "hpet_poll: device-name freq iterations\n"); | |
136 | return; | |
137 | } | |
138 | ||
139 | freq = atoi(argv[1]); | |
140 | iterations = atoi(argv[2]); | |
141 | ||
142 | fd = open(argv[0], O_RDONLY); | |
143 | ||
144 | if (fd < 0) { | |
145 | fprintf(stderr, "hpet_poll: open of %s failed\n", argv[0]); | |
146 | return; | |
147 | } | |
148 | ||
149 | if (ioctl(fd, HPET_IRQFREQ, freq) < 0) { | |
150 | fprintf(stderr, "hpet_poll: HPET_IRQFREQ failed\n"); | |
151 | goto out; | |
152 | } | |
153 | ||
154 | if (ioctl(fd, HPET_INFO, &info) < 0) { | |
155 | fprintf(stderr, "hpet_poll: failed to get info\n"); | |
156 | goto out; | |
157 | } | |
158 | ||
159 | fprintf(stderr, "hpet_poll: info.hi_flags 0x%lx\n", info.hi_flags); | |
160 | ||
161 | if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) { | |
162 | fprintf(stderr, "hpet_poll: HPET_EPI failed\n"); | |
163 | goto out; | |
164 | } | |
165 | ||
166 | if (ioctl(fd, HPET_IE_ON, 0) < 0) { | |
167 | fprintf(stderr, "hpet_poll, HPET_IE_ON failed\n"); | |
168 | goto out; | |
169 | } | |
170 | ||
171 | pfd.fd = fd; | |
172 | pfd.events = POLLIN; | |
173 | ||
174 | for (i = 0; i < iterations; i++) { | |
175 | pfd.revents = 0; | |
176 | gettimeofday(&stv, &tz); | |
177 | if (poll(&pfd, 1, -1) < 0) | |
178 | fprintf(stderr, "hpet_poll: poll failed\n"); | |
179 | else { | |
180 | long data; | |
181 | ||
182 | gettimeofday(&etv, &tz); | |
183 | usec = stv.tv_sec * 1000000 + stv.tv_usec; | |
184 | usec = (etv.tv_sec * 1000000 + etv.tv_usec) - usec; | |
185 | ||
186 | fprintf(stderr, | |
187 | "hpet_poll: expired time = 0x%lx\n", usec); | |
188 | ||
189 | fprintf(stderr, "hpet_poll: revents = 0x%x\n", | |
190 | pfd.revents); | |
191 | ||
192 | if (read(fd, &data, sizeof(data)) != sizeof(data)) { | |
193 | fprintf(stderr, "hpet_poll: read failed\n"); | |
194 | } | |
195 | else | |
196 | fprintf(stderr, "hpet_poll: data 0x%lx\n", | |
197 | data); | |
198 | } | |
199 | } | |
200 | ||
201 | out: | |
202 | close(fd); | |
203 | return; | |
204 | } | |
205 | ||
206 | static int hpet_sigio_count; | |
207 | ||
208 | static void | |
209 | hpet_sigio(int val) | |
210 | { | |
211 | fprintf(stderr, "hpet_sigio: called\n"); | |
212 | hpet_sigio_count++; | |
213 | } | |
214 | ||
215 | void | |
216 | hpet_fasync(int argc, const char **argv) | |
217 | { | |
218 | unsigned long freq; | |
219 | int iterations, i, fd, value; | |
220 | sig_t oldsig; | |
221 | struct hpet_info info; | |
222 | ||
223 | hpet_sigio_count = 0; | |
224 | fd = -1; | |
225 | ||
226 | if ((oldsig = signal(SIGIO, hpet_sigio)) == SIG_ERR) { | |
227 | fprintf(stderr, "hpet_fasync: failed to set signal handler\n"); | |
228 | return; | |
229 | } | |
230 | ||
231 | if (argc != 3) { | |
232 | fprintf(stderr, "hpet_fasync: device-name freq iterations\n"); | |
233 | goto out; | |
234 | } | |
235 | ||
236 | fd = open(argv[0], O_RDONLY); | |
237 | ||
238 | if (fd < 0) { | |
239 | fprintf(stderr, "hpet_fasync: failed to open %s\n", argv[0]); | |
240 | return; | |
241 | } | |
242 | ||
243 | ||
244 | if ((fcntl(fd, F_SETOWN, getpid()) == 1) || | |
245 | ((value = fcntl(fd, F_GETFL)) == 1) || | |
246 | (fcntl(fd, F_SETFL, value | O_ASYNC) == 1)) { | |
247 | fprintf(stderr, "hpet_fasync: fcntl failed\n"); | |
248 | goto out; | |
249 | } | |
250 | ||
251 | freq = atoi(argv[1]); | |
252 | iterations = atoi(argv[2]); | |
253 | ||
254 | if (ioctl(fd, HPET_IRQFREQ, freq) < 0) { | |
255 | fprintf(stderr, "hpet_fasync: HPET_IRQFREQ failed\n"); | |
256 | goto out; | |
257 | } | |
258 | ||
259 | if (ioctl(fd, HPET_INFO, &info) < 0) { | |
260 | fprintf(stderr, "hpet_fasync: failed to get info\n"); | |
261 | goto out; | |
262 | } | |
263 | ||
264 | fprintf(stderr, "hpet_fasync: info.hi_flags 0x%lx\n", info.hi_flags); | |
265 | ||
266 | if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) { | |
267 | fprintf(stderr, "hpet_fasync: HPET_EPI failed\n"); | |
268 | goto out; | |
269 | } | |
270 | ||
271 | if (ioctl(fd, HPET_IE_ON, 0) < 0) { | |
272 | fprintf(stderr, "hpet_fasync, HPET_IE_ON failed\n"); | |
273 | goto out; | |
274 | } | |
275 | ||
276 | for (i = 0; i < iterations; i++) { | |
277 | (void) pause(); | |
278 | fprintf(stderr, "hpet_fasync: count = %d\n", hpet_sigio_count); | |
279 | } | |
280 | ||
281 | out: | |
282 | signal(SIGIO, oldsig); | |
283 | ||
284 | if (fd >= 0) | |
285 | close(fd); | |
286 | ||
287 | return; | |
288 | } | |
289 | ||
290 | The kernel API has three interfaces exported from the driver: | |
291 | ||
292 | hpet_register(struct hpet_task *tp, int periodic) | |
293 | hpet_unregister(struct hpet_task *tp) | |
294 | hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg) | |
295 | ||
8a0d4900 RD |
296 | The kernel module using this interface fills in the ht_func and ht_data |
297 | members of the hpet_task structure before calling hpet_register. | |
298 | hpet_control simply vectors to the hpet_ioctl routine and has the same | |
299 | commands and respective arguments as the user API. hpet_unregister | |
1da177e4 | 300 | is used to terminate usage of the HPET timer reserved by hpet_register. |