xref: /illumos-gate/usr/src/uts/i86pc/io/hrtimers.c (revision 3df2e8b2fd61f45437285750d2880d6416a9200c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
23 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
24 /*	  All Rights Reserved  	*/
25 
26 /*
27  * Copyright (c) 1997, by Sun Microsystems, Inc.
28  * All rights reserved.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/sysmacros.h>
34 #include <sys/systm.h>
35 #include <sys/hrtcntl.h>
36 #include <sys/errno.h>
37 #include <sys/hrtsys.h>
38 #include <sys/time.h>
39 #include <sys/timer.h>
40 #include <sys/cmn_err.h>
41 
42 /*
43  * This file contains the code that manages the hardware clocks and
44  * timers.  We must provide UNIX with a HZ resolution clock and give
45  * the user an interface to the timers through system calls.
46  */
47 
48 static int hrt_checkres(ulong res);
49 static int hrt_bsd_cancel(int clock);
50 static int hrt_checkclock(register int clock);
51 
52 /*
53  * Argument vectors for the various flavors of hrtsys().
54  */
55 
56 #define	HRTCNTL		0
57 #define	HRTALARM	1
58 #define	HRTSLEEP	2
59 #define	HRTCANCEL	3
60 
61 struct 	hrtsysa {
62 	int	opcode;
63 };
64 
65 struct	hrtcntla {
66 	int		opcode;
67 	int		cmd;
68 	int		clk;
69 	interval_t	*intp;
70 	hrtimes_t	*hrtp;
71 };
72 
73 struct	hrtalarma {
74 	int	opcode;
75 	hrtcmd_t	*cmdp;
76 	int		cmds;
77 };
78 
79 
80 /*
81  * Hrtcntl (time control) system call.
82  */
83 
84 
85 /*ARGSUSED1*/
86 int
hrtcntl(uap,rvp)87 hrtcntl(uap, rvp)
88 	register struct hrtcntla *uap;
89 	rval_t	*rvp;
90 {
91 	register int	error = 0;
92 	hrtimes_t	temptofd;
93 
94 	switch (uap->cmd) {
95 
96 	case HRT_TOFD:	/* Get the time of day */
97 
98 		if (uap->clk != CLK_STD) {
99 			error = EINVAL;
100 			break;
101 		}
102 
103 		if (copyin((caddr_t)uap->hrtp,
104 		    (caddr_t)&temptofd, sizeof (hrtimes_t))) {
105 			error = EFAULT;
106 			break;
107 		}
108 
109 		if ((error = hrt_checkres(temptofd.hrt_res)))
110 			break;
111 
112 		hrt_gettofd(&temptofd);
113 
114 		if (copyout((caddr_t)&temptofd,
115 		    (caddr_t)uap->hrtp, sizeof (hrtimes_t)))
116 			error = EFAULT;
117 
118 		break;
119 
120 	default:
121 		error = EINVAL;
122 		break;
123 	}
124 	return (error);
125 }
126 
127 /*
128  * Hrtalarm (start one or more alarms) system call.
129  */
130 
131 int
hrtalarm(uap,rvp)132 hrtalarm(uap, rvp)
133 	register struct hrtalarma *uap;
134 	rval_t	*rvp;
135 {
136 	register hrtcmd_t	*cp;
137 	hrtcmd_t		*hrcmdp;
138 	uint			alarm_cnt;
139 	int			cnt;
140 	int			error = 0;
141 	int			cmd;
142 	hrtcmd_t		timecmd;
143 	hrtimes_t		delay_ht;
144 
145 
146 	/*
147 	 * Return EINVAL for negative and zero counts.
148 	 */
149 
150 	if (uap->cmds <= 0)
151 		return (EINVAL);
152 
153 	cp = &timecmd;
154 	hrcmdp = uap->cmdp;
155 	alarm_cnt = 0;
156 
157 	/* Loop through and process each command. */
158 
159 	for (cnt = 0; cnt < uap->cmds; cnt++, hrcmdp++) {
160 
161 		if (copyin((caddr_t)hrcmdp, (caddr_t)cp, sizeof (hrtcmd_t)))
162 			return (EFAULT);
163 
164 		cmd = cp->hrtc_cmd;
165 
166 		/*
167 		 * If we try to post a Berkley Timer remove
168 		 * previous timers.
169 		 */
170 
171 		if (cmd == HRT_BSD || cmd == HRT_BSD_REP)
172 			(void) hrt_bsd_cancel(cp->hrtc_clk);
173 
174 		/*	See what kind of command we have.  */
175 
176 		switch (cmd) {
177 		case HRT_BSD:		/* one-shot timer */
178 		{
179 			struct itimerval itv;
180 			u_int which;
181 
182 			if ((error = hrt_checkclock(cp->hrtc_clk)) != 0)
183 				break;
184 			switch (cp->hrtc_clk) {
185 			case CLK_STD:
186 				which = ITIMER_REAL;
187 				break;
188 			case CLK_USERVIRT:
189 				which = ITIMER_VIRTUAL;
190 				break;
191 			case CLK_PROCVIRT:
192 				which = ITIMER_PROF;
193 				break;
194 			default:
195 				error = EINVAL;
196 				goto bad;
197 			}
198 			itv.it_value.tv_sec = cp->hrtc_int.hrt_secs;
199 			itv.it_value.tv_usec = cp->hrtc_int.hrt_rem;
200 			itv.it_interval.tv_sec = 0;
201 			itv.it_interval.tv_usec = 0;
202 			(void) xsetitimer(which, &itv, 1);
203 
204 			break;
205 		}
206 
207 		case HRT_BSD_REP:
208 		{
209 			struct itimerval itv;
210 			u_int which;
211 
212 			switch (cp->hrtc_clk) {
213 			case CLK_STD:
214 				which = ITIMER_REAL;
215 				break;
216 			case CLK_USERVIRT:
217 				which = ITIMER_VIRTUAL;
218 				break;
219 			case CLK_PROCVIRT:
220 				which = ITIMER_PROF;
221 				break;
222 			default:
223 				error = EINVAL;
224 				goto bad;
225 			}
226 			itv.it_value.tv_sec = cp->hrtc_tod.hrt_secs;
227 			itv.it_value.tv_usec = cp->hrtc_tod.hrt_rem;
228 			itv.it_interval.tv_sec = cp->hrtc_int.hrt_secs;
229 			itv.it_interval.tv_usec = cp->hrtc_int.hrt_rem;
230 			(void) xsetitimer(which, &itv, 1);
231 
232 			break;
233 		}
234 
235 		case HRT_BSD_PEND:
236 			{
237 				struct itimerval itv;
238 				u_int which;
239 
240 				switch (cp->hrtc_clk) {
241 				case CLK_STD:
242 					which = ITIMER_REAL;
243 					break;
244 				case CLK_USERVIRT:
245 					which = ITIMER_VIRTUAL;
246 					break;
247 				case CLK_PROCVIRT:
248 					which = ITIMER_PROF;
249 					break;
250 				default:
251 					error = EINVAL;
252 					goto bad;
253 				}
254 				(void) xgetitimer(which, &itv, 1);
255 				delay_ht.hrt_secs = itv.it_value.tv_sec;
256 				delay_ht.hrt_rem = itv.it_value.tv_usec;
257 			}
258 
259 			if (copyout((caddr_t)&delay_ht,
260 			    (caddr_t)&hrcmdp->hrtc_int, sizeof (hrtimes_t)))
261 				error = EFAULT;
262 
263 			break;
264 
265 		case HRT_BSD_CANCEL:
266 			if ((error = hrt_checkclock(cp->hrtc_clk)) != 0)
267 				break;
268 
269 			error = hrt_bsd_cancel(cp->hrtc_clk);
270 
271 			break;
272 
273 		default :
274 			error = EINVAL;
275 			break;
276 		}
277 bad:
278 		if (error) {
279 			cp->hrtc_flags |= HRTF_ERROR;
280 			cp->hrtc_error = error;
281 		} else {
282 			cp->hrtc_flags |= HRTF_DONE;
283 			cp->hrtc_error = 0;
284 			alarm_cnt++;
285 		}
286 		if (copyout((caddr_t)&cp->hrtc_flags,
287 		    (caddr_t)&hrcmdp->hrtc_flags,
288 		    sizeof (cp->hrtc_flags) + sizeof (cp->hrtc_error))) {
289 			error = EFAULT;
290 			return (error);
291 		}
292 	}
293 	rvp->r_val1 = alarm_cnt;
294 	return (0);
295 }
296 
297 
298 /*
299  * Cancel BSD timers
300  */
301 
302 static int
hrt_bsd_cancel(int clock)303 hrt_bsd_cancel(int clock)
304 {
305 	struct itimerval itv;
306 	u_int which;
307 
308 	switch (clock) {
309 	case CLK_STD:
310 		which = ITIMER_REAL;
311 		break;
312 	case CLK_USERVIRT:
313 		which = ITIMER_VIRTUAL;
314 		break;
315 	case CLK_PROCVIRT:
316 		which = ITIMER_PROF;
317 		break;
318 	default:
319 		return (EINVAL);
320 	}
321 	itv.it_value.tv_sec = 0;
322 	itv.it_value.tv_usec = 0;
323 	(void) xsetitimer(which, &itv, 1);
324 	return (0);
325 }
326 
327 
328 /*
329  * Return 0 if "res" is a legal resolution. Otherwise,
330  * return an error code, ERANGE.
331  */
332 
333 static int
hrt_checkres(ulong res)334 hrt_checkres(ulong res)
335 {
336 	if (res == 0 || res > NANOSEC)
337 		return (ERANGE);
338 	return (0);
339 }
340 
341 /*
342  * Return 0 if "clock" is a valid clock. Otherwise,
343  * return an error code, EINVAL.
344  */
345 
346 static int
hrt_checkclock(register int clock)347 hrt_checkclock(register int clock)
348 {
349 	switch (clock)
350 	case CLK_STD:
351 	case CLK_USERVIRT:
352 	case CLK_PROCVIRT:
353 		return (0);
354 
355 	return (EINVAL);
356 }
357 
358 
359 /*
360  * Set the current time of day in a specified resolution into
361  * a hrtimes_t structure.
362  */
363 void
hrt_gettofd(hrtimes_t * td)364 hrt_gettofd(hrtimes_t *td)
365 {
366 	ulong new_res = td->hrt_res;
367 	timestruc_t ts;
368 
369 	gethrestime(&ts);
370 	td->hrt_secs = ts.tv_sec;
371 	td->hrt_rem = ts.tv_nsec;
372 	td->hrt_res = NANOSEC;
373 
374 	if (new_res != td->hrt_res) {
375 		td->hrt_rem /= NANOSEC / new_res;
376 		td->hrt_res = new_res;
377 	}
378 }
379 
380 
381 /*
382  * System entry point for hrtcntl, hrtalarm
383  * system calls.
384  */
385 
386 int
hrtsys(uap,rvp)387 hrtsys(uap, rvp)
388 	register struct	hrtsysa *uap;
389 	rval_t *rvp;
390 {
391 	register int	error;
392 
393 	switch (uap->opcode) {
394 	case	HRTCNTL:
395 		error = hrtcntl((struct hrtcntla *)uap, rvp);
396 		break;
397 	case	HRTALARM:
398 		error = hrtalarm((struct hrtalarma *)uap, rvp);
399 		break;
400 	default:
401 		error = EINVAL;
402 		break;
403 	}
404 
405 	return (error);
406 }
407