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
11290001Sglebius#ifdef USE_SNPRINTB
12290001Sglebius# include <util.h>
13290001Sglebius#endif
1482498Sroberto#include "ntpd.h"
1582498Sroberto#include "ntp_io.h"
1682498Sroberto#include "ntp_unixtime.h"
1782498Sroberto#include "ntp_stdlib.h"
1882498Sroberto
19290001Sglebius#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) */
38290001Sglebius#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 */
43290001Sglebius#define CLOCK_FLL	.25	/* FLL loop gain */
44290001Sglebius#define	CLOCK_FLOOR	.0005	/* startup offset floor (s) */
45290001Sglebius#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) */
49290001Sglebius#define	FREQTOD(x)	((x) / 65536e6) /* NTP to double */
50290001Sglebius#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
58290001Sglebius *	========================================================
59290001Sglebius *	NSET	FREQ		step, FREQ	freq not set
6082498Sroberto *
61290001Sglebius *	FSET	SYNC		step, SYNC	freq set
6282498Sroberto *
63290001Sglebius *	FREQ	if (mu < 900)	if (mu < 900)	set freq direct
64182007Sroberto *		    ignore	    ignore
65182007Sroberto *		else		else
66182007Sroberto *		    freq, SYNC	    freq, step, SYNC
6782498Sroberto *
68290001Sglebius *	SYNC	SYNC		SPIK, ignore	adjust phase/freq
69290001Sglebius *
70290001Sglebius *	SPIK	SYNC		if (mu < 900)	adjust phase/freq
71182007Sroberto *				    ignore
72290001Sglebius *				step, SYNC
7354359Sroberto */
7454359Sroberto/*
7554359Sroberto * Kernel PLL/PPS state machine. This is used with the kernel PLL
76290001Sglebius * 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 *
94290001Sglebius * Each PPS time/frequency discipline can be enabled by the atom driver
95290001Sglebius * or another driver. If enabled, the STA_PPSTIME and STA_FREQ bits are
96290001Sglebius * set in the kernel status word; otherwise, these bits are cleared.
97290001Sglebius * 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
104290001Sglebius * clock. Unless specified otherwise, all times are in seconds.
10554359Sroberto */
10654359Sroberto/*
10782498Sroberto * Program variables that can be tinkered.
10882498Sroberto */
109290001Sglebiusdouble	clock_max_back = CLOCK_MAX;	/* step threshold */
110290001Sglebiusdouble	clock_max_fwd =  CLOCK_MAX;	/* step threshold */
111290001Sglebiusdouble	clock_minstep = CLOCK_MINSTEP; /* stepout threshold */
112290001Sglebiusdouble	clock_panic = CLOCK_PANIC; /* panic threshold */
11382498Srobertodouble	clock_phi = CLOCK_PHI;	/* dispersion rate (s/s) */
114290001Sglebiusu_char	allan_xpt = CLOCK_ALLAN; /* Allan intercept (log2 s) */
11582498Sroberto
11682498Sroberto/*
11754359Sroberto * Program variables
11854359Sroberto */
119290001Sglebiusstatic double clock_offset;	/* offset */
120290001Sglebiusdouble	clock_jitter;		/* offset jitter */
121182007Srobertodouble	drift_comp;		/* frequency (s/s) */
122290001Sglebiusstatic double init_drift_comp; /* initial frequency (PPM) */
123182007Srobertodouble	clock_stability;	/* frequency stability (wander) (s/s) */
124290001Sglebiusdouble	clock_codec;		/* audio codec frequency (samples/s) */
125290001Sglebiusstatic u_long clock_epoch;	/* last update */
126290001Sglebiusu_int	sys_tai;		/* TAI offset from UTC */
127290001Sglebiusstatic int loop_started;	/* TRUE after LOOP_DRIFTINIT */
128290001Sglebiusstatic void rstclock (int, double); /* transition function */
129290001Sglebiusstatic double direct_freq(double); /* direct set frequency */
130290001Sglebiusstatic void set_freq(double);	/* set frequency */
131290001Sglebius#ifndef PATH_MAX
132290001Sglebius# define PATH_MAX MAX_PATH
133290001Sglebius#endif
134290001Sglebiusstatic char relative_path[PATH_MAX + 1]; /* relative path per recursive make */
135290001Sglebiusstatic char *this_file = NULL;
13654359Sroberto
13754359Sroberto#ifdef KERNEL_PLL
138290001Sglebiusstatic struct timex ntv;	/* ntp_adjtime() parameters */
139290001Sglebiusint	pll_status;		/* last kernel status bits */
140290001Sglebius#if defined(STA_NANO) && NTP_API == 4
141290001Sglebiusstatic u_int loop_tai;		/* last TAI offset */
142290001Sglebius#endif /* STA_NANO */
143290001Sglebiusstatic	void	start_kern_loop(void);
144290001Sglebiusstatic	void	stop_kern_loop(void);
14554359Sroberto#endif /* KERNEL_PLL */
14654359Sroberto
14754359Sroberto/*
14854359Sroberto * Clock state machine control flags
14954359Sroberto */
150290001Sglebiusint	ntp_enable = TRUE;	/* clock discipline enabled */
15154359Srobertoint	pll_control;		/* kernel support available */
152290001Sglebiusint	kern_enable = TRUE;	/* kernel support enabled */
153290001Sglebiusint	hardpps_enable;		/* kernel PPS discipline enabled */
15454359Srobertoint	ext_enable;		/* external clock enabled */
15582498Srobertoint	pps_stratum;		/* pps stratum */
156290001Sglebiusint	kernel_status;		/* from ntp_adjtime */
157290001Sglebiusint	force_step_once = FALSE; /* always step time once at startup (-G) */
158290001Sglebiusint	mode_ntpdate = FALSE;	/* exit on first clock set (-q) */
159290001Sglebiusint	freq_cnt;		/* initial frequency clamp */
160290001Sglebiusint	freq_set;		/* initial set frequency switch */
16154359Sroberto
16254359Sroberto/*
16354359Sroberto * Clock state machine variables
16454359Sroberto */
165290001Sglebiusint	state = 0;		/* clock discipline state */
166290001Sglebiusu_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
183290001Sglebiusstatic 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
190290001Sglebiusstatic void
191290001Sglebiussync_status(const char *what, int ostatus, int nstatus)
192290001Sglebius{
193290001Sglebius	char obuf[256], nbuf[256], tbuf[1024];
194290001Sglebius#if defined(USE_SNPRINTB) && defined (STA_FMT)
195290001Sglebius	snprintb(obuf, sizeof(obuf), STA_FMT, ostatus);
196290001Sglebius	snprintb(nbuf, sizeof(nbuf), STA_FMT, nstatus);
197290001Sglebius#else
198290001Sglebius	snprintf(obuf, sizeof(obuf), "%04x", ostatus);
199290001Sglebius	snprintf(nbuf, sizeof(nbuf), "%04x", nstatus);
200290001Sglebius#endif
201290001Sglebius	snprintf(tbuf, sizeof(tbuf), "%s status: %s -> %s", what, obuf, nbuf);
202290001Sglebius	report_event(EVNT_KERN, NULL, tbuf);
203290001Sglebius}
204290001Sglebius
20554359Sroberto/*
206290001Sglebius * file_name - return pointer to non-relative portion of this C file pathname
207290001Sglebius */
208290001Sglebiusstatic char *file_name(void)
209290001Sglebius{
210290001Sglebius	if (this_file == NULL) {
211290001Sglebius	    (void)strncpy(relative_path, __FILE__, PATH_MAX);
212290001Sglebius	    for (this_file=relative_path;
213290001Sglebius		*this_file && ! isalnum((unsigned char)*this_file);
214290001Sglebius		this_file++) ;
215290001Sglebius	}
216290001Sglebius	return this_file;
217290001Sglebius}
218290001Sglebius
219290001Sglebius/*
22054359Sroberto * init_loopfilter - initialize loop filter data
22154359Sroberto */
22254359Srobertovoid
22354359Srobertoinit_loopfilter(void)
22454359Sroberto{
22554359Sroberto	/*
226290001Sglebius	 * Initialize state variables.
22754359Sroberto	 */
228290001Sglebius	sys_poll = ntp_minpoll;
229182007Sroberto	clock_jitter = LOGTOD(sys_precision);
230290001Sglebius	freq_cnt = (int)clock_minstep;
23154359Sroberto}
23254359Sroberto
233290001Sglebius#ifdef KERNEL_PLL
23454359Sroberto/*
235290001Sglebius * ntp_adjtime_error_handler - process errors from ntp_adjtime
236290001Sglebius */
237290001Sglebiusstatic void
238290001Sglebiusntp_adjtime_error_handler(
239290001Sglebius	const char *caller,	/* name of calling function */
240290001Sglebius	struct timex *ptimex,	/* pointer to struct timex */
241290001Sglebius	int ret,		/* return value from ntp_adjtime */
242290001Sglebius	int saved_errno,	/* value of errno when ntp_adjtime returned */
243290001Sglebius	int pps_call,		/* ntp_adjtime call was PPS-related */
244290001Sglebius	int tai_call,		/* ntp_adjtime call was TAI-related */
245290001Sglebius	int line		/* line number of ntp_adjtime call */
246290001Sglebius	)
247290001Sglebius{
248290001Sglebius	char des[1024] = "";	/* Decoded Error Status */
249290001Sglebius
250290001Sglebius	switch (ret) {
251290001Sglebius	    case -1:
252290001Sglebius		switch (saved_errno) {
253290001Sglebius		    case EFAULT:
254290001Sglebius			msyslog(LOG_ERR, "%s: %s line %d: invalid struct timex pointer: 0x%lx",
255290001Sglebius			    caller, file_name(), line,
256290001Sglebius			    (long)((void *)ptimex)
257290001Sglebius			);
258290001Sglebius		    break;
259290001Sglebius		    case EINVAL:
260290001Sglebius			msyslog(LOG_ERR, "%s: %s line %d: invalid struct timex \"constant\" element value: %ld",
261290001Sglebius			    caller, file_name(), line,
262290001Sglebius			    (long)(ptimex->constant)
263290001Sglebius			);
264290001Sglebius		    break;
265290001Sglebius		    case EPERM:
266290001Sglebius			if (tai_call) {
267290001Sglebius			    errno = saved_errno;
268290001Sglebius			    msyslog(LOG_ERR,
269290001Sglebius				"%s: ntp_adjtime(TAI) failed: %m",
270290001Sglebius				caller);
271290001Sglebius			}
272290001Sglebius			errno = saved_errno;
273290001Sglebius			msyslog(LOG_ERR, "%s: %s line %d: ntp_adjtime: %m",
274290001Sglebius			    caller, file_name(), line
275290001Sglebius			);
276290001Sglebius		    break;
277290001Sglebius		    default:
278290001Sglebius			msyslog(LOG_NOTICE, "%s: %s line %d: unhandled errno value %d after failed ntp_adjtime call",
279290001Sglebius			    caller, file_name(), line,
280290001Sglebius			    saved_errno
281290001Sglebius			);
282290001Sglebius		    break;
283290001Sglebius		}
284290001Sglebius	    break;
285290001Sglebius#ifdef TIME_OK
286290001Sglebius	    case TIME_OK: /* 0: synchronized, no leap second warning */
287290001Sglebius		/* msyslog(LOG_INFO, "kernel reports time is synchronized normally"); */
288290001Sglebius	    break;
289290001Sglebius#else
290290001Sglebius# warning TIME_OK is not defined
291290001Sglebius#endif
292290001Sglebius#ifdef TIME_INS
293290001Sglebius	    case TIME_INS: /* 1: positive leap second warning */
294290001Sglebius		msyslog(LOG_INFO, "kernel reports leap second insertion scheduled");
295290001Sglebius	    break;
296290001Sglebius#else
297290001Sglebius# warning TIME_INS is not defined
298290001Sglebius#endif
299290001Sglebius#ifdef TIME_DEL
300290001Sglebius	    case TIME_DEL: /* 2: negative leap second warning */
301290001Sglebius		msyslog(LOG_INFO, "kernel reports leap second deletion scheduled");
302290001Sglebius	    break;
303290001Sglebius#else
304290001Sglebius# warning TIME_DEL is not defined
305290001Sglebius#endif
306290001Sglebius#ifdef TIME_OOP
307290001Sglebius	    case TIME_OOP: /* 3: leap second in progress */
308290001Sglebius		msyslog(LOG_INFO, "kernel reports leap second in progress");
309290001Sglebius	    break;
310290001Sglebius#else
311290001Sglebius# warning TIME_OOP is not defined
312290001Sglebius#endif
313290001Sglebius#ifdef TIME_WAIT
314290001Sglebius	    case TIME_WAIT: /* 4: leap second has occured */
315290001Sglebius		msyslog(LOG_INFO, "kernel reports leap second has occurred");
316290001Sglebius	    break;
317290001Sglebius#else
318290001Sglebius# warning TIME_WAIT is not defined
319290001Sglebius#endif
320290001Sglebius#ifdef TIME_ERROR
321290001Sglebius#if 0
322290001Sglebius
323290001Sglebiusfrom the reference implementation of ntp_gettime():
324290001Sglebius
325290001Sglebius		// Hardware or software error
326290001Sglebius        if ((time_status & (STA_UNSYNC | STA_CLOCKERR))
327290001Sglebius
328290001Sglebius	/*
329290001Sglebius         * PPS signal lost when either time or frequency synchronization
330290001Sglebius         * requested
331290001Sglebius         */
332290001Sglebius	|| (time_status & (STA_PPSFREQ | STA_PPSTIME)
333290001Sglebius	    && !(time_status & STA_PPSSIGNAL))
334290001Sglebius
335290001Sglebius        /*
336290001Sglebius         * PPS jitter exceeded when time synchronization requested
337290001Sglebius         */
338290001Sglebius	|| (time_status & STA_PPSTIME &&
339290001Sglebius            time_status & STA_PPSJITTER)
340290001Sglebius
341290001Sglebius        /*
342290001Sglebius         * PPS wander exceeded or calibration error when frequency
343290001Sglebius         * synchronization requested
344290001Sglebius         */
345290001Sglebius	|| (time_status & STA_PPSFREQ &&
346290001Sglebius            time_status & (STA_PPSWANDER | STA_PPSERROR)))
347290001Sglebius                return (TIME_ERROR);
348290001Sglebius
349290001Sglebiusor, from ntp_adjtime():
350290001Sglebius
351290001Sglebius	if (  (time_status & (STA_UNSYNC | STA_CLOCKERR))
352290001Sglebius	    || (time_status & (STA_PPSFREQ | STA_PPSTIME)
353290001Sglebius		&& !(time_status & STA_PPSSIGNAL))
354290001Sglebius	    || (time_status & STA_PPSTIME
355290001Sglebius		&& time_status & STA_PPSJITTER)
356290001Sglebius	    || (time_status & STA_PPSFREQ
357290001Sglebius		&& time_status & (STA_PPSWANDER | STA_PPSERROR))
358290001Sglebius	   )
359290001Sglebius		return (TIME_ERROR);
360290001Sglebius#endif
361290001Sglebius
362290001Sglebius	    case TIME_ERROR: /* 5: unsynchronized, or loss of synchronization */
363290001Sglebius				/* error (see status word) */
364290001Sglebius
365290001Sglebius		if (ptimex->status & STA_UNSYNC)
366290001Sglebius			snprintf(des, sizeof(des), "%s%sClock Unsynchronized",
367290001Sglebius				des, (*des) ? "; " : "");
368290001Sglebius
369290001Sglebius		if (ptimex->status & STA_CLOCKERR)
370290001Sglebius			snprintf(des, sizeof(des), "%s%sClock Error",
371290001Sglebius				des, (*des) ? "; " : "");
372290001Sglebius
373290001Sglebius		if (!(ptimex->status & STA_PPSSIGNAL)
374290001Sglebius		    && ptimex->status & STA_PPSFREQ)
375290001Sglebius			snprintf(des, sizeof(des), "%s%sPPS Frequency Sync wanted but no PPS",
376290001Sglebius				des, (*des) ? "; " : "");
377290001Sglebius
378290001Sglebius		if (!(ptimex->status & STA_PPSSIGNAL)
379290001Sglebius		    && ptimex->status & STA_PPSTIME)
380290001Sglebius			snprintf(des, sizeof(des), "%s%sPPS Time Sync wanted but no PPS signal",
381290001Sglebius				des, (*des) ? "; " : "");
382290001Sglebius
383290001Sglebius		if (   ptimex->status & STA_PPSTIME
384290001Sglebius		    && ptimex->status & STA_PPSJITTER)
385290001Sglebius			snprintf(des, sizeof(des), "%s%sPPS Time Sync wanted but PPS Jitter exceeded",
386290001Sglebius				des, (*des) ? "; " : "");
387290001Sglebius
388290001Sglebius		if (   ptimex->status & STA_PPSFREQ
389290001Sglebius		    && ptimex->status & STA_PPSWANDER)
390290001Sglebius			snprintf(des, sizeof(des), "%s%sPPS Frequency Sync wanted but PPS Wander exceeded",
391290001Sglebius				des, (*des) ? "; " : "");
392290001Sglebius
393290001Sglebius		if (   ptimex->status & STA_PPSFREQ
394290001Sglebius		    && ptimex->status & STA_PPSERROR)
395290001Sglebius			snprintf(des, sizeof(des), "%s%sPPS Frequency Sync wanted but Calibration error detected",
396290001Sglebius				des, (*des) ? "; " : "");
397290001Sglebius
398290001Sglebius		if (pps_call && !(ptimex->status & STA_PPSSIGNAL))
399290001Sglebius			report_event(EVNT_KERN, NULL,
400290001Sglebius			    "no PPS signal");
401290001Sglebius		DPRINTF(1, ("kernel loop status %#x (%s)\n",
402290001Sglebius			ptimex->status, des));
403290001Sglebius		/*
404290001Sglebius		 * This code may be returned when ntp_adjtime() has just
405290001Sglebius		 * been called for the first time, quite a while after
406290001Sglebius		 * startup, when ntpd just starts to discipline the kernel
407290001Sglebius		 * time. In this case the occurrence of this message
408290001Sglebius		 * can be pretty confusing.
409290001Sglebius		 *
410290001Sglebius		 * HMS: How about a message when we begin kernel processing:
411290001Sglebius		 *    Determining kernel clock state...
412290001Sglebius		 * so an initial TIME_ERROR message is less confising,
413290001Sglebius		 * or skipping the first message (ugh),
414290001Sglebius		 * or ???
415290001Sglebius		 * msyslog(LOG_INFO, "kernel reports time synchronization lost");
416290001Sglebius		 */
417290001Sglebius		msyslog(LOG_INFO, "kernel reports TIME_ERROR: %#x: %s",
418290001Sglebius			ptimex->status, des);
419290001Sglebius	    break;
420290001Sglebius#else
421290001Sglebius# warning TIME_ERROR is not defined
422290001Sglebius#endif
423290001Sglebius	    default:
424290001Sglebius		msyslog(LOG_NOTICE, "%s: %s line %d: unhandled return value %d from ntp_adjtime() in %s at line %d",
425290001Sglebius		    caller, file_name(), line,
426290001Sglebius		    ret,
427290001Sglebius		    __func__, __LINE__
428290001Sglebius		);
429290001Sglebius	    break;
430290001Sglebius	}
431290001Sglebius	return;
432290001Sglebius}
433290001Sglebius#endif
434290001Sglebius
435290001Sglebius/*
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
445290001Sglebius * 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 */
454290001Sglebius	int	osys_poll;	/* old system poll */
455290001Sglebius	int	ntp_adj_ret;	/* returned by ntp_adjtime */
456290001Sglebius	double	mu;		/* interval since last update */
457290001Sglebius	double	clock_frequency; /* clock frequency */
458182007Sroberto	double	dtemp, etemp;	/* double temps */
459290001Sglebius	char	tbuf[80];	/* report buffer */
46054359Sroberto
461293896Sglebius	(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	 */
467293896Sglebius#ifndef LOCKCLOCK
468293896Sglebius	if (!ntp_enable)
469293896Sglebius#endif /* not LOCKCLOCK */
470290001Sglebius	{
471182007Sroberto		record_loop_stats(fp_offset, drift_comp, clock_jitter,
47282498Sroberto		    clock_stability, sys_poll);
47382498Sroberto		return (0);
47482498Sroberto	}
47554359Sroberto
476290001Sglebius#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) {
488290001Sglebius		snprintf(tbuf, sizeof(tbuf),
489290001Sglebius		    "%+.0f s; set clock manually within %.0f s.",
49082498Sroberto		    fp_offset, clock_panic);
491290001Sglebius		report_event(EVNT_SYSFAULT, NULL, tbuf);
49254359Sroberto		return (-1);
49354359Sroberto	}
49482498Sroberto
495293896Sglebius	allow_panic = FALSE;
496293896Sglebius
49754359Sroberto	/*
498290001Sglebius	 * This section simulates ntpdate. If the offset exceeds the
499290001Sglebius	 * step threshold (128 ms), step the clock to that time and
500290001Sglebius	 * exit. Otherwise, slew the clock to that time and exit. Note
501290001Sglebius	 * that the slew will persist and eventually complete beyond the
502290001Sglebius	 * life of this program. Note that while ntpdate is active, the
503290001Sglebius	 * terminal does not detach, so the termination message prints
504290001Sglebius	 * directly to the terminal.
50582498Sroberto	 */
50682498Sroberto	if (mode_ntpdate) {
507290001Sglebius		if (  ( fp_offset > clock_max_fwd  && clock_max_fwd  > 0)
508290001Sglebius		   || (-fp_offset > clock_max_back && clock_max_back > 0)) {
50982498Sroberto			step_systime(fp_offset);
510290001Sglebius			msyslog(LOG_NOTICE, "ntpd: time set %+.6f s",
511290001Sglebius			    fp_offset);
512132451Sroberto			printf("ntpd: time set %+.6fs\n", fp_offset);
51382498Sroberto		} else {
51482498Sroberto			adj_systime(fp_offset);
515290001Sglebius			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	 */
532290001Sglebius	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;
542293896Sglebius		DPRINTF(1, ("local_clock: size %d mindly %.6f huffpuff %.6f\n",
543293896Sglebius			    sys_hufflen, sys_mindly, dtemp));
54482498Sroberto	}
54582498Sroberto
54682498Sroberto	/*
547290001Sglebius	 * Clock state machine transition function which defines how the
548290001Sglebius	 * system reacts to large phase and frequency excursion. There
549290001Sglebius	 * are two main regimes: when the offset exceeds the step
550290001Sglebius	 * threshold (128 ms) and when it does not. Under certain
551290001Sglebius	 * conditions updates are suspended until the stepout theshold
552290001Sglebius	 * (900 s) is exceeded. See the documentation on how these
553290001Sglebius	 * thresholds interact with commands and command line options.
554182007Sroberto	 *
555290001Sglebius	 * Note the kernel is disabled if step is disabled or greater
556290001Sglebius	 * than 0.5 s or in ntpdate mode.
55754359Sroberto	 */
558290001Sglebius	osys_poll = sys_poll;
559290001Sglebius	if (sys_poll < peer->minpoll)
560290001Sglebius		sys_poll = peer->minpoll;
561290001Sglebius	if (sys_poll > peer->maxpoll)
562290001Sglebius		sys_poll = peer->maxpoll;
563290001Sglebius	mu = current_time - clock_epoch;
564290001Sglebius	clock_frequency = drift_comp;
565182007Sroberto	rval = 1;
566290001Sglebius	if (  ( fp_offset > clock_max_fwd  && clock_max_fwd  > 0)
567290001Sglebius	   || (-fp_offset > clock_max_back && clock_max_back > 0)
568290001Sglebius	   || force_step_once ) {
569290001Sglebius		if (force_step_once) {
570290001Sglebius			force_step_once = FALSE;  /* we want this only once after startup */
571290001Sglebius			msyslog(LOG_NOTICE, "Doing intital time step" );
572290001Sglebius		}
573290001Sglebius
57454359Sroberto		switch (state) {
57554359Sroberto
57654359Sroberto		/*
577290001Sglebius		 * In SYNC state we ignore the first outlier and switch
578290001Sglebius		 * to SPIK state.
57954359Sroberto		 */
580290001Sglebius		case EVNT_SYNC:
581290001Sglebius			snprintf(tbuf, sizeof(tbuf), "%+.6f s",
582290001Sglebius			    fp_offset);
583290001Sglebius			report_event(EVNT_SPIK, NULL, tbuf);
584290001Sglebius			state = EVNT_SPIK;
58554359Sroberto			return (0);
58654359Sroberto
58754359Sroberto		/*
588290001Sglebius		 * In FREQ state we ignore outliers and inlyers. At the
589290001Sglebius		 * first outlier after the stepout threshold, compute
590290001Sglebius		 * the apparent frequency correction and step the phase.
59154359Sroberto		 */
592290001Sglebius		case EVNT_FREQ:
59382498Sroberto			if (mu < clock_minstep)
59454359Sroberto				return (0);
595182007Sroberto
596290001Sglebius			clock_frequency = direct_freq(fp_offset);
597182007Sroberto
598290001Sglebius			/* fall through to EVNT_SPIK */
59954359Sroberto
60054359Sroberto		/*
601290001Sglebius		 * In SPIK state we ignore succeeding outliers until
602182007Sroberto		 * either an inlyer is found or the stepout threshold is
603182007Sroberto		 * exceeded.
60454359Sroberto		 */
605290001Sglebius		case EVNT_SPIK:
606182007Sroberto			if (mu < clock_minstep)
607182007Sroberto				return (0);
608182007Sroberto
60954359Sroberto			/* fall through to default */
61054359Sroberto
61154359Sroberto		/*
612290001Sglebius		 * We get here by default in NSET and FSET states and
613290001Sglebius		 * from above in FREQ or SPIK states.
614182007Sroberto		 *
615290001Sglebius		 * In NSET state an initial frequency correction is not
616290001Sglebius		 * available, usually because the frequency file has not
617290001Sglebius		 * yet been written. Since the time is outside the step
618290001Sglebius		 * threshold, the clock is stepped. The frequency will
619290001Sglebius		 * be set directly following the stepout interval.
620182007Sroberto		 *
621290001Sglebius		 * In FSET state the initial frequency has been set from
622290001Sglebius		 * the frequency file. Since the time is outside the
623290001Sglebius		 * step threshold, the clock is stepped immediately,
624182007Sroberto		 * rather than after the stepout interval. Guys get
625290001Sglebius		 * nervous if it takes 15 minutes to set the clock for
626182007Sroberto		 * the first time.
627182007Sroberto		 *
628290001Sglebius		 * 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
631290001Sglebius		 * step threshold is always suppressed, even with a
632290001Sglebius		 * long time constant.
633290001Sglebius		 */
63454359Sroberto		default:
635290001Sglebius			snprintf(tbuf, sizeof(tbuf), "%+.6f s",
636290001Sglebius			    fp_offset);
637290001Sglebius			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;
643290001Sglebius			if (state == EVNT_NSET) {
644290001Sglebius				rstclock(EVNT_FREQ, 0);
645182007Sroberto				return (rval);
646182007Sroberto			}
64754359Sroberto			break;
64854359Sroberto		}
649290001Sglebius		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.
655290001Sglebius		 */
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		/*
664290001Sglebius		 * 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		 */
669290001Sglebius		case EVNT_NSET:
670290001Sglebius			adj_systime(fp_offset);
671290001Sglebius			rstclock(EVNT_FREQ, fp_offset);
672182007Sroberto			break;
673182007Sroberto
674182007Sroberto		/*
675290001Sglebius		 * In FREQ state ignore updates until the stepout
676290001Sglebius		 * threshold. After that, compute the new frequency, but
677290001Sglebius		 * do not adjust the frequency until the holdoff counter
678290001Sglebius		 * decrements to zero.
67954359Sroberto		 */
680290001Sglebius		case EVNT_FREQ:
68182498Sroberto			if (mu < clock_minstep)
68254359Sroberto				return (0);
683182007Sroberto
684290001Sglebius			clock_frequency = direct_freq(fp_offset);
685290001Sglebius			/* fall through */
68654359Sroberto
68754359Sroberto		/*
688290001Sglebius		 * We get here by default in FSET, SPIK and SYNC states.
689290001Sglebius		 * Here compute the frequency update due to PLL and FLL
690290001Sglebius		 * contributions. Note, we avoid frequency discipline at
691290001Sglebius		 * startup until the initial transient has subsided.
69254359Sroberto		 */
69354359Sroberto		default:
694290001Sglebius			if (freq_cnt == 0) {
69554359Sroberto
696290001Sglebius				/*
697290001Sglebius				 * The FLL and PLL frequency gain constants
698290001Sglebius				 * depend on the time constant and Allan
699290001Sglebius				 * intercept. The PLL is always used, but
700290001Sglebius				 * becomes ineffective above the Allan intercept
701290001Sglebius				 * where the FLL becomes effective.
702290001Sglebius				 */
703290001Sglebius				if (sys_poll >= allan_xpt)
704310419Sdelphij					clock_frequency +=
705310419Sdelphij					      (fp_offset - clock_offset)
706310419Sdelphij					    / ( max(ULOGTOD(sys_poll), mu)
707310419Sdelphij					       * CLOCK_FLL);
708290001Sglebius
709290001Sglebius				/*
710290001Sglebius				 * The PLL frequency gain (numerator) depends on
711290001Sglebius				 * the minimum of the update interval and Allan
712290001Sglebius				 * intercept. This reduces the PLL gain when the
713290001Sglebius				 * FLL becomes effective.
714290001Sglebius				 */
715290001Sglebius				etemp = min(ULOGTOD(allan_xpt), mu);
716290001Sglebius				dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll);
717310419Sdelphij				clock_frequency +=
718310419Sdelphij				    fp_offset * etemp / (dtemp * dtemp);
719132451Sroberto			}
720290001Sglebius			rstclock(EVNT_SYNC, fp_offset);
721290001Sglebius			if (fabs(fp_offset) < CLOCK_FLOOR)
722290001Sglebius				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	 */
743290001Sglebius	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		 */
756290001Sglebius		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 */
778290001Sglebius			if (ntv.constant < 0)
779290001Sglebius				ntv.constant = 0;
780182007Sroberto
781182007Sroberto			ntv.esterror = (u_int32)(clock_jitter * 1e6);
78254359Sroberto			ntv.maxerror = (u_int32)((sys_rootdelay / 2 +
783290001Sglebius			    sys_rootdisp) * 1e6);
78454359Sroberto			ntv.status = STA_PLL;
78554359Sroberto
78654359Sroberto			/*
787290001Sglebius			 * Enable/disable the PPS if requested.
78854359Sroberto			 */
789290001Sglebius			if (hardpps_enable) {
790290001Sglebius				ntv.status |= (STA_PPSTIME | STA_PPSFREQ);
791290001Sglebius				if (!(pll_status & STA_PPSTIME))
792290001Sglebius					sync_status("PPS enabled",
793290001Sglebius						pll_status,
794290001Sglebius						ntv.status);
79582498Sroberto			} else {
796290001Sglebius				ntv.status &= ~(STA_PPSTIME | STA_PPSFREQ);
797290001Sglebius				if (pll_status & STA_PPSTIME)
798290001Sglebius					sync_status("PPS disabled",
799290001Sglebius						pll_status,
800290001Sglebius						ntv.status);
80182498Sroberto			}
802290001Sglebius			if (sys_leap == LEAP_ADDSECOND)
803290001Sglebius				ntv.status |= STA_INS;
804290001Sglebius			else if (sys_leap == LEAP_DELSECOND)
805290001Sglebius				ntv.status |= STA_DEL;
80654359Sroberto		}
80754359Sroberto
80854359Sroberto		/*
80982498Sroberto		 * Pass the stuff to the kernel. If it squeals, turn off
810290001Sglebius		 * the pps. In any case, fetch the kernel offset,
811290001Sglebius		 * frequency and jitter.
81254359Sroberto		 */
813290001Sglebius		ntp_adj_ret = ntp_adjtime(&ntv);
814290001Sglebius		/*
815290001Sglebius		 * A squeal is a return status < 0, or a state change.
816290001Sglebius		 */
817290001Sglebius		if ((0 > ntp_adj_ret) || (ntp_adj_ret != kernel_status)) {
818290001Sglebius			kernel_status = ntp_adj_ret;
819290001Sglebius			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 */
827290001Sglebius		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		}
839290001Sglebius
840290001Sglebius#if defined(STA_NANO) && NTP_API == 4
841182007Sroberto		/*
842290001Sglebius		 * If the TAI changes, update the kernel TAI.
843182007Sroberto		 */
844290001Sglebius		if (loop_tai != sys_tai) {
845290001Sglebius			loop_tai = sys_tai;
846290001Sglebius			ntv.modes = MOD_TAI;
847290001Sglebius			ntv.constant = sys_tai;
848290001Sglebius			if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
849290001Sglebius			    ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 1, __LINE__ - 1);
850290001Sglebius			}
851290001Sglebius		}
852290001Sglebius#endif /* STA_NANO */
85354359Sroberto	}
85454359Sroberto#endif /* KERNEL_PLL */
855182007Sroberto
85654359Sroberto	/*
857182007Sroberto	 * Clamp the frequency within the tolerance range and calculate
858290001Sglebius	 * the frequency difference since the last update.
85954359Sroberto	 */
860182007Sroberto	if (fabs(clock_frequency) > NTP_MAXFREQ)
861290001Sglebius		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	/*
873290001Sglebius	 * Calculate the wander as the exponentially weighted RMS
874290001Sglebius	 * frequency differences. Record the change for the frequency
875290001Sglebius	 * file update.
876182007Sroberto	 */
877132451Sroberto	etemp = SQUARE(clock_stability);
878132451Sroberto	clock_stability = SQRT(etemp + (dtemp - etemp) / CLOCK_AVG);
879132451Sroberto
88054359Sroberto	/*
881290001Sglebius	 * 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
885290001Sglebius	 * helps calm the dance. Works best using burst mode. Don't
886290001Sglebius	 * fiddle with the poll during the startup clamp period.
88754359Sroberto	 */
888290001Sglebius	if (freq_cnt > 0) {
889290001Sglebius		tc_counter = 0;
890290001Sglebius	} 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	/*
911290001Sglebius	 * If the time constant has changed, update the poll variables.
912290001Sglebius	 */
913290001Sglebius	if (osys_poll != sys_poll)
914290001Sglebius		poll_update(peer, sys_poll);
915290001Sglebius
916290001Sglebius	/*
917182007Sroberto	 * Yibbidy, yibbbidy, yibbidy; that'h all folks.
91854359Sroberto	 */
919182007Sroberto	record_loop_stats(clock_offset, drift_comp, clock_jitter,
92082498Sroberto	    clock_stability, sys_poll);
921293896Sglebius	DPRINTF(1, ("local_clock: offset %.9f jit %.9f freq %.3f stab %.3f poll %d\n",
922290001Sglebius		    clock_offset, clock_jitter, drift_comp * 1e6,
923293896Sglebius		    clock_stability * 1e6, sys_poll));
924182007Sroberto	return (rval);
925293896Sglebius#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
933290001Sglebius * sys_rootdisp variable.
93454359Sroberto */
93554359Srobertovoid
93654359Srobertoadj_host_clock(
93754359Sroberto	void
93854359Sroberto	)
93954359Sroberto{
940290001Sglebius	double	offset_adj;
941290001Sglebius	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
948290001Sglebius	 * would be counterproductive. During the startup clamp period, the
949290001Sglebius	 * time constant is clamped at 2.
95054359Sroberto	 */
951290001Sglebius	sys_rootdisp += clock_phi;
952132451Sroberto#ifndef LOCKCLOCK
953290001Sglebius	if (!ntp_enable || mode_ntpdate)
954290001Sglebius		return;
95554359Sroberto	/*
956290001Sglebius	 * Determine the phase adjustment. The gain factor (denominator)
957290001Sglebius	 * increases with poll interval, so is dominated by the FLL
958290001Sglebius	 * above the Allan intercept. Note the reduced time constant at
959290001Sglebius	 * startup.
960182007Sroberto	 */
961290001Sglebius	if (state != EVNT_SYNC) {
962290001Sglebius		offset_adj = 0.;
963290001Sglebius	} else if (freq_cnt > 0) {
964290001Sglebius		offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(1));
965290001Sglebius		freq_cnt--;
966290001Sglebius#ifdef KERNEL_PLL
967290001Sglebius	} else if (pll_control && kern_enable) {
968290001Sglebius		offset_adj = 0.;
969290001Sglebius#endif /* KERNEL_PLL */
970290001Sglebius	} else {
971290001Sglebius		offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(sys_poll));
972290001Sglebius	}
973182007Sroberto
974182007Sroberto	/*
975290001Sglebius	 * If the kernel discipline is enabled the frequency correction
976290001Sglebius	 * drift_comp has already been engaged via ntp_adjtime() in
977290001Sglebius	 * set_freq().  Otherwise it is a component of the adj_systime()
978290001Sglebius	 * offset.
97954359Sroberto	 */
980290001Sglebius#ifdef KERNEL_PLL
981290001Sglebius	if (pll_control && kern_enable)
982290001Sglebius		freq_adj = 0.;
983290001Sglebius	else
984290001Sglebius#endif /* KERNEL_PLL */
985290001Sglebius		freq_adj = drift_comp;
98654359Sroberto
987290001Sglebius	/* Bound absolute value of total adjustment to NTP_MAXFREQ. */
988290001Sglebius	if (offset_adj + freq_adj > NTP_MAXFREQ)
989290001Sglebius		offset_adj = NTP_MAXFREQ - freq_adj;
990290001Sglebius	else if (offset_adj + freq_adj < -NTP_MAXFREQ)
991290001Sglebius		offset_adj = -NTP_MAXFREQ - freq_adj;
992290001Sglebius
993290001Sglebius	clock_offset -= offset_adj;
99454359Sroberto	/*
995290001Sglebius	 * Windows port adj_systime() must be called each second,
996290001Sglebius	 * even if the argument is zero, to ease emulation of
997290001Sglebius	 * adjtime() using Windows' slew API which controls the rate
998290001Sglebius	 * but does not automatically stop slewing when an offset
999290001Sglebius	 * has decayed to zero.
100054359Sroberto	 */
1001293896Sglebius	DEBUG_INSIST(enable_panic_check == TRUE);
1002293896Sglebius	enable_panic_check = FALSE;
1003290001Sglebius	adj_systime(offset_adj + freq_adj);
1004293896Sglebius	enable_panic_check = TRUE;
1005132451Sroberto#endif /* LOCKCLOCK */
100654359Sroberto}
100754359Sroberto
100854359Sroberto
100954359Sroberto/*
1010290001Sglebius * 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{
1018293896Sglebius	DPRINTF(2, ("rstclock: mu %lu state %d poll %d count %d\n",
1019290001Sglebius		    current_time - clock_epoch, trans, sys_poll,
1020293896Sglebius		    tc_counter));
1021290001Sglebius	if (trans != state && trans != EVNT_FSET)
1022290001Sglebius		report_event(trans, NULL, NULL);
1023182007Sroberto	state = trans;
1024182007Sroberto	last_offset = clock_offset = offset;
1025290001Sglebius	clock_epoch = current_time;
102682498Sroberto}
102754359Sroberto
102854359Sroberto
102982498Sroberto/*
1030290001Sglebius * calc_freq - calculate frequency directly
1031290001Sglebius *
1032290001Sglebius * This is very carefully done. When the offset is first computed at the
1033290001Sglebius * first update, a residual frequency component results. Subsequently,
1034290001Sglebius * updates are suppresed until the end of the measurement interval while
1035290001Sglebius * the offset is amortized. At the end of the interval the frequency is
1036290001Sglebius * calculated from the current offset, residual offset, length of the
1037290001Sglebius * interval and residual frequency component. At the same time the
1038290001Sglebius * frequenchy file is armed for update at the next hourly stats.
1039290001Sglebius */
1040290001Sglebiusstatic double
1041290001Sglebiusdirect_freq(
1042290001Sglebius	double	fp_offset
1043290001Sglebius	)
1044290001Sglebius{
1045290001Sglebius	set_freq(fp_offset / (current_time - clock_epoch));
1046290001Sglebius
1047290001Sglebius	return drift_comp;
1048290001Sglebius}
1049290001Sglebius
1050290001Sglebius
1051290001Sglebius/*
1052290001Sglebius * set_freq - set clock frequency correction
1053290001Sglebius *
1054290001Sglebius * Used to step the frequency correction at startup, possibly again once
1055290001Sglebius * the frequency is measured (that is, transitioning from EVNT_NSET to
1056290001Sglebius * EVNT_FSET), and finally to switch between daemon and kernel loop
1057290001Sglebius * discipline at runtime.
1058290001Sglebius *
1059290001Sglebius * When the kernel loop discipline is available but the daemon loop is
1060290001Sglebius * in use, the kernel frequency correction is disabled (set to 0) to
1061290001Sglebius * ensure drift_comp is applied by only one of the loops.
1062290001Sglebius */
1063290001Sglebiusstatic void
1064290001Sglebiusset_freq(
1065290001Sglebius	double	freq		/* frequency update */
1066290001Sglebius	)
1067290001Sglebius{
1068290001Sglebius	const char *	loop_desc;
1069290001Sglebius	int ntp_adj_ret;
1070290001Sglebius
1071293896Sglebius	(void)ntp_adj_ret; /* not always used below... */
1072290001Sglebius	drift_comp = freq;
1073290001Sglebius	loop_desc = "ntpd";
1074290001Sglebius#ifdef KERNEL_PLL
1075290001Sglebius	if (pll_control) {
1076290001Sglebius		ZERO(ntv);
1077290001Sglebius		ntv.modes = MOD_FREQUENCY;
1078290001Sglebius		if (kern_enable) {
1079290001Sglebius			loop_desc = "kernel";
1080290001Sglebius			ntv.freq = DTOFREQ(drift_comp);
1081290001Sglebius		}
1082290001Sglebius		if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
1083290001Sglebius		    ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1);
1084290001Sglebius		}
1085290001Sglebius	}
1086290001Sglebius#endif /* KERNEL_PLL */
1087290001Sglebius	mprintf_event(EVNT_FSET, NULL, "%s %.3f PPM", loop_desc,
1088290001Sglebius	    drift_comp * 1e6);
1089290001Sglebius}
1090290001Sglebius
1091290001Sglebius
1092290001Sglebius#ifdef KERNEL_PLL
1093290001Sglebiusstatic void
1094290001Sglebiusstart_kern_loop(void)
1095290001Sglebius{
1096290001Sglebius	static int atexit_done;
1097290001Sglebius	int ntp_adj_ret;
1098290001Sglebius
1099290001Sglebius	pll_control = TRUE;
1100290001Sglebius	ZERO(ntv);
1101290001Sglebius	ntv.modes = MOD_BITS;
1102290001Sglebius	ntv.status = STA_PLL;
1103290001Sglebius	ntv.maxerror = MAXDISPERSE;
1104290001Sglebius	ntv.esterror = MAXDISPERSE;
1105290001Sglebius	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? */
1106290001Sglebius#ifdef SIGSYS
1107290001Sglebius	/*
1108290001Sglebius	 * Use sigsetjmp() to save state and then call ntp_adjtime(); if
1109290001Sglebius	 * it fails, then pll_trap() will set pll_control FALSE before
1110290001Sglebius	 * returning control using siglogjmp().
1111290001Sglebius	 */
1112290001Sglebius	newsigsys.sa_handler = pll_trap;
1113290001Sglebius	newsigsys.sa_flags = 0;
1114290001Sglebius	if (sigaction(SIGSYS, &newsigsys, &sigsys)) {
1115290001Sglebius		msyslog(LOG_ERR, "sigaction() trap SIGSYS: %m");
1116290001Sglebius		pll_control = FALSE;
1117290001Sglebius	} else {
1118290001Sglebius		if (sigsetjmp(env, 1) == 0) {
1119290001Sglebius			if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
1120290001Sglebius			    ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1);
1121290001Sglebius			}
1122290001Sglebius		}
1123290001Sglebius		if (sigaction(SIGSYS, &sigsys, NULL)) {
1124290001Sglebius			msyslog(LOG_ERR,
1125290001Sglebius			    "sigaction() restore SIGSYS: %m");
1126290001Sglebius			pll_control = FALSE;
1127290001Sglebius		}
1128290001Sglebius	}
1129290001Sglebius#else /* SIGSYS */
1130290001Sglebius	if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) {
1131290001Sglebius	    ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1);
1132290001Sglebius	}
1133290001Sglebius#endif /* SIGSYS */
1134290001Sglebius
1135290001Sglebius	/*
1136290001Sglebius	 * Save the result status and light up an external clock
1137290001Sglebius	 * if available.
1138290001Sglebius	 */
1139290001Sglebius	pll_status = ntv.status;
1140290001Sglebius	if (pll_control) {
1141290001Sglebius		if (!atexit_done) {
1142290001Sglebius			atexit_done = TRUE;
1143290001Sglebius			atexit(&stop_kern_loop);
1144290001Sglebius		}
1145290001Sglebius#ifdef STA_NANO
1146290001Sglebius		if (pll_status & STA_CLK)
1147290001Sglebius			ext_enable = TRUE;
1148290001Sglebius#endif /* STA_NANO */
1149290001Sglebius		report_event(EVNT_KERN, NULL,
1150290001Sglebius	  	    "kernel time sync enabled");
1151290001Sglebius	}
1152290001Sglebius}
1153290001Sglebius#endif	/* KERNEL_PLL */
1154290001Sglebius
1155290001Sglebius
1156290001Sglebius#ifdef KERNEL_PLL
1157290001Sglebiusstatic void
1158290001Sglebiusstop_kern_loop(void)
1159290001Sglebius{
1160290001Sglebius	if (pll_control && kern_enable)
1161290001Sglebius		report_event(EVNT_KERN, NULL,
1162290001Sglebius		    "kernel time sync disabled");
1163290001Sglebius}
1164290001Sglebius#endif	/* KERNEL_PLL */
1165290001Sglebius
1166290001Sglebius
1167290001Sglebius/*
1168290001Sglebius * select_loop() - choose kernel or daemon loop discipline.
1169290001Sglebius */
1170290001Sglebiusvoid
1171290001Sglebiusselect_loop(
1172290001Sglebius	int	use_kern_loop
1173290001Sglebius	)
1174290001Sglebius{
1175290001Sglebius	if (kern_enable == use_kern_loop)
1176290001Sglebius		return;
1177290001Sglebius#ifdef KERNEL_PLL
1178290001Sglebius	if (pll_control && !use_kern_loop)
1179290001Sglebius		stop_kern_loop();
1180290001Sglebius#endif
1181290001Sglebius	kern_enable = use_kern_loop;
1182290001Sglebius#ifdef KERNEL_PLL
1183290001Sglebius	if (pll_control && use_kern_loop)
1184290001Sglebius		start_kern_loop();
1185290001Sglebius#endif
1186290001Sglebius	/*
1187290001Sglebius	 * If this loop selection change occurs after initial startup,
1188290001Sglebius	 * call set_freq() to switch the frequency compensation to or
1189290001Sglebius	 * from the kernel loop.
1190290001Sglebius	 */
1191290001Sglebius#ifdef KERNEL_PLL
1192290001Sglebius	if (pll_control && loop_started)
1193290001Sglebius		set_freq(drift_comp);
1194290001Sglebius#endif
1195290001Sglebius}
1196290001Sglebius
1197290001Sglebius
1198290001Sglebius/*
119982498Sroberto * huff-n'-puff filter
120082498Sroberto */
120182498Srobertovoid
1202290001Sglebiushuffpuff(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(
1226290001Sglebius	int	item,
1227290001Sglebius	double	freq
122854359Sroberto	)
122954359Sroberto{
1230290001Sglebius	int	i;
1231290001Sglebius	double	ftemp;
123254359Sroberto
1233293896Sglebius	DPRINTF(2, ("loop_config: item %d freq %f\n", item, freq));
123454359Sroberto	switch (item) {
123554359Sroberto
1236290001Sglebius	/*
1237290001Sglebius	 * We first assume the kernel supports the ntp_adjtime()
1238290001Sglebius	 * syscall. If that syscall works, initialize the kernel time
1239290001Sglebius	 * variables. Otherwise, continue leaving no harm behind.
1240290001Sglebius	 */
124182498Sroberto	case LOOP_DRIFTINIT:
1242132451Sroberto#ifndef LOCKCLOCK
124354359Sroberto#ifdef KERNEL_PLL
1244182007Sroberto		if (mode_ntpdate)
1245132451Sroberto			break;
1246132451Sroberto
1247290001Sglebius		start_kern_loop();
124882498Sroberto#endif /* KERNEL_PLL */
124954359Sroberto
125082498Sroberto		/*
1251290001Sglebius		 * Initialize frequency if given; otherwise, begin frequency
1252290001Sglebius		 * calibration phase.
125382498Sroberto		 */
1254290001Sglebius		ftemp = init_drift_comp / 1e6;
1255290001Sglebius		if (ftemp > NTP_MAXFREQ)
1256290001Sglebius			ftemp = NTP_MAXFREQ;
1257290001Sglebius		else if (ftemp < -NTP_MAXFREQ)
1258290001Sglebius			ftemp = -NTP_MAXFREQ;
1259290001Sglebius		set_freq(ftemp);
1260290001Sglebius		if (freq_set)
1261290001Sglebius			rstclock(EVNT_FSET, 0);
1262290001Sglebius		else
1263290001Sglebius			rstclock(EVNT_NSET, 0);
1264290001Sglebius		loop_started = TRUE;
1265132451Sroberto#endif /* LOCKCLOCK */
126682498Sroberto		break;
126782498Sroberto
1268182007Sroberto	case LOOP_KERN_CLEAR:
1269290001Sglebius#if 0		/* XXX: needs more review, and how can we get here? */
1270182007Sroberto#ifndef LOCKCLOCK
1271290001Sglebius# ifdef KERNEL_PLL
1272290001Sglebius		if (pll_control && kern_enable) {
1273182007Sroberto			memset((char *)&ntv, 0, sizeof(ntv));
1274290001Sglebius			ntv.modes = MOD_STATUS;
1275182007Sroberto			ntv.status = STA_UNSYNC;
1276182007Sroberto			ntp_adjtime(&ntv);
1277290001Sglebius			sync_status("kernel time sync disabled",
1278290001Sglebius				pll_status,
1279290001Sglebius				ntv.status);
1280182007Sroberto		   }
1281290001Sglebius# endif /* KERNEL_PLL */
1282182007Sroberto#endif /* LOCKCLOCK */
1283290001Sglebius#endif
1284182007Sroberto		break;
1285182007Sroberto
128682498Sroberto	/*
1287290001Sglebius	 * Tinker command variables for Ulrich Windl. Very dangerous.
128882498Sroberto	 */
1289290001Sglebius	case LOOP_ALLAN:	/* Allan intercept (log2) (allan) */
1290290001Sglebius		allan_xpt = (u_char)freq;
129182498Sroberto		break;
129282498Sroberto
1293290001Sglebius	case LOOP_CODEC:	/* audio codec frequency (codec) */
1294290001Sglebius		clock_codec = freq / 1e6;
129582498Sroberto		break;
129682498Sroberto
1297290001Sglebius	case LOOP_PHI:		/* dispersion threshold (dispersion) */
1298290001Sglebius		clock_phi = freq / 1e6;
129982498Sroberto		break;
130082498Sroberto
1301290001Sglebius	case LOOP_FREQ:		/* initial frequency (freq) */
1302290001Sglebius		init_drift_comp = freq;
1303290001Sglebius		freq_set++;
130482498Sroberto		break;
130582498Sroberto
1306290001Sglebius	case LOOP_HUFFPUFF:	/* huff-n'-puff length (huffpuff) */
130782498Sroberto		if (freq < HUFFPUFF)
130882498Sroberto			freq = HUFFPUFF;
130982498Sroberto		sys_hufflen = (int)(freq / HUFFPUFF);
1310290001Sglebius		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
1317290001Sglebius	case LOOP_PANIC:	/* panic threshold (panic) */
1318290001Sglebius		clock_panic = freq;
1319132451Sroberto		break;
1320290001Sglebius
1321290001Sglebius	case LOOP_MAX:		/* step threshold (step) */
1322290001Sglebius		clock_max_fwd = clock_max_back = freq;
1323290001Sglebius		if (freq == 0 || freq > 0.5)
1324290001Sglebius			select_loop(FALSE);
1325290001Sglebius		break;
1326290001Sglebius
1327290001Sglebius	case LOOP_MAX_BACK:	/* step threshold (step) */
1328290001Sglebius		clock_max_back = freq;
1329290001Sglebius		/*
1330290001Sglebius		 * Leave using the kernel discipline code unless both
1331290001Sglebius		 * limits are massive.  This assumes the reason to stop
1332290001Sglebius		 * using it is that it's pointless, not that it goes wrong.
1333290001Sglebius		 */
1334290001Sglebius		if (  (clock_max_back == 0 || clock_max_back > 0.5)
1335290001Sglebius		   || (clock_max_fwd  == 0 || clock_max_fwd  > 0.5))
1336290001Sglebius			select_loop(FALSE);
1337290001Sglebius		break;
1338290001Sglebius
1339290001Sglebius	case LOOP_MAX_FWD:	/* step threshold (step) */
1340290001Sglebius		clock_max_fwd = freq;
1341290001Sglebius		if (  (clock_max_back == 0 || clock_max_back > 0.5)
1342290001Sglebius		   || (clock_max_fwd  == 0 || clock_max_fwd  > 0.5))
1343290001Sglebius			select_loop(FALSE);
1344290001Sglebius		break;
1345290001Sglebius
1346290001Sglebius	case LOOP_MINSTEP:	/* stepout threshold (stepout) */
1347290001Sglebius		if (freq < CLOCK_MINSTEP)
1348290001Sglebius			clock_minstep = CLOCK_MINSTEP;
1349290001Sglebius		else
1350290001Sglebius			clock_minstep = freq;
1351290001Sglebius		break;
1352290001Sglebius
1353290001Sglebius	case LOOP_TICK:		/* tick increment (tick) */
1354290001Sglebius		set_sys_tick_precision(freq);
1355290001Sglebius		break;
1356290001Sglebius
1357290001Sglebius	case LOOP_LEAP:		/* not used, fall through */
1358290001Sglebius	default:
1359290001Sglebius		msyslog(LOG_NOTICE,
1360290001Sglebius		    "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{
1379290001Sglebius	pll_control = FALSE;
138054359Sroberto	siglongjmp(env, 1);
138154359Sroberto}
138254359Sroberto#endif /* KERNEL_PLL && SIGSYS */
1383