154359Sroberto#ifdef HAVE_CONFIG_H
2132451Sroberto# include <config.h>
354359Sroberto#endif
454359Sroberto
5106163Sroberto#ifdef MPE
6106163Sroberto/*
7106163Sroberto * MPE lacks adjtime(), so we define our own.  But note that time slewing has
8106163Sroberto * a sub-second accuracy bug documented in SR 5003462838 which prevents ntpd
9106163Sroberto * from being able to maintain clock synch.  Because of the bug, this adjtime()
10106163Sroberto * implementation as used by ntpd has a side-effect of screwing up the hardware
11106163Sroberto * PDC clock, which will need to be reset with a reboot.
12106163Sroberto *
13106163Sroberto * This problem affects all versions of MPE at the time of this writing (when
14106163Sroberto * MPE/iX 7.0 is the most current).  It only causes bad things to happen when
15106163Sroberto * doing continuous clock synchronization with ntpd; note that you CAN run ntpd
16106163Sroberto * with "disable ntp" in ntp.conf if you wish to provide a time server.
17106163Sroberto *
18106163Sroberto * The one-time clock adjustment functionality of ntpdate and ntp_timeset can
19106163Sroberto * be used without screwing up the PDC clock.
20106163Sroberto *
21106163Sroberto */
22106163Sroberto#include <time.h>
23106163Sroberto
24106163Srobertoint adjtime(struct timeval *delta, struct timeval *olddelta);
25106163Sroberto
26106163Srobertoint adjtime(struct timeval *delta, struct timeval *olddelta)
27106163Sroberto
28106163Sroberto{
29106163Sroberto/* Documented, supported MPE system intrinsics. */
30106163Sroberto
31106163Srobertoextern void GETPRIVMODE(void);
32106163Srobertoextern void GETUSERMODE(void);
33106163Sroberto
34106163Sroberto/* Undocumented, unsupported MPE internal functions. */
35106163Sroberto
36106163Srobertoextern long long current_correction_usecs(void);
37106163Srobertoextern long long get_time(void);
38106163Srobertoextern void get_time_change_info(long long *, char *, char *);
39106163Srobertoextern long long pdc_time(int *);
40106163Srobertoextern void set_time_correction(long long, int, int);
41106163Srobertoextern long long ticks_to_micro(long long);
42106163Sroberto
43106163Srobertolong long big_sec, big_usec, new_correction = 0LL;
44106163Srobertolong long prev_correction;
45106163Sroberto
46106163Srobertoif (delta != NULL) {
47106163Sroberto  /* Adjustment required.  Convert delta to 64-bit microseconds. */
48106163Sroberto  big_sec = (long)delta->tv_sec;
49106163Sroberto  big_usec = delta->tv_usec;
50106163Sroberto  new_correction = (big_sec * 1000000LL) + big_usec;
51106163Sroberto}
52106163Sroberto
53106163SrobertoGETPRIVMODE();
54106163Sroberto
55106163Sroberto/* Determine how much of a previous correction (if any) we're interrupting. */
56106163Srobertoprev_correction = current_correction_usecs();
57106163Sroberto
58106163Srobertoif (delta != NULL) {
59106163Sroberto  /* Adjustment required. */
60106163Sroberto
61106163Sroberto#if 0
62106163Sroberto  /* Speculative code disabled until bug SR 5003462838 is fixed.  This bug
63106163Sroberto     prevents accurate time slewing, and indeed renders ntpd inoperable. */
64106163Sroberto
65106163Sroberto  if (prev_correction != 0LL) {
66106163Sroberto    /* A previous adjustment did not complete.  Since the PDC UTC clock was
67106163Sroberto    immediately jumped at the start of the previous adjustment, we must
68106163Sroberto    explicitly reset it to the value of the MPE local time clock minus the
69106163Sroberto    time zone offset. */
70106163Sroberto
71106163Sroberto    char pwf_since_boot, recover_pwf_time;
72106163Sroberto    long long offset_ticks, offset_usecs, pdc_usecs_current, pdc_usecs_wanted;
73106163Sroberto    int hpe_status;
74106163Sroberto
75106163Sroberto    get_time_change_info(&offset_ticks, &pwf_since_boot, &recover_pwf_time);
76106163Sroberto    offset_usecs = ticks_to_micro(offset_ticks);
77106163Sroberto    pdc_usecs_wanted = get_time() - offset_usecs;
78106163Sroberto    pdc_usecs_current = pdc_time(&hpe_status);
79106163Sroberto    if (hpe_status == 0)
80106163Sroberto      /* Force new PDC time by starting an extra correction. */
81106163Sroberto      set_time_correction(pdc_usecs_wanted - pdc_usecs_current,0,1);
82106163Sroberto  }
83132451Sroberto#endif /* 0 */
84106163Sroberto
85106163Sroberto  /* Immediately jump the PDC time to the new value, and then initiate a
86106163Sroberto     gradual MPE time correction slew. */
87106163Sroberto  set_time_correction(new_correction,0,1);
88106163Sroberto}
89106163Sroberto
90106163SrobertoGETUSERMODE();
91106163Sroberto
92106163Srobertoif (olddelta != NULL) {
93106163Sroberto  /* Caller wants to know remaining amount of previous correction. */
94106163Sroberto  (long)olddelta->tv_sec = prev_correction / 1000000LL;
95106163Sroberto  olddelta->tv_usec = prev_correction % 1000000LL;
96106163Sroberto}
97106163Sroberto
98106163Srobertoreturn 0;
99106163Sroberto}
100106163Sroberto#endif /* MPE */
101106163Sroberto
10254359Sroberto#ifdef NEED_HPUX_ADJTIME
10354359Sroberto/*************************************************************************/
10454359Sroberto/* (c) Copyright Tai Jin, 1988.  All Rights Reserved.                    */
10554359Sroberto/*     Hewlett-Packard Laboratories.                                     */
10654359Sroberto/*                                                                       */
10754359Sroberto/* Permission is hereby granted for unlimited modification, use, and     */
10854359Sroberto/* distribution.  This software is made available with no warranty of    */
10954359Sroberto/* any kind, express or implied.  This copyright notice must remain      */
11054359Sroberto/* intact in all versions of this software.                              */
11154359Sroberto/*                                                                       */
11254359Sroberto/* The author would appreciate it if any bug fixes and enhancements were */
11354359Sroberto/* to be sent back to him for incorporation into future versions of this */
11454359Sroberto/* software.  Please send changes to tai@iag.hp.com or ken@sdd.hp.com.   */
11554359Sroberto/*************************************************************************/
11654359Sroberto
11754359Sroberto/*
11854359Sroberto * Revision history
11954359Sroberto *
12054359Sroberto * 9 Jul 94	David L. Mills, Unibergity of Delabunch
12154359Sroberto *		Implemented variable threshold to limit age of
12254359Sroberto *		corrections; reformatted code for readability.
12354359Sroberto */
12454359Sroberto
12554359Sroberto#ifndef lint
12654359Srobertostatic char RCSid[] = "adjtime.c,v 3.1 1993/07/06 01:04:42 jbj Exp";
12754359Sroberto#endif
12854359Sroberto
12954359Sroberto#include <sys/types.h>
13054359Sroberto#include <sys/ipc.h>
13154359Sroberto#include <sys/msg.h>
13254359Sroberto#include <time.h>
13354359Sroberto#include <signal.h>
13454359Sroberto#include "adjtime.h"
13554359Sroberto
13654359Sroberto#define abs(x)  ((x) < 0 ? -(x) : (x))
13754359Sroberto
13854359Sroberto/*
13954359Sroberto * The following paramters are appropriate for an NTP adjustment
14054359Sroberto * interval of one second.
14154359Sroberto */
14254359Sroberto#define ADJ_THRESH 200		/* initial threshold */
14354359Sroberto#define ADJ_DELTA 4		/* threshold decrement */
14454359Sroberto
14554359Srobertostatic long adjthresh;		/* adjustment threshold */
14654359Srobertostatic long saveup;		/* corrections accumulator */
14754359Sroberto
14854359Sroberto/*
14954359Sroberto * clear_adjtime - reset accumulator and threshold variables
15054359Sroberto */
15154359Srobertovoid
15254359Sroberto_clear_adjtime(void)
15354359Sroberto{
15454359Sroberto	saveup = 0;
15554359Sroberto	adjthresh = ADJ_THRESH;
15654359Sroberto}
15754359Sroberto
15854359Sroberto/*
15954359Sroberto * adjtime - hp-ux copout of the standard Unix adjtime() system call
16054359Sroberto */
16154359Srobertoint
16254359Srobertoadjtime(
16354359Sroberto	register struct timeval *delta,
16454359Sroberto	register struct timeval *olddelta
16554359Sroberto	)
16654359Sroberto{
16754359Sroberto	struct timeval newdelta;
16854359Sroberto
16954359Sroberto	/*
17054359Sroberto	 * Corrections greater than one second are done immediately.
17154359Sroberto	 */
17254359Sroberto	if (delta->tv_sec) {
17354359Sroberto		adjthresh = ADJ_THRESH;
17454359Sroberto		saveup = 0;
17554359Sroberto		return(_adjtime(delta, olddelta));
17654359Sroberto	}
17754359Sroberto
17854359Sroberto	/*
17954359Sroberto	 * Corrections less than one second are accumulated until
18054359Sroberto	 * tripping a threshold, which is initially set at ADJ_THESH and
18154359Sroberto	 * reduced in ADJ_DELTA steps to zero. The idea here is to
18254359Sroberto	 * introduce large corrections quickly, while making sure that
18354359Sroberto	 * small corrections are introduced without excessive delay. The
18454359Sroberto	 * idea comes from the ARPAnet routing update algorithm.
18554359Sroberto	 */
18654359Sroberto	saveup += delta->tv_usec;
18754359Sroberto	if (abs(saveup) >= adjthresh) {
18854359Sroberto		adjthresh = ADJ_THRESH;
18954359Sroberto		newdelta.tv_sec = 0;
19054359Sroberto		newdelta.tv_usec = saveup;
19154359Sroberto		saveup = 0;
19254359Sroberto		return(_adjtime(&newdelta, olddelta));
19354359Sroberto	} else {
19454359Sroberto		adjthresh -= ADJ_DELTA;
19554359Sroberto	}
19654359Sroberto
19754359Sroberto	/*
19854359Sroberto	 * While nobody uses it, return the residual before correction,
19954359Sroberto	 * as per Unix convention.
20054359Sroberto	 */
20154359Sroberto	if (olddelta)
20254359Sroberto	    olddelta->tv_sec = olddelta->tv_usec = 0;
20354359Sroberto	return(0);
20454359Sroberto}
20554359Sroberto
20654359Sroberto/*
20754359Sroberto * _adjtime - does the actual work
20854359Sroberto */
20954359Srobertoint
21054359Sroberto_adjtime(
21154359Sroberto	register struct timeval *delta,
21254359Sroberto	register struct timeval *olddelta
21354359Sroberto	)
21454359Sroberto{
21554359Sroberto	register int mqid;
21654359Sroberto	MsgBuf msg;
21754359Sroberto	register MsgBuf *msgp = &msg;
21854359Sroberto
21954359Sroberto	/*
22054359Sroberto	 * Get the key to the adjtime message queue (note that we must
22154359Sroberto	 * get it every time because the queue might have been removed
22254359Sroberto	 * and recreated)
22354359Sroberto	 */
22454359Sroberto	if ((mqid = msgget(KEY, 0)) == -1)
22554359Sroberto	    return (-1);
22654359Sroberto	msgp->msgb.mtype = CLIENT;
22754359Sroberto	msgp->msgb.tv = *delta;
22854359Sroberto	if (olddelta)
22954359Sroberto	    msgp->msgb.code = DELTA2;
23054359Sroberto	else
23154359Sroberto	    msgp->msgb.code = DELTA1;
23254359Sroberto
23354359Sroberto	/*
23454359Sroberto	 * Tickle adjtimed and snatch residual, if indicated. Lots of
23554359Sroberto	 * fanatic error checking here.
23654359Sroberto	 */
23754359Sroberto	if (msgsnd(mqid, &msgp->msgp, MSGSIZE, 0) == -1)
23854359Sroberto	    return (-1);
23954359Sroberto	if (olddelta) {
24054359Sroberto		if (msgrcv(mqid, &msgp->msgp, MSGSIZE, SERVER, 0) == -1)
24154359Sroberto		    return (-1);
24254359Sroberto		*olddelta = msgp->msgb.tv;
24354359Sroberto	}
24454359Sroberto	return (0);
24554359Sroberto}
24654359Sroberto
247132451Sroberto#else
248132451Sroberto# if NEED_QNX_ADJTIME
249132451Sroberto/*
250132451Sroberto * Emulate adjtime() using QNX ClockAdjust().
251132451Sroberto * Chris Burghart <burghart@atd.ucar.edu>, 11/2001
252182007Sroberto * Miroslaw Pabich <miroslaw_pabich@o2.pl>, 09/2005
253132451Sroberto *
254182007Sroberto * This is an implementation of adjtime() for QNX.
255182007Sroberto * ClockAdjust() is used to tweak the system clock for about
256182007Sroberto * 1 second period until the desired delta is achieved.
257182007Sroberto * Time correction slew is limited to reasonable value.
258182007Sroberto * Internal rounding and relative errors are reduced.
259132451Sroberto */
260132451Sroberto# include <sys/neutrino.h>
261132451Sroberto# include <sys/time.h>
262132451Sroberto
263132451Sroberto# include <ntp_stdlib.h>
264132451Sroberto
265182007Sroberto/*
266182007Sroberto * Time correction slew limit. QNX is a hard real-time system,
267182007Sroberto * so don't adjust system clock too fast.
268182007Sroberto */
269182007Sroberto#define CORR_SLEW_LIMIT     0.02  /* [s/s] */
270182007Sroberto
271182007Sroberto/*
272182007Sroberto * Period of system clock adjustment. It should be equal to adjtime
273182007Sroberto * execution period (1s). If slightly less than 1s (0.95-0.99), then olddelta
274182007Sroberto * residual error (introduced by execution period jitter) will be reduced.
275182007Sroberto */
276182007Sroberto#define ADJUST_PERIOD       0.97  /* [s] */
277182007Sroberto
278132451Srobertoint
279132451Srobertoadjtime (struct timeval *delta, struct timeval *olddelta)
280132451Sroberto{
281132451Sroberto    double delta_nsec;
282132451Sroberto    double delta_nsec_old;
283132451Sroberto    struct _clockadjust adj;
284132451Sroberto    struct _clockadjust oldadj;
285182007Sroberto
286132451Sroberto    /*
287132451Sroberto     * How many nanoseconds are we adjusting?
288132451Sroberto     */
289182007Sroberto    if (delta != NULL)
290182007Sroberto	delta_nsec = 1e9 * (long)delta->tv_sec + 1e3 * delta->tv_usec;
291182007Sroberto    else
292182007Sroberto	delta_nsec = 0;
293182007Sroberto
294132451Sroberto    /*
295132451Sroberto     * Build the adjust structure and call ClockAdjust()
296132451Sroberto     */
297132451Sroberto    if (delta_nsec != 0)
298132451Sroberto    {
299132451Sroberto	struct _clockperiod period;
300132451Sroberto	long count;
301132451Sroberto	long increment;
302182007Sroberto	long increment_limit;
303182007Sroberto	int isneg = 0;
304132451Sroberto
305132451Sroberto	/*
306182007Sroberto	 * Convert to absolute value for future processing
307182007Sroberto	 */
308182007Sroberto	if (delta_nsec < 0)
309182007Sroberto	{
310182007Sroberto	    isneg = 1;
311182007Sroberto	    delta_nsec = -delta_nsec;
312182007Sroberto	}
313182007Sroberto
314182007Sroberto	/*
315132451Sroberto	 * Get the current clock period (nanoseconds)
316132451Sroberto	 */
317132451Sroberto	if (ClockPeriod (CLOCK_REALTIME, 0, &period, 0) < 0)
318132451Sroberto	    return -1;
319132451Sroberto
320132451Sroberto	/*
321182007Sroberto	 * Compute count and nanoseconds increment
322132451Sroberto	 */
323182007Sroberto	count = 1e9 * ADJUST_PERIOD / period.nsec;
324182007Sroberto	increment = delta_nsec / count + .5;
325182007Sroberto	/* Reduce relative error */
326182007Sroberto	if (count > increment + 1)
327182007Sroberto	{
328182007Sroberto	    increment = 1 + (long)((delta_nsec - 1) / count);
329182007Sroberto	    count = delta_nsec / increment + .5;
330182007Sroberto	}
331132451Sroberto
332182007Sroberto	/*
333182007Sroberto	 * Limit the adjust increment to appropriate value
334182007Sroberto	 */
335182007Sroberto	increment_limit = CORR_SLEW_LIMIT * period.nsec;
336182007Sroberto	if (increment > increment_limit)
337182007Sroberto	{
338182007Sroberto	    increment = increment_limit;
339182007Sroberto	    count = delta_nsec / increment + .5;
340182007Sroberto	    /* Reduce relative error */
341182007Sroberto	    if (increment > count + 1)
342182007Sroberto	    {
343182007Sroberto		count =  1 + (long)((delta_nsec - 1) / increment);
344182007Sroberto		increment = delta_nsec / count + .5;
345182007Sroberto	    }
346182007Sroberto	}
347182007Sroberto
348182007Sroberto	adj.tick_nsec_inc = isneg ? -increment : increment;
349132451Sroberto	adj.tick_count = count;
350132451Sroberto    }
351132451Sroberto    else
352132451Sroberto    {
353132451Sroberto	adj.tick_nsec_inc = 0;
354132451Sroberto	adj.tick_count = 0;
355132451Sroberto    }
356132451Sroberto
357132451Sroberto    if (ClockAdjust (CLOCK_REALTIME, &adj, &oldadj) < 0)
358132451Sroberto	return -1;
359132451Sroberto
360132451Sroberto    /*
361132451Sroberto     * Build olddelta
362132451Sroberto     */
363182007Sroberto    delta_nsec_old = (double)oldadj.tick_count * oldadj.tick_nsec_inc;
364182007Sroberto    if (olddelta != NULL)
365182007Sroberto    {
366182007Sroberto	if (delta_nsec_old != 0)
367182007Sroberto	{
368182007Sroberto	    /* Reduce rounding error */
369182007Sroberto	    delta_nsec_old += (delta_nsec_old < 0) ? -500 : 500;
370182007Sroberto	    olddelta->tv_sec = delta_nsec_old / 1e9;
371182007Sroberto	    olddelta->tv_usec = (long)(delta_nsec_old - 1e9
372182007Sroberto				 * (long)olddelta->tv_sec) / 1000;
373182007Sroberto	}
374182007Sroberto	else
375182007Sroberto	{
376182007Sroberto	    olddelta->tv_sec = 0;
377182007Sroberto	    olddelta->tv_usec = 0;
378182007Sroberto	}
379182007Sroberto    }
380182007Sroberto
381132451Sroberto    return 0;
382132451Sroberto}
383132451Sroberto# else /* no special adjtime() needed */
38454359Srobertoint adjtime_bs;
385132451Sroberto# endif
386132451Sroberto#endif
387