kern_ntptime.c revision 21101
133965Sjdp/******************************************************************************
278828Sobrien *                                                                            *
378828Sobrien * Copyright (c) David L. Mills 1993, 1994                                    *
4213274Srpaulo *                                                                            *
5213274Srpaulo * Permission to use, copy, modify, and distribute this software and its      *
633965Sjdp * documentation for any purpose and without fee is hereby granted, provided  *
778828Sobrien * that the above copyright notice appears in all copies and that both the    *
878828Sobrien * copyright notice and this permission notice appear in supporting           *
978828Sobrien * documentation, and that the name University of Delaware not be used in     *
1078828Sobrien * advertising or publicity pertaining to distribution of the software        *
1178828Sobrien * without specific, written prior permission.  The University of Delaware    *
1278828Sobrien * makes no representations about the suitability this software for any       *
1378828Sobrien * purpose.  It is provided "as is" without express or implied warranty.      *
1478828Sobrien *                                                                            *
1578828Sobrien ******************************************************************************/
1678828Sobrien
1778828Sobrien/*
1878828Sobrien * Modification history kern_ntptime.c
19213274Srpaulo *
2078828Sobrien * 24 Sep 94	David L. Mills
2133965Sjdp *	Tightened code at exits.
2233965Sjdp *
2333965Sjdp * 24 Mar 94	David L. Mills
2433965Sjdp *	Revised syscall interface to include new variables for PPS
2533965Sjdp *	time discipline.
2633965Sjdp *
2733965Sjdp * 14 Feb 94	David L. Mills
2833965Sjdp *	Added code for external clock
2933965Sjdp *
3033965Sjdp * 28 Nov 93	David L. Mills
3133965Sjdp *	Revised frequency scaling to conform with adjusted parameters
3233965Sjdp *
3333965Sjdp * 17 Sep 93	David L. Mills
3433965Sjdp *	Created file
3533965Sjdp */
3633965Sjdp/*
3733965Sjdp * ntp_gettime(), ntp_adjtime() - precision time interface for SunOS
3833965Sjdp * V4.1.1 and V4.1.3
3933965Sjdp *
4033965Sjdp * These routines consitute the Network Time Protocol (NTP) interfaces
4133965Sjdp * for user and daemon application programs. The ntp_gettime() routine
4233965Sjdp * provides the time, maximum error (synch distance) and estimated error
4333965Sjdp * (dispersion) to client user application programs. The ntp_adjtime()
4433965Sjdp * routine is used by the NTP daemon to adjust the system clock to an
4533965Sjdp * externally derived time. The time offset and related variables set by
4633965Sjdp * this routine are used by hardclock() to adjust the phase and
4733965Sjdp * frequency of the phase-lock loop which controls the system clock.
4833965Sjdp */
4933965Sjdp#include <sys/param.h>
5033965Sjdp#include <sys/systm.h>
5133965Sjdp#include <sys/sysproto.h>
5233965Sjdp#include <sys/kernel.h>
5333965Sjdp#include <sys/proc.h>
5433965Sjdp#include <sys/timex.h>
5533965Sjdp#include <sys/sysctl.h>
5633965Sjdp
5733965Sjdp/*
5833965Sjdp * The following variables are used by the hardclock() routine in the
5933965Sjdp * kern_clock.c module and are described in that module.
6033965Sjdp */
61213274Srpauloextern int time_state;		/* clock state */
62213274Srpauloextern int time_status;		/* clock status bits */
6333965Sjdpextern long time_offset;	/* time adjustment (us) */
6433965Sjdpextern long time_freq;		/* frequency offset (scaled ppm) */
6533965Sjdpextern long time_maxerror;	/* maximum error (us) */
6633965Sjdpextern long time_esterror;	/* estimated error (us) */
67213274Srpauloextern long time_constant;	/* pll time constant */
68213274Srpauloextern long time_precision;	/* clock precision (us) */
69213274Srpauloextern long time_tolerance;	/* frequency tolerance (scaled ppm) */
70213274Srpaulo
71213274Srpaulo#ifdef PPS_SYNC
72213274Srpaulo/*
73213274Srpaulo * The following variables are used only if the PPS signal discipline
7489857Sobrien * is configured in the kernel.
7533965Sjdp */
7633965Sjdpextern int pps_shift;		/* interval duration (s) (shift) */
7733965Sjdpextern long pps_freq;		/* pps frequency offset (scaled ppm) */
7833965Sjdpextern long pps_jitter;		/* pps jitter (us) */
7933965Sjdpextern long pps_stabil;		/* pps stability (scaled ppm) */
8033965Sjdpextern long pps_jitcnt;		/* jitter limit exceeded */
8133965Sjdpextern long pps_calcnt;		/* calibration intervals */
8277298Sobrienextern long pps_errcnt;		/* calibration errors */
8333965Sjdpextern long pps_stbcnt;		/* stability limit exceeded */
8433965Sjdp#endif /* PPS_SYNC */
8533965Sjdp
8633965Sjdpstatic int
8789857Sobrienntp_sysctl SYSCTL_HANDLER_ARGS
8889857Sobrien{
8989857Sobrien	struct timeval atv;
9089857Sobrien	struct ntptimeval ntv;
9189857Sobrien	int s;
9289857Sobrien
9389857Sobrien	s = splclock();
9489857Sobrien#ifdef EXT_CLOCK
9589857Sobrien	/*
96213274Srpaulo	 * The microtime() external clock routine returns a
97213274Srpaulo	 * status code. If less than zero, we declare an error
9833965Sjdp	 * in the clock status word and return the kernel
9933965Sjdp	 * (software) time variable. While there are other
10033965Sjdp	 * places that call microtime(), this is the only place
10133965Sjdp	 * that matters from an application point of view.
10233965Sjdp	 */
10333965Sjdp	if (microtime(&atv) < 0) {
10433965Sjdp		time_status |= STA_CLOCKERR;
10533965Sjdp		ntv.time = time;
10633965Sjdp	} else {
10733965Sjdp		time_status &= ~STA_CLOCKERR;
10833965Sjdp	}
109213274Srpaulo#else /* EXT_CLOCK */
11033965Sjdp	microtime(&atv);
11189857Sobrien#endif /* EXT_CLOCK */
11233965Sjdp	ntv.time = atv;
11333965Sjdp	ntv.maxerror = time_maxerror;
11433965Sjdp	ntv.esterror = time_esterror;
11533965Sjdp	splx(s);
11633965Sjdp
117213274Srpaulo	ntv.time_state = time_state;
118213274Srpaulo
119213274Srpaulo	/*
120213274Srpaulo	 * Status word error decode. If any of these conditions
121213274Srpaulo	 * occur, an error is returned, instead of the status
122213274Srpaulo	 * word. Most applications will care only about the fact
123213274Srpaulo	 * the system clock may not be trusted, not about the
124213274Srpaulo	 * details.
125213274Srpaulo	 *
126213274Srpaulo	 * Hardware or software error
127213274Srpaulo	 */
128213274Srpaulo	if (time_status & (STA_UNSYNC | STA_CLOCKERR)) {
129213274Srpaulo		ntv.time_state = TIME_ERROR;
130213274Srpaulo	}
131213274Srpaulo
132213274Srpaulo	/*
13333965Sjdp	 * PPS signal lost when either time or frequency
13433965Sjdp	 * synchronization requested
13560484Sobrien	 */
13633965Sjdp	if (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
13733965Sjdp	    !(time_status & STA_PPSSIGNAL)) {
13833965Sjdp		ntv.time_state = TIME_ERROR;
13960484Sobrien	}
14060484Sobrien
14160484Sobrien	/*
14260484Sobrien	 * PPS jitter exceeded when time synchronization
14333965Sjdp	 * requested
14433965Sjdp	 */
14533965Sjdp	if (time_status & STA_PPSTIME &&
14677298Sobrien	    time_status & STA_PPSJITTER) {
14777298Sobrien		ntv.time_state = TIME_ERROR;
14877298Sobrien	}
14933965Sjdp
15033965Sjdp	/*
15133965Sjdp	 * PPS wander exceeded or calibration error when
152213274Srpaulo	 * frequency synchronization requested
153213274Srpaulo	 */
154213274Srpaulo	if (time_status & STA_PPSFREQ &&
155213274Srpaulo	    time_status & (STA_PPSWANDER | STA_PPSERROR)) {
156213274Srpaulo		ntv.time_state = TIME_ERROR;
157213274Srpaulo	}
158213274Srpaulo	return (sysctl_handle_opaque(oidp, &ntv, sizeof ntv, req));
159213274Srpaulo}
160213274Srpaulo
161213274SrpauloSYSCTL_NODE(_kern, KERN_NTP_PLL, ntp_pll, CTLFLAG_RW, 0,
162213274Srpaulo	"NTP kernel PLL related stuff");
163213274SrpauloSYSCTL_PROC(_kern_ntp_pll, NTP_PLL_GETTIME, gettime, CTLTYPE_OPAQUE|CTLFLAG_RD,
164213274Srpaulo	0, sizeof(struct ntptimeval) , ntp_sysctl, "S,ntptimeval", "");
165213274Srpaulo
166213274Srpaulo/*
167213274Srpaulo * ntp_adjtime() - NTP daemon application interface
168213274Srpaulo */
169213274Srpaulo#ifndef _SYS_SYSPROTO_H_
170213274Srpaulostruct ntp_adjtime_args {
171213274Srpaulo  struct timex *tp;
172213274Srpaulo};
173213274Srpaulo#endif
17433965Sjdp
17533965Sjdpint
17633965Sjdpntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap, int *retval)
17733965Sjdp{
17833965Sjdp	struct timex ntv;
17933965Sjdp	int modes;
18033965Sjdp	int s;
18133965Sjdp	int error;
18233965Sjdp
18333965Sjdp	error = copyin((caddr_t)uap->tp, (caddr_t)&ntv, sizeof(ntv));
18433965Sjdp	if (error)
18533965Sjdp		return error;
18633965Sjdp
18733965Sjdp	/*
18833965Sjdp	 * Update selected clock variables - only the superuser can
18933965Sjdp	 * change anything. Note that there is no error checking here on
19033965Sjdp	 * the assumption the superuser should know what it is doing.
19133965Sjdp	 */
19233965Sjdp	modes = ntv.modes;
19333965Sjdp	if ((modes != 0)
19433965Sjdp	    && (error = suser(p->p_cred->pc_ucred, &p->p_acflag)))
19533965Sjdp		return error;
19633965Sjdp
19733965Sjdp	s = splclock();
19833965Sjdp	if (modes & MOD_FREQUENCY)
199213274Srpaulo#ifdef PPS_SYNC
20033965Sjdp		time_freq = ntv.freq - pps_freq;
20133965Sjdp#else /* PPS_SYNC */
20233965Sjdp		time_freq = ntv.freq;
20333965Sjdp#endif /* PPS_SYNC */
20433965Sjdp	if (modes & MOD_MAXERROR)
20533965Sjdp		time_maxerror = ntv.maxerror;
20633965Sjdp	if (modes & MOD_ESTERROR)
20733965Sjdp		time_esterror = ntv.esterror;
20833965Sjdp	if (modes & MOD_STATUS) {
20933965Sjdp		time_status &= STA_RONLY;
21033965Sjdp		time_status |= ntv.status & ~STA_RONLY;
21133965Sjdp	}
21233965Sjdp	if (modes & MOD_TIMECONST)
21333965Sjdp		time_constant = ntv.constant;
21433965Sjdp	if (modes & MOD_OFFSET)
21533965Sjdp		hardupdate(ntv.offset);
21633965Sjdp
21733965Sjdp	/*
21833965Sjdp	 * Retrieve all clock variables
21933965Sjdp	 */
22033965Sjdp	if (time_offset < 0)
22133965Sjdp		ntv.offset = -(-time_offset >> SHIFT_UPDATE);
22233965Sjdp	else
22333965Sjdp		ntv.offset = time_offset >> SHIFT_UPDATE;
22433965Sjdp#ifdef PPS_SYNC
22533965Sjdp	ntv.freq = time_freq + pps_freq;
22689857Sobrien#else /* PPS_SYNC */
22733965Sjdp	ntv.freq = time_freq;
22833965Sjdp#endif /* PPS_SYNC */
22933965Sjdp	ntv.maxerror = time_maxerror;
23033965Sjdp	ntv.esterror = time_esterror;
23133965Sjdp	ntv.status = time_status;
23233965Sjdp	ntv.constant = time_constant;
23333965Sjdp	ntv.precision = time_precision;
23433965Sjdp	ntv.tolerance = time_tolerance;
23533965Sjdp#ifdef PPS_SYNC
23633965Sjdp	ntv.shift = pps_shift;
23789857Sobrien	ntv.ppsfreq = pps_freq;
23889857Sobrien	ntv.jitter = pps_jitter >> PPS_AVG;
23933965Sjdp	ntv.stabil = pps_stabil;
24033965Sjdp	ntv.calcnt = pps_calcnt;
24133965Sjdp	ntv.errcnt = pps_errcnt;
24233965Sjdp	ntv.jitcnt = pps_jitcnt;
24333965Sjdp	ntv.stbcnt = pps_stbcnt;
24433965Sjdp#endif /* PPS_SYNC */
24533965Sjdp	(void)splx(s);
24633965Sjdp
24733965Sjdp	error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv));
24833965Sjdp	if (!error) {
24933965Sjdp		/*
25033965Sjdp		 * Status word error decode. See comments in
25133965Sjdp		 * ntp_gettime() routine.
25233965Sjdp		 */
25333965Sjdp		retval[0] = time_state;
25433965Sjdp		if (time_status & (STA_UNSYNC | STA_CLOCKERR))
25533965Sjdp			retval[0] = TIME_ERROR;
25633965Sjdp		if (time_status & (STA_PPSFREQ | STA_PPSTIME) &&
25733965Sjdp		    !(time_status & STA_PPSSIGNAL))
25833965Sjdp			retval[0] = TIME_ERROR;
25933965Sjdp		if (time_status & STA_PPSTIME &&
26033965Sjdp		    time_status & STA_PPSJITTER)
26133965Sjdp			retval[0] = TIME_ERROR;
26233965Sjdp		if (time_status & STA_PPSFREQ &&
26333965Sjdp		    time_status & (STA_PPSWANDER | STA_PPSERROR))
26433965Sjdp			retval[0] = TIME_ERROR;
26533965Sjdp	}
26633965Sjdp	return error;
26733965Sjdp}
26833965Sjdp
26933965Sjdp
27033965Sjdp