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) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23/*	  All Rights Reserved  	*/
24
25
26/*
27 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28 * Use is subject to license terms.
29 */
30
31#pragma ident	"%Z%%M%	%I%	%E% SMI"
32
33#include <stdio.h>
34#include <stdarg.h>
35#include <stdlib.h>
36#include <unistd.h>
37#include <sys/types.h>
38#include <sys/time.h>
39#include <limits.h>
40
41#include "dispadmin.h"
42
43
44/*
45 * Utility functions for dispadmin command.
46 */
47
48
49void
50fatalerr(const char *format, ...)
51{
52	va_list ap;
53
54	(void) va_start(ap, format);
55	(void) vfprintf(stderr, format, ap);
56	va_end(ap);
57	exit(1);
58}
59
60
61/*
62 * hrtconvert() returns the interval specified by htp as a single
63 * value in resolution htp->hrt_res.  Returns -1 on overflow.
64 */
65long
66hrtconvert(hrtimer_t *htp)
67{
68	long	sum;
69	long	product;
70
71	product = htp->hrt_secs * htp->hrt_res;
72
73	if (product / htp->hrt_res == htp->hrt_secs) {
74		sum = product + htp->hrt_rem;
75		if (sum - htp->hrt_rem == product) {
76			return (sum);
77		}
78	}
79	return (-1);
80}
81
82/*
83 * The following routine was removed from libc (libc/port/gen/hrtnewres.c).
84 * It has also been added to priocntl, so if you fix it here, you should
85 * also probably fix it there. In the long term, this should be recoded to
86 * not be hrt'ish.
87 */
88
89/*
90 *	Convert interval expressed in htp->hrt_res to new_res.
91 *
92 *	Calculate: (interval * new_res) / htp->hrt_res  rounding off as
93 *		specified by round.
94 *
95 *	Note:	All args are assumed to be positive.  If
96 *	the last divide results in something bigger than
97 *	a long, then -1 is returned instead.
98 */
99
100int
101_hrtnewres(hrtimer_t *htp, ulong_t new_res, long round)
102{
103	long		interval;
104	longlong_t	dint;
105	longlong_t	dto_res;
106	longlong_t	drem;
107	longlong_t	dfrom_res;
108	longlong_t	prod;
109	longlong_t	quot;
110	long		numerator;
111	long		result;
112	ulong_t		modulus;
113	ulong_t		twomodulus;
114	long		temp;
115
116	if (htp->hrt_res == 0 || new_res == 0 ||
117	    new_res > NANOSEC || htp->hrt_rem < 0)
118		return (-1);
119
120	if (htp->hrt_rem >= htp->hrt_res) {
121		htp->hrt_secs += htp->hrt_rem / htp->hrt_res;
122		htp->hrt_rem = htp->hrt_rem % htp->hrt_res;
123	}
124
125	interval = htp->hrt_rem;
126	if (interval == 0) {
127		htp->hrt_res = new_res;
128		return (0);
129	}
130
131	/*
132	 *	Try to do the calculations in single precision first
133	 *	(for speed).  If they overflow, use double precision.
134	 *	What we want to compute is:
135	 *
136	 *		(interval * new_res) / hrt->hrt_res
137	 */
138
139	numerator = interval * new_res;
140
141	if (numerator / new_res  ==  interval) {
142
143		/*
144		 *	The above multiply didn't give overflow since
145		 *	the division got back the original number.  Go
146		 *	ahead and compute the result.
147		 */
148
149		result = numerator / htp->hrt_res;
150
151		/*
152		 *	For HRT_RND, compute the value of:
153		 *
154		 *		(interval * new_res) % htp->hrt_res
155		 *
156		 *	If it is greater than half of the htp->hrt_res,
157		 *	then rounding increases the result by 1.
158		 *
159		 *	For HRT_RNDUP, we increase the result by 1 if:
160		 *
161		 *		result * htp->hrt_res != numerator
162		 *
163		 *	because this tells us we truncated when calculating
164		 *	result above.
165		 *
166		 *	We also check for overflow when incrementing result
167		 *	although this is extremely rare.
168		 */
169
170		if (round == HRT_RND) {
171			modulus = numerator - result * htp->hrt_res;
172			if ((twomodulus = 2 * modulus) / 2 == modulus) {
173
174				/*
175				 * No overflow (if we overflow in calculation
176				 * of twomodulus we fall through and use
177				 * double precision).
178				 */
179				if (twomodulus >= htp->hrt_res) {
180					temp = result + 1;
181					if (temp - 1 == result)
182						result++;
183					else
184						return (-1);
185				}
186				htp->hrt_res = new_res;
187				htp->hrt_rem = result;
188				return (0);
189			}
190		} else if (round == HRT_RNDUP) {
191			if (result * htp->hrt_res != numerator) {
192				temp = result + 1;
193				if (temp - 1 == result)
194					result++;
195				else
196					return (-1);
197			}
198			htp->hrt_res = new_res;
199			htp->hrt_rem = result;
200			return (0);
201		} else {	/* round == HRT_TRUNC */
202			htp->hrt_res = new_res;
203			htp->hrt_rem = result;
204			return (0);
205		}
206	}
207
208	/*
209	 *	We would get overflow doing the calculation is
210	 *	single precision so do it the slow but careful way.
211	 *
212	 *	Compute the interval times the resolution we are
213	 *	going to.
214	 */
215
216	dint = interval;
217	dto_res = new_res;
218	prod = dint * dto_res;
219
220	/*
221	 *	For HRT_RND the result will be equal to:
222	 *
223	 *		((interval * new_res) + htp->hrt_res / 2) / htp->hrt_res
224	 *
225	 *	and for HRT_RNDUP we use:
226	 *
227	 *		((interval * new_res) + htp->hrt_res - 1) / htp->hrt_res
228	 *
229	 * 	This is a different but equivalent way of rounding.
230	 */
231
232	if (round == HRT_RND) {
233		drem = htp->hrt_res / 2;
234		prod = prod + drem;
235	} else if (round == HRT_RNDUP) {
236		drem = htp->hrt_res - 1;
237		prod = prod + drem;
238	}
239
240	dfrom_res = htp->hrt_res;
241	quot = prod / dfrom_res;
242
243	/*
244	 *	If the quotient won't fit in a long, then we have
245	 *	overflow.  Otherwise, return the result.
246	 */
247
248	if (quot > UINT_MAX) {
249		return (-1);
250	} else {
251		htp->hrt_res = new_res;
252		htp->hrt_rem = (int)quot;
253		return (0);
254	}
255}
256