1		High Precision Event Timer Driver for Linux
2
3The High Precision Event Timer (HPET) hardware is the future replacement
4for the 8254 and Real Time Clock (RTC) periodic timer functionality.
5Each HPET can have up to 32 timers.  It is possible to configure the
6first two timers as legacy replacements for 8254 and RTC periodic timers.
7A specification done by Intel and Microsoft can be found at
8<http://www.intel.com/hardwaredesign/hpetspec.htm>.
9
10The driver supports detection of HPET driver allocation and initialization
11of the HPET before the driver module_init routine is called.  This enables
12platform code which uses timer 0 or 1 as the main timer to intercept HPET
13initialization.  An example of this initialization can be found in
14arch/i386/kernel/time_hpet.c.
15
16The driver provides two APIs which are very similar to the API found in
17the rtc.c driver.  There is a user space API and a kernel space API.
18An example user space program is provided below.
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
38extern void hpet_open_close(int, const char **);
39extern void hpet_info(int, const char **);
40extern void hpet_poll(int, const char **);
41extern void hpet_fasync(int, const char **);
42extern void hpet_read(int, const char **);
43
44#include <sys/poll.h>
45#include <sys/ioctl.h>
46#include <signal.h>
47
48struct 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
70int
71main(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
99void
100hpet_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
118void
119hpet_info(int argc, const char **argv)
120{
121}
122
123void
124hpet_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
201out:
202	close(fd);
203	return;
204}
205
206static int hpet_sigio_count;
207
208static void
209hpet_sigio(int val)
210{
211	fprintf(stderr, "hpet_sigio: called\n");
212	hpet_sigio_count++;
213}
214
215void
216hpet_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
281out:
282	signal(SIGIO, oldsig);
283
284	if (fd >= 0)
285		close(fd);
286
287	return;
288}
289
290The 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
296The kernel module using this interface fills in the ht_func and ht_data
297members of the hpet_task structure before calling hpet_register.
298hpet_control simply vectors to the hpet_ioctl routine and has the same
299commands and respective arguments as the user API.  hpet_unregister
300is used to terminate usage of the HPET timer reserved by hpet_register.
301