154359Sroberto/*
254359Sroberto * ntp_loopfilter.c - implements the NTP loop filter algorithm
354359Sroberto *
4132451Sroberto * ATTENTION: Get approval from Dave Mills on all changes to this file!
5132451Sroberto *
654359Sroberto */
754359Sroberto#ifdef HAVE_CONFIG_H
854359Sroberto# include <config.h>
954359Sroberto#endif
1054359Sroberto
11290000Sglebius#ifdef USE_SNPRINTB
12290000Sglebius# include <util.h>
13290000Sglebius#endif
1482498Sroberto#include "ntpd.h"
1582498Sroberto#include "ntp_io.h"
1682498Sroberto#include "ntp_unixtime.h"
1782498Sroberto#include "ntp_stdlib.h"
1882498Sroberto
19290000Sglebius#include <limits.h>
2054359Sroberto#include <stdio.h>
2154359Sroberto#include <ctype.h>
2254359Sroberto
2354359Sroberto#include <signal.h>
2454359Sroberto#include <setjmp.h>
2554359Sroberto
2654359Sroberto#ifdef KERNEL_PLL
2754359Sroberto#include "ntp_syscall.h"
2854359Sroberto#endif /* KERNEL_PLL */
2954359Sroberto
3054359Sroberto/*
3154359Sroberto * This is an implementation of the clock discipline algorithm described
3254359Sroberto * in UDel TR 97-4-3, as amended. It operates as an adaptive parameter,
3354359Sroberto * hybrid phase/frequency-lock loop. A number of sanity checks are
3454359Sroberto * included to protect against timewarps, timespikes and general mayhem.
3554359Sroberto * All units are in s and s/s, unless noted otherwise.
3654359Sroberto */
37132451Sroberto#define CLOCK_MAX	.128	/* default step threshold (s) */
38290000Sglebius#define CLOCK_MINSTEP	300.	/* default stepout threshold (s) */
39132451Sroberto#define CLOCK_PANIC	1000.	/* default panic threshold (s) */
4082498Sroberto#define	CLOCK_PHI	15e-6	/* max frequency error (s/s) */
41182007Sroberto#define CLOCK_PLL	16.	/* PLL loop gain (log2) */
42182007Sroberto#define CLOCK_AVG	8.	/* parameter averaging constant */
43290000Sglebius#define CLOCK_FLL	.25	/* FLL loop gain */
44290000Sglebius#define	CLOCK_FLOOR	.0005	/* startup offset floor (s) */
45290000Sglebius#define	CLOCK_ALLAN	11	/* Allan intercept (log2 s) */
4654359Sroberto#define CLOCK_LIMIT	30	/* poll-adjust threshold */
4754359Sroberto#define CLOCK_PGATE	4.	/* poll-adjust gate */
48106163Sroberto#define PPS_MAXAGE	120	/* kernel pps signal timeout (s) */
49290000Sglebius#define	FREQTOD(x)	((x) / 65536e6) /* NTP to double */
50290000Sglebius#define	DTOFREQ(x)	((int32)((x) * 65536e6)) /* double to NTP */
5154359Sroberto
5254359Sroberto/*
5354359Sroberto * Clock discipline state machine. This is used to control the
5454359Sroberto * synchronization behavior during initialization and following a
5582498Sroberto * timewarp.
5682498Sroberto *
57182007Sroberto *	State	< step		> step		Comments
58290000Sglebius *	========================================================
59290000Sglebius *	NSET	FREQ		step, FREQ	freq not set
6082498Sroberto *
61290000Sglebius *	FSET	SYNC		step, SYNC	freq set
6282498Sroberto *
63290000Sglebius *	FREQ	if (mu < 900)	if (mu < 900)	set freq direct
64182007Sroberto *		    ignore	    ignore
65182007Sroberto *		else		else
66182007Sroberto *		    freq, SYNC	    freq, step, SYNC
6782498Sroberto *
68290000Sglebius *	SYNC	SYNC		SPIK, ignore	adjust phase/freq
69290000Sglebius *
70290000Sglebius *	SPIK	SYNC		if (mu < 900)	adjust phase/freq
71182007Sroberto *				    ignore
72290000Sglebius *				step, SYNC
7354359Sroberto */
7454359Sroberto/*
7554359Sroberto * Kernel PLL/PPS state machine. This is used with the kernel PLL
76290000Sglebius * modifications described in the documentation.
7754359Sroberto *
7854359Sroberto * If kernel support for the ntp_adjtime() system call is available, the
7954359Sroberto * ntp_control flag is set. The ntp_enable and kern_enable flags can be
8054359Sroberto * set at configuration time or run time using ntpdc. If ntp_enable is
81182007Sroberto * false, the discipline loop is unlocked and no corrections of any kind
8254359Sroberto * are made. If both ntp_control and kern_enable are set, the kernel
8354359Sroberto * support is used as described above; if false, the kernel is bypassed
84182007Sroberto * entirely and the daemon discipline used instead.
8554359Sroberto *
86182007Sroberto * There have been three versions of the kernel discipline code. The
87182007Sroberto * first (microkernel) now in Solaris discipilnes the microseconds. The
88182007Sroberto * second and third (nanokernel) disciplines the clock in nanoseconds.
89182007Sroberto * These versions are identifed if the symbol STA_PLL is present in the
90182007Sroberto * header file /usr/include/sys/timex.h. The third and current version
91182007Sroberto * includes TAI offset and is identified by the symbol NTP_API with
92182007Sroberto * value 4.
93182007Sroberto *
94290000Sglebius * Each PPS time/frequency discipline can be enabled by the atom driver
95290000Sglebius * or another driver. If enabled, the STA_PPSTIME and STA_FREQ bits are
96290000Sglebius * set in the kernel status word; otherwise, these bits are cleared.
97290000Sglebius * These bits are also cleard if the kernel reports an error.
9854359Sroberto *
9954359Sroberto * If an external clock is present, the clock driver sets STA_CLK in the
10054359Sroberto * status word. When the local clock driver sees this bit, it updates
10154359Sroberto * via this routine, which then calls ntp_adjtime() with the STA_PLL bit
10254359Sroberto * set to zero, in which case the system clock is not adjusted. This is
10354359Sroberto * also a signal for the external clock driver to discipline the system
104290000Sglebius * clock. Unless specified otherwise, all times are in seconds.
10554359Sroberto */
10654359Sroberto/*
10782498Sroberto * Program variables that can be tinkered.
10882498Sroberto */
109290000Sglebiusdouble	clock_max_back = CLOCK_MAX;	/* step threshold */
110290000Sglebiusdouble	clock_max_fwd =  CLOCK_MAX;	/* step threshold */
111290000Sglebiusdouble	clock_minstep = CLOCK_MINSTEP; /* stepout threshold */
112290000Sglebiusdouble	clock_panic = CLOCK_PANIC; /* panic threshold */
11382498Srobertodouble	clock_phi = CLOCK_PHI;	/* dispersion rate (s/s) */
114290000Sglebiusu_char	allan_xpt = CLOCK_ALLAN; /* Allan intercept (log2 s) */
11582498Sroberto
11682498Sroberto/*
11754359Sroberto * Program variables
11854359Sroberto */
119290000Sglebiusstatic double clock_offset;	/* offset */
120290000Sglebiusdouble	clock_jitter;		/* offset jitter */
121182007Srobertodouble	drift_comp;		/* frequency (s/s) */
122290000Sglebiusstatic double init_drift_comp; /* initial frequency (PPM) */
123182007Srobertodouble	clock_stability;	/* frequency stability (wander) (s/s) */
124290000Sglebiusdouble	clock_codec;		/* audio codec frequency (samples/s) */
125290000Sglebiusstatic u_long clock_epoch;	/* last update */
126290000Sglebiusu_int	sys_tai;		/* TAI offset from UTC */
127290000Sglebiusstatic int loop_started;	/* TRUE after LOOP_DRIFTINIT */
128290000Sglebiusstatic void rstclock (int, double); /* transition function */
129290000Sglebiusstatic double direct_freq(double); /* direct set frequency */
130290000Sglebiusstatic void set_freq(double);	/* set frequency */
131290000Sglebius#ifndef PATH_MAX
132290000Sglebius# define PATH_MAX MAX_PATH
133290000Sglebius#endif
134290000Sglebiusstatic char relative_path[PATH_MAX + 1]; /* relative path per recursive make */
135290000Sglebiusstatic char *this_file = NULL;
13654359Sroberto
13754359Sroberto#ifdef KERNEL_PLL
138290000Sglebiusstatic struct timex ntv;	/* ntp_adjtime() parameters */
139290000Sglebiusint	pll_status;		/* last kernel status bits */
140290000Sglebius#if defined(STA_NANO) && NTP_API == 4
141290000Sglebiusstatic u_int loop_tai;		/* last TAI offset */
142290000Sglebius#endif /* STA_NANO */
143290000Sglebiusstatic	void	start_kern_loop(void);
144290000Sglebiusstatic	void	stop_kern_loop(void);
14554359Sroberto#endif /* KERNEL_PLL */
14654359Sroberto
14754359Sroberto/*
14854359Sroberto * Clock state machine control flags
14954359Sroberto */
150290000Sglebiusint	ntp_enable = TRUE;	/* clock discipline enabled */
15154359Srobertoint	pll_control;		/* kernel support available */
152290000Sglebiusint	kern_enable = TRUE;	/* kernel support enabled */
153290000Sglebiusint	hardpps_enable;		/* kernel PPS discipline enabled */
15454359Srobertoint	ext_enable;		/* external clock enabled */
15582498Srobertoint	pps_stratum;		/* pps stratum */
156290000Sglebiusint	kernel_status;		/* from ntp_adjtime */
157290000Sglebiusint	force_step_once = FALSE; /* always step time once at startup (-G) */
158290000Sglebiusint	mode_ntpdate = FALSE;	/* exit on first clock set (-q) */
159290000Sglebiusint	freq_cnt;		/* initial frequency clamp */
160290000Sglebiusint	freq_set;		/* initial set frequency switch */
16154359Sroberto
16254359Sroberto/*
16354359Sroberto * Clock state machine variables
16454359Sroberto */
165290000Sglebiusint	state = 0;		/* clock discipline state */
166290000Sglebiusu_char	sys_poll;		/* time constant/poll (log2 s) */
167182007Srobertoint	tc_counter;		/* jiggle counter */
168182007Srobertodouble	last_offset;		/* last offset (s) */
16954359Sroberto
17082498Sroberto/*
17182498Sroberto * Huff-n'-puff filter variables
17282498Sroberto */
17382498Srobertostatic double *sys_huffpuff;	/* huff-n'-puff filter */
17482498Srobertostatic int sys_hufflen;		/* huff-n'-puff filter stages */
17582498Srobertostatic int sys_huffptr;		/* huff-n'-puff filter pointer */
17682498Srobertostatic double sys_mindly;	/* huff-n'-puff filter min delay */
17782498Sroberto
17854359Sroberto#if defined(KERNEL_PLL)
17954359Sroberto/* Emacs cc-mode goes nuts if we split the next line... */
18054359Sroberto#define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \
18154359Sroberto    MOD_STATUS | MOD_TIMECONST)
18282498Sroberto#ifdef SIGSYS
183290000Sglebiusstatic void pll_trap (int);	/* configuration trap */
18454359Srobertostatic struct sigaction sigsys;	/* current sigaction status */
18554359Srobertostatic struct sigaction newsigsys; /* new sigaction status */
18654359Srobertostatic sigjmp_buf env;		/* environment var. for pll_trap() */
18754359Sroberto#endif /* SIGSYS */
18854359Sroberto#endif /* KERNEL_PLL */
18954359Sroberto
190290000Sglebiusstatic void
191290000Sglebiussync_status(const char *what, int ostatus, int nstatus)
192290000Sglebius{
193290000Sglebius	char obuf[256], nbuf[256], tbuf[1024];
194290000Sglebius#if defined(USE_SNPRINTB) && defined (STA_FMT)
195290000Sglebius	snprintb(obuf, sizeof(obuf), STA_FMT, ostatus);
196290000Sglebius	snprintb(nbuf, sizeof(nbuf), STA_FMT, nstatus);
197290000Sglebius#else
198290000Sglebius	snprintf(obuf, sizeof(obuf), "%04x", ostatus);
199290000Sglebius	snprintf(nbuf, sizeof(nbuf), "%04x", nstatus);
200290000Sglebius#endif
201290000Sglebius	snprintf(tbuf, sizeof(tbuf), "%s status: %s -> %s", what, obuf, nbuf);
202290000Sglebius	report_event(EVNT_KERN, NULL, tbuf);
203290000Sglebius}
204290000Sglebius
20554359Sroberto/*
206290000Sglebius * file_name - return pointer to non-relative portion of this C file pathname
207290000Sglebius */
208290000Sglebiusstatic char *file_name(void)
209290000Sglebius{
210290000Sglebius	if (this_file == NULL) {
211290000Sglebius	    (void)strncpy(relative_path, __FILE__, PATH_MAX);
212290000Sglebius	    for (this_file=relative_path;
213290000Sglebius		*this_file && ! isalnum((unsigned char)*this_file);
214290000Sglebius		this_file++) ;
215290000Sglebius	}
216290000Sglebius	return this_file;
217290000Sglebius}
218290000Sglebius
219290000Sglebius/*
22054359Sroberto * init_loopfilter - initialize loop filter data
22154359Sroberto */
22254359Srobertovoid
22354359Srobertoinit_loopfilter(void)
22454359Sroberto{
22554359Sroberto	/*
226290000Sglebius	 * Initialize state variables.
22754359Sroberto	 */
228290000Sglebius	sys_poll = ntp_minpoll;
229182007Sroberto	clock_jitter = LOGTOD(sys_precision);
230290000Sglebius	freq_cnt = (int)clock_minstep;
23154359Sroberto}
23254359Sroberto
233290000Sglebius#ifdef KERNEL_PLL
23454359Sroberto/*
235290000Sglebius * ntp_adjtime_error_handler - process errors from ntp_adjtime
236290000Sglebius */
237290000Sglebiusstatic void
238290000Sglebiusntp_adjtime_error_handler(
239290000Sglebius	const char *caller,	/* name of calling function */
240290000Sglebius	struct timex *ptimex,	/* pointer to struct timex */
241290000Sglebius	int ret,		/* return value from ntp_adjtime */
242290000Sglebius	int saved_errno,	/* value of errno when ntp_adjtime returned */
243290000Sglebius	int pps_call,		/* ntp_adjtime call was PPS-related */
244290000Sglebius	int tai_call,		/* ntp_adjtime call was TAI-related */
245290000Sglebius	int line		/* line number of ntp_adjtime call */
246290000Sglebius	)
247290000Sglebius{
248290000Sglebius	char des[1024] = "";	/* Decoded Error Status */
249290000Sglebius
250290000Sglebius	switch (ret) {
251290000Sglebius	    case -1:
252290000Sglebius		switch (saved_errno) {
253290000Sglebius		    case EFAULT:
254290000Sglebius			msyslog(LOG_ERR, "%s: %s line %d: invalid struct timex pointer: 0x%lx",
255290000Sglebius			    caller, file_name(), line,
256290000Sglebius			    (long)((void *)ptimex)
257290000Sglebius			);
258290000Sglebius		    break;
259290000Sglebius		    case EINVAL:
260290000Sglebius			msyslog(LOG_ERR, "%s: %s line %d: invalid struct timex \"constant\" element value: %ld",
261290000Sglebius			    caller, file_name(), line,
262290000Sglebius			    (long)(ptimex->constant)
263290000Sglebius			);
264290000Sglebius		    break;
265290000Sglebius		    case EPERM:
266290000Sglebius			if (tai_call) {
267290000Sglebius			    errno = saved_errno;
268290000Sglebius			    msyslog(LOG_ERR,
269290000Sglebius				"%s: ntp_adjtime(TAI) failed: %m",
270290000Sglebius				caller);
271290000Sglebius			}
272290000Sglebius			errno = saved_errno;
273290000Sglebius			msyslog(LOG_ERR, "%s: %s line %d: ntp_adjtime: %m",
274290000Sglebius			    caller, file_name(), line
275290000Sglebius			);
276290000Sglebius		    break;
277290000Sglebius		    default:
278290000Sglebius			msyslog(LOG_NOTICE, "%s: %s line %d: unhandled errno value %d after failed ntp_adjtime call",
279290000Sglebius			    caller, file_name(), line,
280290000Sglebius			    saved_errno
281290000Sglebius			);
282290000Sglebius		    break;
283290000Sglebius		}
284290000Sglebius	    break;
285290000Sglebius#ifdef TIME_OK
286290000Sglebius	    case TIME_OK: /* 0: synchronized, no leap second warning */
287290000Sglebius		/* msyslog(LOG_INFO, "kernel reports time is synchronized normally"); */
288290000Sglebius	    break;
289290000Sglebius#else
290290000Sglebius# warning TIME_OK is not defined
291290000Sglebius#endif
292290000Sglebius#ifdef TIME_INS
293290000Sglebius	    case TIME_INS: /* 1: positive leap second warning */
294290000Sglebius		msyslog(LOG_INFO, "kernel reports leap second insertion scheduled");
295290000Sglebius	    break;
296290000Sglebius#else
297290000Sglebius# warning TIME_INS is not defined
298290000Sglebius#endif
299290000Sglebius#ifdef TIME_DEL
300290000Sglebius	    case TIME_DEL: /* 2: negative leap second warning */
301290000Sglebius		msyslog(LOG_INFO, "kernel reports leap second deletion scheduled");
302290000Sglebius	    break;
303290000Sglebius#else
304290000Sglebius# warning TIME_DEL is not defined
305290000Sglebius#endif
306290000Sglebius#ifdef TIME_OOP
307290000Sglebius	    case TIME_OOP: /* 3: leap second in progress */
308290000Sglebius		msyslog(LOG_INFO, "kernel reports leap second in progress");
309290000Sglebius	    break;
310290000Sglebius#else
311290000Sglebius# warning TIME_OOP is not defined
312290000Sglebius#endif
313290000Sglebius#ifdef TIME_WAIT
314290000Sglebius	    case TIME_WAIT: /* 4: leap second has occured */
315290000Sglebius		msyslog(LOG_INFO, "kernel reports leap second has occurred");
316290000Sglebius	    break;
317290000Sglebius#else
318290000Sglebius# warning TIME_WAIT is not defined
319290000Sglebius#endif
320290000Sglebius#ifdef TIME_ERROR
321290000Sglebius#if 0
322290000Sglebius
323290000Sglebiusfrom the reference implementation of ntp_gettime():
324290000Sglebius
325290000Sglebius		// Hardware or software error
326290000Sglebius        if ((time_status & (STA_UNSYNC | STA_CLOCKERR))
327290000Sglebius
328290000Sglebius	/*
329290000Sglebius         * PPS signal lost when either time or frequency synchronization
330290000Sglebius         * requested
331290000Sglebius         */
332290000Sglebius	|| (time_status & (STA_PPSFREQ | STA_PPSTIME)
333290000Sglebius	    && !(time_status & STA_PPSSIGNAL))
334290000Sglebius
335290000Sglebius        /*
336290000Sglebius         * PPS jitter exceeded when time synchronization requested
337290000Sglebius         */
338290000Sglebius	|| (time_status & STA_PPSTIME &&
339290000Sglebius            time_status & STA_PPSJITTER)
340290000Sglebius
341290000Sglebius        /*
342290000Sglebius         * PPS wander exceeded or calibration error when frequency
343290000Sglebius         * synchronization requested
344290000Sglebius         */
345290000Sglebius	|| (time_status & STA_PPSFREQ &&
346290000Sglebius            time_status & (STA_PPSWANDER | STA_PPSERROR)))
347290000Sglebius                return (TIME_ERROR);
348290000Sglebius
349290000Sglebiusor, from ntp_adjtime():
350290000Sglebius
351290000Sglebius	if (  (time_status & (STA_UNSYNC | STA_CLOCKERR))
352290000Sglebius	    || (time_status & (STA_PPSFREQ | STA_PPSTIME)
353290000Sglebius		&& !(time_status & STA_PPSSIGNAL))
354290000Sglebius	    || (time_status & STA_PPSTIME
355290000Sglebius		&& time_status & STA_PPSJITTER)
356290000Sglebius	    || (time_status & STA_PPSFREQ
357290000Sglebius		&& time_status & (STA_PPSWANDER | STA_PPSERROR))
358290000Sglebius	   )
359290000Sglebius		return (TIME_ERROR);
360290000Sglebius#endif
361290000Sglebius
362290000Sglebius	    case TIME_ERROR: /* 5: unsynchronized, or loss of synchronization */
363290000Sglebius				/* error (see status word) */
364290000Sglebius
365290000Sglebius		if (ptimex->status & STA_UNSYNC)
366290000Sglebius			snprintf(des, sizeof(des), "%s%sClock Unsynchronized",
367290000Sglebius				des, (*des) ? "; " : "");
368290000Sglebius
369290000Sglebius		if (ptimex->status & STA_CLOCKERR)
370290000Sglebius			snprintf(des, sizeof(des), "%s%sClock Error",
371290000Sglebius				des, (*des) ? "; " : "");
372290000Sglebius
373290000Sglebius		if (!(ptimex->status & STA_PPSSIGNAL)
374290000Sglebius		    && ptimex->status & STA_PPSFREQ)
375290000Sglebius			snprintf(des, sizeof(des), "%s%sPPS Frequency Sync wanted but no PPS",
376290000Sglebius				des, (*des) ? "; " : "");
377290000Sglebius
378290000Sglebius		if (!(ptimex->status & STA_PPSSIGNAL)
379290000Sglebius		    && ptimex->status & STA_PPSTIME)
380290000Sglebius			snprintf(des, sizeof(des), "%s%sPPS Time Sync wanted but no PPS signal",
381290000Sglebius				des, (*des) ? "; " : "");
382290000Sglebius
383290000Sglebius		if (   ptimex->status & STA_PPSTIME
384290000Sglebius		    && ptimex->status & STA_PPSJITTER)
385290000Sglebius			snprintf(des, sizeof(des), "%s%sPPS Time Sync wanted but PPS Jitter exceeded",
386290000Sglebius				des, (*des) ? "; " : "");
387290000Sglebius
388290000Sglebius		if (   ptimex->status & STA_PPSFREQ
389290000Sglebius		    && ptimex->status & STA_PPSWANDER)
390290000Sglebius			snprintf(des, sizeof(des), "%s%sPPS Frequency Sync wanted but PPS Wander exceeded",
391290000Sglebius				des, (*des) ? "; " : "");
392290000Sglebius
393290000Sglebius		if (   ptimex->status & STA_PPSFREQ
394290000Sglebius		    && ptimex->status & STA_PPSERROR)
395290000Sglebius			snprintf(des, sizeof(des), "%s%sPPS Frequency Sync wanted but Calibration error detected",
396290000Sglebius				des, (*des) ? "; " : "");
397290000Sglebius
398290000Sglebius		if (pps_call && !(ptimex->status & STA_PPSSIGNAL))
399290000Sglebius			report_event(EVNT_KERN, NULL,
400290000Sglebius			    "no PPS signal");
401290000Sglebius		DPRINTF(1, ("kernel loop status %#x (%s)\n",
402290000Sglebius			ptimex->status, des));
403290000Sglebius		/*
404290000Sglebius		 * This code may be returned when ntp_adjtime() has just
405290000Sglebius		 * been called for the first time, quite a while after
406290000Sglebius		 * startup, when ntpd just starts to discipline the kernel
407290000Sglebius		 * time. In this case the occurrence of this message
408290000Sglebius		 * can be pretty confusing.
409290000Sglebius		 *
410290000Sglebius		 * HMS: How about a message when we begin kernel processing:
411290000Sglebius		 *    Determining kernel clock state...
412290000Sglebius		 * so an initial TIME_ERROR message is less confising,
413290000Sglebius		 * or skipping the first message (ugh),
414290000Sglebius		 * or ???
415290000Sglebius		 * msyslog(LOG_INFO, "kernel reports time synchronization lost");
416290000Sglebius		 */
417290000Sglebius		msyslog(LOG_INFO, "kernel reports TIME_ERROR: %#x: %s",
418290000Sglebius			ptimex->status, des);
419290000Sglebius	    break;
420290000Sglebius#else
421290000Sglebius# warning TIME_ERROR is not defined
422290000Sglebius#endif
423290000Sglebius	    default:
424290000Sglebius		msyslog(LOG_NOTICE, "%s: %s line %d: unhandled return value %d from ntp_adjtime() in %s at line %d",
425290000Sglebius		    caller, file_name(), line,
426290000Sglebius		    ret,
427290000Sglebius		    __func__, __LINE__
428290000Sglebius		);
429290000Sglebius	    break;
430290000Sglebius	}
431290000Sglebius	return;
432290000Sglebius}
433290000Sglebius#endif
434290000Sglebius
435290000Sglebius/*
436182007Sroberto * local_clock - the NTP logical clock loop filter.
437132451Sroberto *
438182007Sroberto * Return codes:
439182007Sroberto * -1	update ignored: exceeds panic threshold
440182007Sroberto * 0	update ignored: popcorn or exceeds step threshold
441182007Sroberto * 1	clock was slewed
442182007Sroberto * 2	clock was stepped
443182007Sroberto *
444132451Sroberto * LOCKCLOCK: The only thing this routine does is set the
445290000Sglebius * sys_rootdisp variable equal to the peer dispersion.
44654359Sroberto */
44754359Srobertoint
44854359Srobertolocal_clock(
449182007Sroberto	struct	peer *peer,	/* synch source peer structure */
450182007Sroberto	double	fp_offset	/* clock offset (s) */
45154359Sroberto	)
45254359Sroberto{
453182007Sroberto	int	rval;		/* return code */
454290000Sglebius	int	osys_poll;	/* old system poll */
455290000Sglebius	int	ntp_adj_ret;	/* returned by ntp_adjtime */
456290000Sglebius	double	mu;		/* interval since last update */
457290000Sglebius	double	clock_frequency; /* clock frequency */
458182007Sroberto	double	dtemp, etemp;	/* double temps */
459290000Sglebius	char	tbuf[80];	/* report buffer */
46054359Sroberto
461293894Sglebius	(void)ntp_adj_ret; /* not always used below... */
46282498Sroberto	/*
463182007Sroberto	 * If the loop is opened or the NIST LOCKCLOCK is in use,
464182007Sroberto	 * monitor and record the offsets anyway in order to determine
465182007Sroberto	 * the open-loop response and then go home.
46682498Sroberto	 */
467293894Sglebius#ifndef LOCKCLOCK
468293894Sglebius	if (!ntp_enable)
469293894Sglebius#endif /* not LOCKCLOCK */
470290000Sglebius	{
471182007Sroberto		record_loop_stats(fp_offset, drift_comp, clock_jitter,
47282498Sroberto		    clock_stability, sys_poll);
47382498Sroberto		return (0);
47482498Sroberto	}
47554359Sroberto
476290000Sglebius#ifndef LOCKCLOCK
47754359Sroberto	/*
47882498Sroberto	 * If the clock is way off, panic is declared. The clock_panic
47982498Sroberto	 * defaults to 1000 s; if set to zero, the panic will never
48082498Sroberto	 * occur. The allow_panic defaults to FALSE, so the first panic
48182498Sroberto	 * will exit. It can be set TRUE by a command line option, in
48282498Sroberto	 * which case the clock will be set anyway and time marches on.
483182007Sroberto	 * But, allow_panic will be set FALSE when the update is less
484182007Sroberto	 * than the step threshold; so, subsequent panics will exit.
48554359Sroberto	 */
48682498Sroberto	if (fabs(fp_offset) > clock_panic && clock_panic > 0 &&
48782498Sroberto	    !allow_panic) {
488290000Sglebius		snprintf(tbuf, sizeof(tbuf),
489290000Sglebius		    "%+.0f s; set clock manually within %.0f s.",
49082498Sroberto		    fp_offset, clock_panic);
491290000Sglebius		report_event(EVNT_SYSFAULT, NULL, tbuf);
49254359Sroberto		return (-1);
49354359Sroberto	}
49482498Sroberto
495293894Sglebius	allow_panic = FALSE;
496293894Sglebius
49754359Sroberto	/*
498290000Sglebius	 * This section simulates ntpdate. If the offset exceeds the
499290000Sglebius	 * step threshold (128 ms), step the clock to that time and
500290000Sglebius	 * exit. Otherwise, slew the clock to that time and exit. Note
501290000Sglebius	 * that the slew will persist and eventually complete beyond the
502290000Sglebius	 * life of this program. Note that while ntpdate is active, the
503290000Sglebius	 * terminal does not detach, so the termination message prints
504290000Sglebius	 * directly to the terminal.
50582498Sroberto	 */
50682498Sroberto	if (mode_ntpdate) {
507290000Sglebius		if (  ( fp_offset > clock_max_fwd  && clock_max_fwd  > 0)
508290000Sglebius		   || (-fp_offset > clock_max_back && clock_max_back > 0)) {
50982498Sroberto			step_systime(fp_offset);
510290000Sglebius			msyslog(LOG_NOTICE, "ntpd: time set %+.6f s",
511290000Sglebius			    fp_offset);
512132451Sroberto			printf("ntpd: time set %+.6fs\n", fp_offset);
51382498Sroberto		} else {
51482498Sroberto			adj_systime(fp_offset);
515290000Sglebius			msyslog(LOG_NOTICE, "ntpd: time slew %+.6f s",
51682498Sroberto			    fp_offset);
517132451Sroberto			printf("ntpd: time slew %+.6fs\n", fp_offset);
51882498Sroberto		}
519182007Sroberto		record_loop_stats(fp_offset, drift_comp, clock_jitter,
52082498Sroberto		    clock_stability, sys_poll);
52182498Sroberto		exit (0);
52282498Sroberto	}
52382498Sroberto
52482498Sroberto	/*
52582498Sroberto	 * The huff-n'-puff filter finds the lowest delay in the recent
52682498Sroberto	 * interval. This is used to correct the offset by one-half the
52782498Sroberto	 * difference between the sample delay and minimum delay. This
52882498Sroberto	 * is most effective if the delays are highly assymetric and
52982498Sroberto	 * clockhopping is avoided and the clock frequency wander is
53082498Sroberto	 * relatively small.
53182498Sroberto	 */
532290000Sglebius	if (sys_huffpuff != NULL) {
53382498Sroberto		if (peer->delay < sys_huffpuff[sys_huffptr])
53482498Sroberto			sys_huffpuff[sys_huffptr] = peer->delay;
53582498Sroberto		if (peer->delay < sys_mindly)
53682498Sroberto			sys_mindly = peer->delay;
53782498Sroberto		if (fp_offset > 0)
53882498Sroberto			dtemp = -(peer->delay - sys_mindly) / 2;
53982498Sroberto		else
54082498Sroberto			dtemp = (peer->delay - sys_mindly) / 2;
54182498Sroberto		fp_offset += dtemp;
542293894Sglebius		DPRINTF(1, ("local_clock: size %d mindly %.6f huffpuff %.6f\n",
543293894Sglebius			    sys_hufflen, sys_mindly, dtemp));
54482498Sroberto	}
54582498Sroberto
54682498Sroberto	/*
547290000Sglebius	 * Clock state machine transition function which defines how the
548290000Sglebius	 * system reacts to large phase and frequency excursion. There
549290000Sglebius	 * are two main regimes: when the offset exceeds the step
550290000Sglebius	 * threshold (128 ms) and when it does not. Under certain
551290000Sglebius	 * conditions updates are suspended until the stepout theshold
552290000Sglebius	 * (900 s) is exceeded. See the documentation on how these
553290000Sglebius	 * thresholds interact with commands and command line options.
554182007Sroberto	 *
555290000Sglebius	 * Note the kernel is disabled if step is disabled or greater
556290000Sglebius	 * than 0.5 s or in ntpdate mode.
55754359Sroberto	 */
558290000Sglebius	osys_poll = sys_poll;
559290000Sglebius	if (sys_poll < peer->minpoll)
560290000Sglebius		sys_poll = peer->minpoll;
561290000Sglebius	if (sys_poll > peer->maxpoll)
562290000Sglebius		sys_poll = peer->maxpoll;
563290000Sglebius	mu = current_time - clock_epoch;
564290000Sglebius	clock_frequency = drift_comp;
565182007Sroberto	rval = 1;
566290000Sglebius	if (  ( fp_offset > clock_max_fwd  && clock_max_fwd  > 0)
567290000Sglebius	   || (-fp_offset > clock_max_back && clock_max_back > 0)
568290000Sglebius	   || force_step_once ) {
569290000Sglebius		if (force_step_once) {
570290000Sglebius			force_step_once = FALSE;  /* we want this only once after startup */
571290000Sglebius			msyslog(LOG_NOTICE, "Doing intital time step" );
572290000Sglebius		}
573290000Sglebius
57454359Sroberto		switch (state) {
57554359Sroberto
57654359Sroberto		/*
577290000Sglebius		 * In SYNC state we ignore the first outlier and switch
578290000Sglebius		 * to SPIK state.
57954359Sroberto		 */
580290000Sglebius		case EVNT_SYNC:
581290000Sglebius			snprintf(tbuf, sizeof(tbuf), "%+.6f s",
582290000Sglebius			    fp_offset);
583290000Sglebius			report_event(EVNT_SPIK, NULL, tbuf);
584290000Sglebius			state = EVNT_SPIK;
58554359Sroberto			return (0);
58654359Sroberto
58754359Sroberto		/*
588290000Sglebius		 * In FREQ state we ignore outliers and inlyers. At the
589290000Sglebius		 * first outlier after the stepout threshold, compute
590290000Sglebius		 * the apparent frequency correction and step the phase.
59154359Sroberto		 */
592290000Sglebius		case EVNT_FREQ:
59382498Sroberto			if (mu < clock_minstep)
59454359Sroberto				return (0);
595182007Sroberto
596290000Sglebius			clock_frequency = direct_freq(fp_offset);
597182007Sroberto
598290000Sglebius			/* fall through to EVNT_SPIK */
59954359Sroberto
60054359Sroberto		/*
601290000Sglebius		 * In SPIK state we ignore succeeding outliers until
602182007Sroberto		 * either an inlyer is found or the stepout threshold is
603182007Sroberto		 * exceeded.
60454359Sroberto		 */
605290000Sglebius		case EVNT_SPIK:
606182007Sroberto			if (mu < clock_minstep)
607182007Sroberto				return (0);
608182007Sroberto
60954359Sroberto			/* fall through to default */
61054359Sroberto
61154359Sroberto		/*
612290000Sglebius		 * We get here by default in NSET and FSET states and
613290000Sglebius		 * from above in FREQ or SPIK states.
614182007Sroberto		 *
615290000Sglebius		 * In NSET state an initial frequency correction is not
616290000Sglebius		 * available, usually because the frequency file has not
617290000Sglebius		 * yet been written. Since the time is outside the step
618290000Sglebius		 * threshold, the clock is stepped. The frequency will
619290000Sglebius		 * be set directly following the stepout interval.
620182007Sroberto		 *
621290000Sglebius		 * In FSET state the initial frequency has been set from
622290000Sglebius		 * the frequency file. Since the time is outside the
623290000Sglebius		 * step threshold, the clock is stepped immediately,
624182007Sroberto		 * rather than after the stepout interval. Guys get
625290000Sglebius		 * nervous if it takes 15 minutes to set the clock for
626182007Sroberto		 * the first time.
627182007Sroberto		 *
628290000Sglebius		 * In FREQ and SPIK states the stepout threshold has
629182007Sroberto		 * expired and the phase is still above the step
630182007Sroberto		 * threshold. Note that a single spike greater than the
631290000Sglebius		 * step threshold is always suppressed, even with a
632290000Sglebius		 * long time constant.
633290000Sglebius		 */
63454359Sroberto		default:
635290000Sglebius			snprintf(tbuf, sizeof(tbuf), "%+.6f s",
636290000Sglebius			    fp_offset);
637290000Sglebius			report_event(EVNT_CLOCKRESET, NULL, tbuf);
638132451Sroberto			step_systime(fp_offset);
639132451Sroberto			reinit_timer();
640182007Sroberto			tc_counter = 0;
641182007Sroberto			clock_jitter = LOGTOD(sys_precision);
642182007Sroberto			rval = 2;
643290000Sglebius			if (state == EVNT_NSET) {
644290000Sglebius				rstclock(EVNT_FREQ, 0);
645182007Sroberto				return (rval);
646182007Sroberto			}
64754359Sroberto			break;
64854359Sroberto		}
649290000Sglebius		rstclock(EVNT_SYNC, 0);
65054359Sroberto	} else {
651182007Sroberto		/*
652182007Sroberto		 * The offset is less than the step threshold. Calculate
653182007Sroberto		 * the jitter as the exponentially weighted offset
654182007Sroberto		 * differences.
655290000Sglebius		 */
656182007Sroberto		etemp = SQUARE(clock_jitter);
657182007Sroberto		dtemp = SQUARE(max(fabs(fp_offset - last_offset),
658182007Sroberto		    LOGTOD(sys_precision)));
659182007Sroberto		clock_jitter = SQRT(etemp + (dtemp - etemp) /
660182007Sroberto		    CLOCK_AVG);
66154359Sroberto		switch (state) {
66254359Sroberto
66354359Sroberto		/*
664290000Sglebius		 * In NSET state this is the first update received and
665182007Sroberto		 * the frequency has not been initialized. Adjust the
666182007Sroberto		 * phase, but do not adjust the frequency until after
667182007Sroberto		 * the stepout threshold.
668182007Sroberto		 */
669290000Sglebius		case EVNT_NSET:
670290000Sglebius			adj_systime(fp_offset);
671290000Sglebius			rstclock(EVNT_FREQ, fp_offset);
672182007Sroberto			break;
673182007Sroberto
674182007Sroberto		/*
675290000Sglebius		 * In FREQ state ignore updates until the stepout
676290000Sglebius		 * threshold. After that, compute the new frequency, but
677290000Sglebius		 * do not adjust the frequency until the holdoff counter
678290000Sglebius		 * decrements to zero.
67954359Sroberto		 */
680290000Sglebius		case EVNT_FREQ:
68182498Sroberto			if (mu < clock_minstep)
68254359Sroberto				return (0);
683182007Sroberto
684290000Sglebius			clock_frequency = direct_freq(fp_offset);
685290000Sglebius			/* fall through */
68654359Sroberto
68754359Sroberto		/*
688290000Sglebius		 * We get here by default in FSET, SPIK and SYNC states.
689290000Sglebius		 * Here compute the frequency update due to PLL and FLL
690290000Sglebius		 * contributions. Note, we avoid frequency discipline at
691290000Sglebius		 * startup until the initial transient has subsided.
69254359Sroberto		 */
69354359Sroberto		default:
694290000Sglebius			if (freq_cnt == 0) {
69554359Sroberto
696290000Sglebius				/*
697290000Sglebius				 * The FLL and PLL frequency gain constants
698290000Sglebius				 * depend on the time constant and Allan
699290000Sglebius				 * intercept. The PLL is always used, but
700290000Sglebius				 * becomes ineffective above the Allan intercept
701290000Sglebius				 * where the FLL becomes effective.
702290000Sglebius				 */
703290000Sglebius				if (sys_poll >= allan_xpt)
704310419Sdelphij					clock_frequency +=
705310419Sdelphij					      (fp_offset - clock_offset)
706310419Sdelphij					    / ( max(ULOGTOD(sys_poll), mu)
707310419Sdelphij					       * CLOCK_FLL);
708290000Sglebius
709290000Sglebius				/*
710290000Sglebius				 * The PLL frequency gain (numerator) depends on
711290000Sglebius				 * the minimum of the update interval and Allan
712290000Sglebius				 * intercept. This reduces the PLL gain when the
713290000Sglebius				 * FLL becomes effective.
714290000Sglebius				 */
715290000Sglebius				etemp = min(ULOGTOD(allan_xpt), mu);
716290000Sglebius				dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll);
717310419Sdelphij				clock_frequency +=
718310419Sdelphij				    fp_offset * etemp / (dtemp * dtemp);
719132451Sroberto			}
720290000Sglebius			rstclock(EVNT_SYNC, fp_offset);
721290000Sglebius			if (fabs(fp_offset) < CLOCK_FLOOR)
722290000Sglebius				freq_cnt = 0;
72354359Sroberto			break;
72454359Sroberto		}
72554359Sroberto	}
72654359Sroberto
727132451Sroberto#ifdef KERNEL_PLL
72854359Sroberto	/*
72954359Sroberto	 * This code segment works when clock adjustments are made using
73054359Sroberto	 * precision time kernel support and the ntp_adjtime() system
73154359Sroberto	 * call. This support is available in Solaris 2.6 and later,
73254359Sroberto	 * Digital Unix 4.0 and later, FreeBSD, Linux and specially
73354359Sroberto	 * modified kernels for HP-UX 9 and Ultrix 4. In the case of the
73454359Sroberto	 * DECstation 5000/240 and Alpha AXP, additional kernel
73554359Sroberto	 * modifications provide a true microsecond clock and nanosecond
73654359Sroberto	 * clock, respectively.
737182007Sroberto	 *
738182007Sroberto	 * Important note: The kernel discipline is used only if the
739182007Sroberto	 * step threshold is less than 0.5 s, as anything higher can
740182007Sroberto	 * lead to overflow problems. This might occur if some misguided
741182007Sroberto	 * lad set the step threshold to something ridiculous.
74254359Sroberto	 */
743290000Sglebius	if (pll_control && kern_enable && freq_cnt == 0) {
74454359Sroberto
74554359Sroberto		/*
74654359Sroberto		 * We initialize the structure for the ntp_adjtime()
74754359Sroberto		 * system call. We have to convert everything to
74854359Sroberto		 * microseconds or nanoseconds first. Do not update the
74954359Sroberto		 * system variables if the ext_enable flag is set. In
75054359Sroberto		 * this case, the external clock driver will update the
75154359Sroberto		 * variables, which will be read later by the local
75254359Sroberto		 * clock driver. Afterwards, remember the time and
75354359Sroberto		 * frequency offsets for jitter and stability values and
754182007Sroberto		 * to update the frequency file.
75554359Sroberto		 */
756290000Sglebius		ZERO(ntv);
75754359Sroberto		if (ext_enable) {
75854359Sroberto			ntv.modes = MOD_STATUS;
75954359Sroberto		} else {
760182007Sroberto#ifdef STA_NANO
761182007Sroberto			ntv.modes = MOD_BITS | MOD_NANO;
762182007Sroberto#else /* STA_NANO */
76354359Sroberto			ntv.modes = MOD_BITS;
764182007Sroberto#endif /* STA_NANO */
76554359Sroberto			if (clock_offset < 0)
76654359Sroberto				dtemp = -.5;
76754359Sroberto			else
76854359Sroberto				dtemp = .5;
769182007Sroberto#ifdef STA_NANO
770182007Sroberto			ntv.offset = (int32)(clock_offset * 1e9 +
771182007Sroberto			    dtemp);
772182007Sroberto			ntv.constant = sys_poll;
773182007Sroberto#else /* STA_NANO */
774182007Sroberto			ntv.offset = (int32)(clock_offset * 1e6 +
775182007Sroberto			    dtemp);
776182007Sroberto			ntv.constant = sys_poll - 4;
777182007Sroberto#endif /* STA_NANO */
778290000Sglebius			if (ntv.constant < 0)
779290000Sglebius				ntv.constant = 0;
780182007Sroberto
781182007Sroberto			ntv.esterror = (u_int32)(clock_jitter * 1e6);
78254359Sroberto			ntv.maxerror = (u_int32)((sys_rootdelay / 2 +
783290000Sglebius			    sys_rootdisp) * 1e6);
78454359Sroberto			ntv.status = STA_PLL;
78554359Sroberto
78654359Sroberto			/*
787290000Sglebius			 * Enable/disable the PPS if requested.
78854359Sroberto			 */
789290000Sglebius			if (hardpps_enable) {
790290000Sglebius				ntv.status |= (STA_PPSTIME | STA_PPSFREQ);
791290000Sglebius				if (!(pll_status & STA_PPSTIME))
792290000Sglebius					sync_status("PPS enabled",
793290000Sglebius						pll_status,
794290000Sglebius						ntv.status);
79582498Sroberto			} else {
796290000Sglebius				ntv.status &= ~(STA_PPSTIME | STA_PPSFREQ);
797290000Sglebius				if (pll_status & STA_PPSTIME)
798290000Sglebius					sync_status("PPS disabled",
799290000Sglebius						pll_status,
800290000Sglebius						ntv.status);
80182498Sroberto			}
802290000Sglebius			if (sys_leap == LEAP_ADDSECOND)
803290000Sglebius				ntv.status |= STA_INS;
804290000Sglebius			else if (sys_leap == LEAP_DELSECOND)
805290000Sglebius				ntv.status |= STA_DEL;
80654359Sroberto		}
80754359Sroberto
80854359Sroberto		/*
80982498Sroberto		 * Pass the stuff to the kernel. If it squeals, turn off
810290000Sglebius		 * the pps. In any case, fetch the kernel offset,
811290000Sglebius		 * frequency and jitter.
81254359Sroberto		 */
813290000Sglebius		ntp_adj_ret = ntp_adjtime(&ntv);
814290000Sglebius		/*
815290000Sglebius		 * A squeal is a return status < 0, or a state change.
816290000Sglebius		 */
817290000Sglebius		if ((0 > ntp_adj_ret) || (ntp_adj_ret != kernel_status)) {
818290000Sglebius			kernel_status = ntp_adj_ret;
819290000Sglebius			ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, hardpps_enable, 0, __LINE__ - 1);
82054359Sroberto		}
82154359Sroberto		pll_status = ntv.status;
822182007Sroberto#ifdef STA_NANO
823182007Sroberto		clock_offset = ntv.offset / 1e9;
824182007Sroberto#else /* STA_NANO */
825182007Sroberto		clock_offset = ntv.offset / 1e6;
826182007Sroberto#endif /* STA_NANO */
827290000Sglebius		clock_frequency = FREQTOD(ntv.freq);
82854359Sroberto
82954359Sroberto		/*
83082498Sroberto		 * If the kernel PPS is lit, monitor its performance.
83154359Sroberto		 */
83254359Sroberto		if (ntv.status & STA_PPSTIME) {
833182007Sroberto#ifdef STA_NANO
834182007Sroberto			clock_jitter = ntv.jitter / 1e9;
835182007Sroberto#else /* STA_NANO */
836182007Sroberto			clock_jitter = ntv.jitter / 1e6;
837182007Sroberto#endif /* STA_NANO */
83854359Sroberto		}
839290000Sglebius
840290000Sglebius#if defined(STA_NANO) && NTP_API == 4
841182007Sroberto		/*
842290000Sglebius		 * If the TAI changes, update the kernel TAI.
843182007Sroberto		 */
844290000Sglebius		if (loop_tai != sys_tai) {
845290000Sglebius			loop_tai = sys_tai;
846290000Sglebius			ntv.modes = MOD_TAI;
847290000Sglebius			ntv.constant = sys_tai;
848290000Sglebius			if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
849290000Sglebius			    ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 1, __LINE__ - 1);
850290000Sglebius			}
851290000Sglebius		}
852290000Sglebius#endif /* STA_NANO */
85354359Sroberto	}
85454359Sroberto#endif /* KERNEL_PLL */
855182007Sroberto
85654359Sroberto	/*
857182007Sroberto	 * Clamp the frequency within the tolerance range and calculate
858290000Sglebius	 * the frequency difference since the last update.
85954359Sroberto	 */
860182007Sroberto	if (fabs(clock_frequency) > NTP_MAXFREQ)
861290000Sglebius		msyslog(LOG_NOTICE,
862182007Sroberto		    "frequency error %.0f PPM exceeds tolerance %.0f PPM",
863182007Sroberto		    clock_frequency * 1e6, NTP_MAXFREQ * 1e6);
864182007Sroberto	dtemp = SQUARE(clock_frequency - drift_comp);
865182007Sroberto	if (clock_frequency > NTP_MAXFREQ)
86682498Sroberto		drift_comp = NTP_MAXFREQ;
867182007Sroberto	else if (clock_frequency < -NTP_MAXFREQ)
86882498Sroberto		drift_comp = -NTP_MAXFREQ;
869132451Sroberto	else
870182007Sroberto		drift_comp = clock_frequency;
87154359Sroberto
872182007Sroberto	/*
873290000Sglebius	 * Calculate the wander as the exponentially weighted RMS
874290000Sglebius	 * frequency differences. Record the change for the frequency
875290000Sglebius	 * file update.
876182007Sroberto	 */
877132451Sroberto	etemp = SQUARE(clock_stability);
878132451Sroberto	clock_stability = SQRT(etemp + (dtemp - etemp) / CLOCK_AVG);
879132451Sroberto
88054359Sroberto	/*
881290000Sglebius	 * Here we adjust the time constant by comparing the current
882182007Sroberto	 * offset with the clock jitter. If the offset is less than the
883182007Sroberto	 * clock jitter times a constant, then the averaging interval is
884182007Sroberto	 * increased, otherwise it is decreased. A bit of hysteresis
885290000Sglebius	 * helps calm the dance. Works best using burst mode. Don't
886290000Sglebius	 * fiddle with the poll during the startup clamp period.
88754359Sroberto	 */
888290000Sglebius	if (freq_cnt > 0) {
889290000Sglebius		tc_counter = 0;
890290000Sglebius	} else if (fabs(clock_offset) < CLOCK_PGATE * clock_jitter) {
891182007Sroberto		tc_counter += sys_poll;
892182007Sroberto		if (tc_counter > CLOCK_LIMIT) {
893182007Sroberto			tc_counter = CLOCK_LIMIT;
894182007Sroberto			if (sys_poll < peer->maxpoll) {
895182007Sroberto				tc_counter = 0;
896182007Sroberto				sys_poll++;
89754359Sroberto			}
898182007Sroberto		}
899182007Sroberto	} else {
900182007Sroberto		tc_counter -= sys_poll << 1;
901182007Sroberto		if (tc_counter < -CLOCK_LIMIT) {
902182007Sroberto			tc_counter = -CLOCK_LIMIT;
903182007Sroberto			if (sys_poll > peer->minpoll) {
904182007Sroberto				tc_counter = 0;
905182007Sroberto				sys_poll--;
90654359Sroberto			}
90754359Sroberto		}
90854359Sroberto	}
90954359Sroberto
91054359Sroberto	/*
911290000Sglebius	 * If the time constant has changed, update the poll variables.
912290000Sglebius	 */
913290000Sglebius	if (osys_poll != sys_poll)
914290000Sglebius		poll_update(peer, sys_poll);
915290000Sglebius
916290000Sglebius	/*
917182007Sroberto	 * Yibbidy, yibbbidy, yibbidy; that'h all folks.
91854359Sroberto	 */
919182007Sroberto	record_loop_stats(clock_offset, drift_comp, clock_jitter,
92082498Sroberto	    clock_stability, sys_poll);
921293894Sglebius	DPRINTF(1, ("local_clock: offset %.9f jit %.9f freq %.3f stab %.3f poll %d\n",
922290000Sglebius		    clock_offset, clock_jitter, drift_comp * 1e6,
923293894Sglebius		    clock_stability * 1e6, sys_poll));
924182007Sroberto	return (rval);
925293894Sglebius#endif /* not LOCKCLOCK */
92654359Sroberto}
92754359Sroberto
92854359Sroberto
92954359Sroberto/*
93054359Sroberto * adj_host_clock - Called once every second to update the local clock.
931132451Sroberto *
932132451Sroberto * LOCKCLOCK: The only thing this routine does is increment the
933290000Sglebius * sys_rootdisp variable.
93454359Sroberto */
93554359Srobertovoid
93654359Srobertoadj_host_clock(
93754359Sroberto	void
93854359Sroberto	)
93954359Sroberto{
940290000Sglebius	double	offset_adj;
941290000Sglebius	double	freq_adj;
94254359Sroberto
94354359Sroberto	/*
94454359Sroberto	 * Update the dispersion since the last update. In contrast to
94554359Sroberto	 * NTPv3, NTPv4 does not declare unsynchronized after one day,
94654359Sroberto	 * since the dispersion check serves this function. Also,
94754359Sroberto	 * since the poll interval can exceed one day, the old test
948290000Sglebius	 * would be counterproductive. During the startup clamp period, the
949290000Sglebius	 * time constant is clamped at 2.
95054359Sroberto	 */
951290000Sglebius	sys_rootdisp += clock_phi;
952132451Sroberto#ifndef LOCKCLOCK
953290000Sglebius	if (!ntp_enable || mode_ntpdate)
954290000Sglebius		return;
95554359Sroberto	/*
956290000Sglebius	 * Determine the phase adjustment. The gain factor (denominator)
957290000Sglebius	 * increases with poll interval, so is dominated by the FLL
958290000Sglebius	 * above the Allan intercept. Note the reduced time constant at
959290000Sglebius	 * startup.
960182007Sroberto	 */
961290000Sglebius	if (state != EVNT_SYNC) {
962290000Sglebius		offset_adj = 0.;
963290000Sglebius	} else if (freq_cnt > 0) {
964290000Sglebius		offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(1));
965290000Sglebius		freq_cnt--;
966290000Sglebius#ifdef KERNEL_PLL
967290000Sglebius	} else if (pll_control && kern_enable) {
968290000Sglebius		offset_adj = 0.;
969290000Sglebius#endif /* KERNEL_PLL */
970290000Sglebius	} else {
971290000Sglebius		offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(sys_poll));
972290000Sglebius	}
973182007Sroberto
974182007Sroberto	/*
975290000Sglebius	 * If the kernel discipline is enabled the frequency correction
976290000Sglebius	 * drift_comp has already been engaged via ntp_adjtime() in
977290000Sglebius	 * set_freq().  Otherwise it is a component of the adj_systime()
978290000Sglebius	 * offset.
97954359Sroberto	 */
980290000Sglebius#ifdef KERNEL_PLL
981290000Sglebius	if (pll_control && kern_enable)
982290000Sglebius		freq_adj = 0.;
983290000Sglebius	else
984290000Sglebius#endif /* KERNEL_PLL */
985290000Sglebius		freq_adj = drift_comp;
98654359Sroberto
987290000Sglebius	/* Bound absolute value of total adjustment to NTP_MAXFREQ. */
988290000Sglebius	if (offset_adj + freq_adj > NTP_MAXFREQ)
989290000Sglebius		offset_adj = NTP_MAXFREQ - freq_adj;
990290000Sglebius	else if (offset_adj + freq_adj < -NTP_MAXFREQ)
991290000Sglebius		offset_adj = -NTP_MAXFREQ - freq_adj;
992290000Sglebius
993290000Sglebius	clock_offset -= offset_adj;
99454359Sroberto	/*
995290000Sglebius	 * Windows port adj_systime() must be called each second,
996290000Sglebius	 * even if the argument is zero, to ease emulation of
997290000Sglebius	 * adjtime() using Windows' slew API which controls the rate
998290000Sglebius	 * but does not automatically stop slewing when an offset
999290000Sglebius	 * has decayed to zero.
100054359Sroberto	 */
1001293894Sglebius	DEBUG_INSIST(enable_panic_check == TRUE);
1002293894Sglebius	enable_panic_check = FALSE;
1003290000Sglebius	adj_systime(offset_adj + freq_adj);
1004293894Sglebius	enable_panic_check = TRUE;
1005132451Sroberto#endif /* LOCKCLOCK */
100654359Sroberto}
100754359Sroberto
100854359Sroberto
100954359Sroberto/*
1010290000Sglebius * Clock state machine. Enter new state and set state variables.
101154359Sroberto */
101254359Srobertostatic void
101354359Srobertorstclock(
1014182007Sroberto	int	trans,		/* new state */
1015182007Sroberto	double	offset		/* new offset */
101654359Sroberto	)
101754359Sroberto{
1018293894Sglebius	DPRINTF(2, ("rstclock: mu %lu state %d poll %d count %d\n",
1019290000Sglebius		    current_time - clock_epoch, trans, sys_poll,
1020293894Sglebius		    tc_counter));
1021290000Sglebius	if (trans != state && trans != EVNT_FSET)
1022290000Sglebius		report_event(trans, NULL, NULL);
1023182007Sroberto	state = trans;
1024182007Sroberto	last_offset = clock_offset = offset;
1025290000Sglebius	clock_epoch = current_time;
102682498Sroberto}
102754359Sroberto
102854359Sroberto
102982498Sroberto/*
1030290000Sglebius * calc_freq - calculate frequency directly
1031290000Sglebius *
1032290000Sglebius * This is very carefully done. When the offset is first computed at the
1033290000Sglebius * first update, a residual frequency component results. Subsequently,
1034290000Sglebius * updates are suppresed until the end of the measurement interval while
1035290000Sglebius * the offset is amortized. At the end of the interval the frequency is
1036290000Sglebius * calculated from the current offset, residual offset, length of the
1037290000Sglebius * interval and residual frequency component. At the same time the
1038290000Sglebius * frequenchy file is armed for update at the next hourly stats.
1039290000Sglebius */
1040290000Sglebiusstatic double
1041290000Sglebiusdirect_freq(
1042290000Sglebius	double	fp_offset
1043290000Sglebius	)
1044290000Sglebius{
1045290000Sglebius	set_freq(fp_offset / (current_time - clock_epoch));
1046290000Sglebius
1047290000Sglebius	return drift_comp;
1048290000Sglebius}
1049290000Sglebius
1050290000Sglebius
1051290000Sglebius/*
1052290000Sglebius * set_freq - set clock frequency correction
1053290000Sglebius *
1054290000Sglebius * Used to step the frequency correction at startup, possibly again once
1055290000Sglebius * the frequency is measured (that is, transitioning from EVNT_NSET to
1056290000Sglebius * EVNT_FSET), and finally to switch between daemon and kernel loop
1057290000Sglebius * discipline at runtime.
1058290000Sglebius *
1059290000Sglebius * When the kernel loop discipline is available but the daemon loop is
1060290000Sglebius * in use, the kernel frequency correction is disabled (set to 0) to
1061290000Sglebius * ensure drift_comp is applied by only one of the loops.
1062290000Sglebius */
1063290000Sglebiusstatic void
1064290000Sglebiusset_freq(
1065290000Sglebius	double	freq		/* frequency update */
1066290000Sglebius	)
1067290000Sglebius{
1068290000Sglebius	const char *	loop_desc;
1069290000Sglebius	int ntp_adj_ret;
1070290000Sglebius
1071293894Sglebius	(void)ntp_adj_ret; /* not always used below... */
1072290000Sglebius	drift_comp = freq;
1073290000Sglebius	loop_desc = "ntpd";
1074290000Sglebius#ifdef KERNEL_PLL
1075290000Sglebius	if (pll_control) {
1076290000Sglebius		ZERO(ntv);
1077290000Sglebius		ntv.modes = MOD_FREQUENCY;
1078290000Sglebius		if (kern_enable) {
1079290000Sglebius			loop_desc = "kernel";
1080290000Sglebius			ntv.freq = DTOFREQ(drift_comp);
1081290000Sglebius		}
1082290000Sglebius		if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
1083290000Sglebius		    ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1);
1084290000Sglebius		}
1085290000Sglebius	}
1086290000Sglebius#endif /* KERNEL_PLL */
1087290000Sglebius	mprintf_event(EVNT_FSET, NULL, "%s %.3f PPM", loop_desc,
1088290000Sglebius	    drift_comp * 1e6);
1089290000Sglebius}
1090290000Sglebius
1091290000Sglebius
1092290000Sglebius#ifdef KERNEL_PLL
1093290000Sglebiusstatic void
1094290000Sglebiusstart_kern_loop(void)
1095290000Sglebius{
1096290000Sglebius	static int atexit_done;
1097290000Sglebius	int ntp_adj_ret;
1098290000Sglebius
1099290000Sglebius	pll_control = TRUE;
1100290000Sglebius	ZERO(ntv);
1101290000Sglebius	ntv.modes = MOD_BITS;
1102290000Sglebius	ntv.status = STA_PLL;
1103290000Sglebius	ntv.maxerror = MAXDISPERSE;
1104290000Sglebius	ntv.esterror = MAXDISPERSE;
1105290000Sglebius	ntv.constant = sys_poll; /* why is it that here constant is unconditionally set to sys_poll, whereas elsewhere is is modified depending on nanosecond vs. microsecond kernel? */
1106290000Sglebius#ifdef SIGSYS
1107290000Sglebius	/*
1108290000Sglebius	 * Use sigsetjmp() to save state and then call ntp_adjtime(); if
1109290000Sglebius	 * it fails, then pll_trap() will set pll_control FALSE before
1110290000Sglebius	 * returning control using siglogjmp().
1111290000Sglebius	 */
1112290000Sglebius	newsigsys.sa_handler = pll_trap;
1113290000Sglebius	newsigsys.sa_flags = 0;
1114290000Sglebius	if (sigaction(SIGSYS, &newsigsys, &sigsys)) {
1115290000Sglebius		msyslog(LOG_ERR, "sigaction() trap SIGSYS: %m");
1116290000Sglebius		pll_control = FALSE;
1117290000Sglebius	} else {
1118290000Sglebius		if (sigsetjmp(env, 1) == 0) {
1119290000Sglebius			if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
1120290000Sglebius			    ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1);
1121290000Sglebius			}
1122290000Sglebius		}
1123290000Sglebius		if (sigaction(SIGSYS, &sigsys, NULL)) {
1124290000Sglebius			msyslog(LOG_ERR,
1125290000Sglebius			    "sigaction() restore SIGSYS: %m");
1126290000Sglebius			pll_control = FALSE;
1127290000Sglebius		}
1128290000Sglebius	}
1129290000Sglebius#else /* SIGSYS */
1130290000Sglebius	if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
1131290000Sglebius	    ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1);
1132290000Sglebius	}
1133290000Sglebius#endif /* SIGSYS */
1134290000Sglebius
1135290000Sglebius	/*
1136290000Sglebius	 * Save the result status and light up an external clock
1137290000Sglebius	 * if available.
1138290000Sglebius	 */
1139290000Sglebius	pll_status = ntv.status;
1140290000Sglebius	if (pll_control) {
1141290000Sglebius		if (!atexit_done) {
1142290000Sglebius			atexit_done = TRUE;
1143290000Sglebius			atexit(&stop_kern_loop);
1144290000Sglebius		}
1145290000Sglebius#ifdef STA_NANO
1146290000Sglebius		if (pll_status & STA_CLK)
1147290000Sglebius			ext_enable = TRUE;
1148290000Sglebius#endif /* STA_NANO */
1149290000Sglebius		report_event(EVNT_KERN, NULL,
1150290000Sglebius	  	    "kernel time sync enabled");
1151290000Sglebius	}
1152290000Sglebius}
1153290000Sglebius#endif	/* KERNEL_PLL */
1154290000Sglebius
1155290000Sglebius
1156290000Sglebius#ifdef KERNEL_PLL
1157290000Sglebiusstatic void
1158290000Sglebiusstop_kern_loop(void)
1159290000Sglebius{
1160290000Sglebius	if (pll_control && kern_enable)
1161290000Sglebius		report_event(EVNT_KERN, NULL,
1162290000Sglebius		    "kernel time sync disabled");
1163290000Sglebius}
1164290000Sglebius#endif	/* KERNEL_PLL */
1165290000Sglebius
1166290000Sglebius
1167290000Sglebius/*
1168290000Sglebius * select_loop() - choose kernel or daemon loop discipline.
1169290000Sglebius */
1170290000Sglebiusvoid
1171290000Sglebiusselect_loop(
1172290000Sglebius	int	use_kern_loop
1173290000Sglebius	)
1174290000Sglebius{
1175290000Sglebius	if (kern_enable == use_kern_loop)
1176290000Sglebius		return;
1177290000Sglebius#ifdef KERNEL_PLL
1178290000Sglebius	if (pll_control && !use_kern_loop)
1179290000Sglebius		stop_kern_loop();
1180290000Sglebius#endif
1181290000Sglebius	kern_enable = use_kern_loop;
1182290000Sglebius#ifdef KERNEL_PLL
1183290000Sglebius	if (pll_control && use_kern_loop)
1184290000Sglebius		start_kern_loop();
1185290000Sglebius#endif
1186290000Sglebius	/*
1187290000Sglebius	 * If this loop selection change occurs after initial startup,
1188290000Sglebius	 * call set_freq() to switch the frequency compensation to or
1189290000Sglebius	 * from the kernel loop.
1190290000Sglebius	 */
1191290000Sglebius#ifdef KERNEL_PLL
1192290000Sglebius	if (pll_control && loop_started)
1193290000Sglebius		set_freq(drift_comp);
1194290000Sglebius#endif
1195290000Sglebius}
1196290000Sglebius
1197290000Sglebius
1198290000Sglebius/*
119982498Sroberto * huff-n'-puff filter
120082498Sroberto */
120182498Srobertovoid
1202290000Sglebiushuffpuff(void)
120382498Sroberto{
120482498Sroberto	int i;
120554359Sroberto
120682498Sroberto	if (sys_huffpuff == NULL)
120782498Sroberto		return;
1208182007Sroberto
120982498Sroberto	sys_huffptr = (sys_huffptr + 1) % sys_hufflen;
121082498Sroberto	sys_huffpuff[sys_huffptr] = 1e9;
121182498Sroberto	sys_mindly = 1e9;
121282498Sroberto	for (i = 0; i < sys_hufflen; i++) {
121382498Sroberto		if (sys_huffpuff[i] < sys_mindly)
121482498Sroberto			sys_mindly = sys_huffpuff[i];
121554359Sroberto	}
121654359Sroberto}
121754359Sroberto
121854359Sroberto
121954359Sroberto/*
122054359Sroberto * loop_config - configure the loop filter
1221132451Sroberto *
1222132451Sroberto * LOCKCLOCK: The LOOP_DRIFTINIT and LOOP_DRIFTCOMP cases are no-ops.
122354359Sroberto */
122454359Srobertovoid
122554359Srobertoloop_config(
1226290000Sglebius	int	item,
1227290000Sglebius	double	freq
122854359Sroberto	)
122954359Sroberto{
1230290000Sglebius	int	i;
1231290000Sglebius	double	ftemp;
123254359Sroberto
1233293894Sglebius	DPRINTF(2, ("loop_config: item %d freq %f\n", item, freq));
123454359Sroberto	switch (item) {
123554359Sroberto
1236290000Sglebius	/*
1237290000Sglebius	 * We first assume the kernel supports the ntp_adjtime()
1238290000Sglebius	 * syscall. If that syscall works, initialize the kernel time
1239290000Sglebius	 * variables. Otherwise, continue leaving no harm behind.
1240290000Sglebius	 */
124182498Sroberto	case LOOP_DRIFTINIT:
1242132451Sroberto#ifndef LOCKCLOCK
124354359Sroberto#ifdef KERNEL_PLL
1244182007Sroberto		if (mode_ntpdate)
1245132451Sroberto			break;
1246132451Sroberto
1247290000Sglebius		start_kern_loop();
124882498Sroberto#endif /* KERNEL_PLL */
124954359Sroberto
125082498Sroberto		/*
1251290000Sglebius		 * Initialize frequency if given; otherwise, begin frequency
1252290000Sglebius		 * calibration phase.
125382498Sroberto		 */
1254290000Sglebius		ftemp = init_drift_comp / 1e6;
1255290000Sglebius		if (ftemp > NTP_MAXFREQ)
1256290000Sglebius			ftemp = NTP_MAXFREQ;
1257290000Sglebius		else if (ftemp < -NTP_MAXFREQ)
1258290000Sglebius			ftemp = -NTP_MAXFREQ;
1259290000Sglebius		set_freq(ftemp);
1260290000Sglebius		if (freq_set)
1261290000Sglebius			rstclock(EVNT_FSET, 0);
1262290000Sglebius		else
1263290000Sglebius			rstclock(EVNT_NSET, 0);
1264290000Sglebius		loop_started = TRUE;
1265132451Sroberto#endif /* LOCKCLOCK */
126682498Sroberto		break;
126782498Sroberto
1268182007Sroberto	case LOOP_KERN_CLEAR:
1269290000Sglebius#if 0		/* XXX: needs more review, and how can we get here? */
1270182007Sroberto#ifndef LOCKCLOCK
1271290000Sglebius# ifdef KERNEL_PLL
1272290000Sglebius		if (pll_control && kern_enable) {
1273182007Sroberto			memset((char *)&ntv, 0, sizeof(ntv));
1274290000Sglebius			ntv.modes = MOD_STATUS;
1275182007Sroberto			ntv.status = STA_UNSYNC;
1276182007Sroberto			ntp_adjtime(&ntv);
1277290000Sglebius			sync_status("kernel time sync disabled",
1278290000Sglebius				pll_status,
1279290000Sglebius				ntv.status);
1280182007Sroberto		   }
1281290000Sglebius# endif /* KERNEL_PLL */
1282182007Sroberto#endif /* LOCKCLOCK */
1283290000Sglebius#endif
1284182007Sroberto		break;
1285182007Sroberto
128682498Sroberto	/*
1287290000Sglebius	 * Tinker command variables for Ulrich Windl. Very dangerous.
128882498Sroberto	 */
1289290000Sglebius	case LOOP_ALLAN:	/* Allan intercept (log2) (allan) */
1290290000Sglebius		allan_xpt = (u_char)freq;
129182498Sroberto		break;
129282498Sroberto
1293290000Sglebius	case LOOP_CODEC:	/* audio codec frequency (codec) */
1294290000Sglebius		clock_codec = freq / 1e6;
129582498Sroberto		break;
129682498Sroberto
1297290000Sglebius	case LOOP_PHI:		/* dispersion threshold (dispersion) */
1298290000Sglebius		clock_phi = freq / 1e6;
129982498Sroberto		break;
130082498Sroberto
1301290000Sglebius	case LOOP_FREQ:		/* initial frequency (freq) */
1302290000Sglebius		init_drift_comp = freq;
1303290000Sglebius		freq_set++;
130482498Sroberto		break;
130582498Sroberto
1306290000Sglebius	case LOOP_HUFFPUFF:	/* huff-n'-puff length (huffpuff) */
130782498Sroberto		if (freq < HUFFPUFF)
130882498Sroberto			freq = HUFFPUFF;
130982498Sroberto		sys_hufflen = (int)(freq / HUFFPUFF);
1310290000Sglebius		sys_huffpuff = emalloc(sizeof(sys_huffpuff[0]) *
131182498Sroberto		    sys_hufflen);
131282498Sroberto		for (i = 0; i < sys_hufflen; i++)
131382498Sroberto			sys_huffpuff[i] = 1e9;
131482498Sroberto		sys_mindly = 1e9;
131582498Sroberto		break;
1316132451Sroberto
1317290000Sglebius	case LOOP_PANIC:	/* panic threshold (panic) */
1318290000Sglebius		clock_panic = freq;
1319132451Sroberto		break;
1320290000Sglebius
1321290000Sglebius	case LOOP_MAX:		/* step threshold (step) */
1322290000Sglebius		clock_max_fwd = clock_max_back = freq;
1323290000Sglebius		if (freq == 0 || freq > 0.5)
1324290000Sglebius			select_loop(FALSE);
1325290000Sglebius		break;
1326290000Sglebius
1327290000Sglebius	case LOOP_MAX_BACK:	/* step threshold (step) */
1328290000Sglebius		clock_max_back = freq;
1329290000Sglebius		/*
1330290000Sglebius		 * Leave using the kernel discipline code unless both
1331290000Sglebius		 * limits are massive.  This assumes the reason to stop
1332290000Sglebius		 * using it is that it's pointless, not that it goes wrong.
1333290000Sglebius		 */
1334290000Sglebius		if (  (clock_max_back == 0 || clock_max_back > 0.5)
1335290000Sglebius		   || (clock_max_fwd  == 0 || clock_max_fwd  > 0.5))
1336290000Sglebius			select_loop(FALSE);
1337290000Sglebius		break;
1338290000Sglebius
1339290000Sglebius	case LOOP_MAX_FWD:	/* step threshold (step) */
1340290000Sglebius		clock_max_fwd = freq;
1341290000Sglebius		if (  (clock_max_back == 0 || clock_max_back > 0.5)
1342290000Sglebius		   || (clock_max_fwd  == 0 || clock_max_fwd  > 0.5))
1343290000Sglebius			select_loop(FALSE);
1344290000Sglebius		break;
1345290000Sglebius
1346290000Sglebius	case LOOP_MINSTEP:	/* stepout threshold (stepout) */
1347290000Sglebius		if (freq < CLOCK_MINSTEP)
1348290000Sglebius			clock_minstep = CLOCK_MINSTEP;
1349290000Sglebius		else
1350290000Sglebius			clock_minstep = freq;
1351290000Sglebius		break;
1352290000Sglebius
1353290000Sglebius	case LOOP_TICK:		/* tick increment (tick) */
1354290000Sglebius		set_sys_tick_precision(freq);
1355290000Sglebius		break;
1356290000Sglebius
1357290000Sglebius	case LOOP_LEAP:		/* not used, fall through */
1358290000Sglebius	default:
1359290000Sglebius		msyslog(LOG_NOTICE,
1360290000Sglebius		    "loop_config: unsupported option %d", item);
136154359Sroberto	}
136254359Sroberto}
136354359Sroberto
136454359Sroberto
136554359Sroberto#if defined(KERNEL_PLL) && defined(SIGSYS)
136654359Sroberto/*
136754359Sroberto * _trap - trap processor for undefined syscalls
136854359Sroberto *
136954359Sroberto * This nugget is called by the kernel when the SYS_ntp_adjtime()
137054359Sroberto * syscall bombs because the silly thing has not been implemented in
137154359Sroberto * the kernel. In this case the phase-lock loop is emulated by
137254359Sroberto * the stock adjtime() syscall and a lot of indelicate abuse.
137354359Sroberto */
137454359Srobertostatic RETSIGTYPE
137554359Srobertopll_trap(
137654359Sroberto	int arg
137754359Sroberto	)
137854359Sroberto{
1379290000Sglebius	pll_control = FALSE;
138054359Sroberto	siglongjmp(env, 1);
138154359Sroberto}
138254359Sroberto#endif /* KERNEL_PLL && SIGSYS */
1383