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