patch-2.0.32 linux/kernel/time.c

Next file: linux/mm/filemap.c
Previous file: linux/kernel/sched.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.31/linux/kernel/time.c linux/kernel/time.c
@@ -16,6 +16,10 @@
  *      adjtime interface update and CMOS clock write code
  * 1995-08-13    Torsten Duwe
  *      kernel PLL updated to 1994-12-13 specs (rfc-1589)
+ * 1996-10-22, 1997-09-13    Ulrich Windl
+ *	support for external PPS signal, error checking in adjtimex()
+ *	Updated NTP code according to technical memorandum Jan '96
+ *	"A Kernel Model for Precision Timekeeping" by Dave Mills
  */
 
 #include <linux/errno.h>
@@ -75,9 +79,11 @@
 	cli();
 	xtime.tv_sec = value;
 	xtime.tv_usec = 0;
-	time_state = TIME_ERROR;
-	time_maxerror = MAXPHASE;
-	time_esterror = MAXPHASE;
+	time_adjust = 0;	/* stop active adjtime() */
+	time_status |= STA_UNSYNC;
+	time_state = TIME_ERROR;	/* p. 24, (a) */
+	time_maxerror = NTP_PHASE_LIMIT;
+	time_esterror = NTP_PHASE_LIMIT;
 	sti();
 	return 0;
 }
@@ -195,14 +201,14 @@
 asmlinkage int sys_adjtimex(struct timex *txc_p)
 {
         long ltemp, mtemp, save_adjust;
-	int error;
+	int error = 0;
 
 	/* Local copy of parameter */
 	struct timex txc;
 
 	error = verify_area(VERIFY_WRITE, txc_p, sizeof(struct timex));
 	if (error)
-	  return error;
+		return error;	/* do not write results */
 
 	/* Copy the user data space into the kernel copy
 	 * structure. But bear in mind that the structures
@@ -216,124 +222,163 @@
 
 	/* Now we validate the data before disabling interrupts
 	 */
-
 	if (txc.modes != ADJ_OFFSET_SINGLESHOT && (txc.modes & ADJ_OFFSET))
-	  /* adjustment Offset limited to +- .512 seconds */
-	  if (txc.offset <= - MAXPHASE || txc.offset >= MAXPHASE )
-	    return -EINVAL;
-
-	/* if the quartz is off by more than 10% something is VERY wrong ! */
-	if (txc.modes & ADJ_TICK)
-	  if (txc.tick < 900000/HZ || txc.tick > 1100000/HZ)
-	    return -EINVAL;
+		/* adjustment Offset limited to +- .512 seconds */
+		if (txc.offset <= - MAXPHASE || txc.offset >= MAXPHASE )
+			return -EINVAL;
 
 	cli();
 
-	/* Save for later - semantics of adjtime is to return old value */
+	/* Save for later - semantics of adjtime() is to return old value */
 	save_adjust = time_adjust;
 
 	/* If there are input parameters, then process them */
+#if 0	/* STA_CLOCKERR is never set yet */
+	time_status &= ~STA_CLOCKERR;		/* reset STA_CLOCKERR */
+#endif
 	if (txc.modes)
 	{
-	    if (time_state == TIME_BAD)
-		time_state = TIME_OK;
+	    if (time_state == TIME_ERROR)
+		time_state = TIME_OK;		/* reset error -- why? */
 
-	    if (txc.modes & ADJ_STATUS)
-		time_status = txc.status;
-
-	    if (txc.modes & ADJ_FREQUENCY)
-		time_freq = txc.freq;
+	    if (txc.modes & ADJ_STATUS)	/* only set allowed bits */
+		time_status = (txc.status & ~STA_RONLY) |
+			      (time_status & STA_RONLY);
+
+	    if (txc.modes & ADJ_FREQUENCY) {	/* p. 22 */
+		if (txc.freq > MAXFREQ || txc.freq < -MAXFREQ) {
+		    error = -EINVAL;
+		    goto leave;
+		}
+		time_freq = txc.freq - pps_freq;
+	    }
 
-	    if (txc.modes & ADJ_MAXERROR)
+	    if (txc.modes & ADJ_MAXERROR) {
+		if (txc.maxerror < 0 || txc.maxerror >= NTP_PHASE_LIMIT) {
+		    error = -EINVAL;
+		    goto leave;
+		}
 		time_maxerror = txc.maxerror;
+	    }
 
-	    if (txc.modes & ADJ_ESTERROR)
+	    if (txc.modes & ADJ_ESTERROR) {
+		if (txc.esterror < 0 || txc.esterror >= NTP_PHASE_LIMIT) {
+		    error = -EINVAL;
+		    goto leave;
+		}
 		time_esterror = txc.esterror;
+	    }
 
-	    if (txc.modes & ADJ_TIMECONST)
+	    if (txc.modes & ADJ_TIMECONST) {	/* p. 24 */
+		if (txc.constant < 0 || txc.constant > MAXTC) {
+		    error = -EINVAL;
+		    goto leave;
+		}
 		time_constant = txc.constant;
+	    }
 
-	    if (txc.modes & ADJ_OFFSET)
-	      if ((txc.modes == ADJ_OFFSET_SINGLESHOT)
-		  || !(time_status & STA_PLL))
-		{
-		  time_adjust = txc.offset;
+	    if (txc.modes & ADJ_OFFSET) {	/* values checked earlier */
+		if (txc.modes == ADJ_OFFSET_SINGLESHOT) {
+		    /* adjtime() is independent from ntp_adjtime() */
+		    time_adjust = txc.offset;
 		}
-	      else if ((time_status & STA_PLL)||(time_status & STA_PPSTIME))
-		{
-		  ltemp = (time_status & STA_PPSTIME &&
-			   time_status & STA_PPSSIGNAL) ?
-		    pps_offset : txc.offset;
-
-		  /*
-		   * Scale the phase adjustment and
-		   * clamp to the operating range.
-		   */
-		  if (ltemp > MAXPHASE)
-		    time_offset = MAXPHASE << SHIFT_UPDATE;
-		  else if (ltemp < -MAXPHASE)
-		    time_offset = -(MAXPHASE << SHIFT_UPDATE);
-		  else
-		    time_offset = ltemp << SHIFT_UPDATE;
-
-		  /*
-		   * Select whether the frequency is to be controlled and in which
-		   * mode (PLL or FLL). Clamp to the operating range. Ugly
-		   * multiply/divide should be replaced someday.
-		   */
-
-		  if (time_status & STA_FREQHOLD || time_reftime == 0)
+		else if ( time_status & (STA_PLL | STA_PPSTIME) ) {
+		    ltemp = (time_status & (STA_PPSTIME | STA_PPSSIGNAL)) ==
+		            (STA_PPSTIME | STA_PPSSIGNAL) ?
+		            pps_offset : txc.offset;
+
+		    /*
+		     * Scale the phase adjustment and
+		     * clamp to the operating range.
+		     */
+		    if (ltemp > MAXPHASE)
+		        time_offset = MAXPHASE << SHIFT_UPDATE;
+		    else if (ltemp < -MAXPHASE)
+			time_offset = -(MAXPHASE << SHIFT_UPDATE);
+		    else
+		        time_offset = ltemp << SHIFT_UPDATE;
+
+		    /*
+		     * Select whether the frequency is to be controlled
+		     * and in which mode (PLL or FLL). Clamp to the operating
+		     * range. Ugly multiply/divide should be replaced someday.
+		     */
+
+		    if (time_status & STA_FREQHOLD || time_reftime == 0)
+		        time_reftime = xtime.tv_sec;
+		    mtemp = xtime.tv_sec - time_reftime;
 		    time_reftime = xtime.tv_sec;
-		  mtemp = xtime.tv_sec - time_reftime;
-		  time_reftime = xtime.tv_sec;
-		  if (time_status & STA_FLL)
-		    {
-		      if (mtemp >= MINSEC)
-			{
-			  ltemp = ((time_offset / mtemp) << (SHIFT_USEC -
-							     SHIFT_UPDATE));
-			  if (ltemp < 0)
-			    time_freq -= -ltemp >> SHIFT_KH;
-			  else
-			    time_freq += ltemp >> SHIFT_KH;
-			}
-		    } 
-		  else 
-		    {
-		      if (mtemp < MAXSEC)
-			{
-			  ltemp *= mtemp;
-			  if (ltemp < 0)
-			    time_freq -= -ltemp >> (time_constant +
-						    time_constant + SHIFT_KF -
-						    SHIFT_USEC);
-			  else
-			    time_freq += ltemp >> (time_constant +
-						   time_constant + SHIFT_KF -
-						   SHIFT_USEC);
-			}
+		    if (time_status & STA_FLL) {
+		        if (mtemp >= MINSEC) {
+			    ltemp = (time_offset / mtemp) << (SHIFT_USEC -
+							      SHIFT_UPDATE);
+			    if (ltemp < 0)
+			        time_freq -= -ltemp >> SHIFT_KH;
+			    else
+			        time_freq += ltemp >> SHIFT_KH;
+			} else /* calibration interval too short (p. 12) */
+				time_state = TIME_ERROR;
+		    } else {	/* PLL mode */
+		        if (mtemp < MAXSEC) {
+			    ltemp *= mtemp;
+			    if (ltemp < 0)
+			        time_freq -= -ltemp >> (time_constant +
+							time_constant +
+							SHIFT_KF - SHIFT_USEC);
+			    else
+			        time_freq += ltemp >> (time_constant +
+						       time_constant +
+						       SHIFT_KF - SHIFT_USEC);
+			} else /* calibration interval too long (p. 12) */
+				time_state = TIME_ERROR;
 		    }
-		  if (time_freq > time_tolerance)
-		    time_freq = time_tolerance;
-		  else if (time_freq < -time_tolerance)
-		    time_freq = -time_tolerance;
+		    if (time_freq > time_tolerance)
+		        time_freq = time_tolerance;
+		    else if (time_freq < -time_tolerance)
+		        time_freq = -time_tolerance;
 		} /* STA_PLL || STA_PPSTIME */
-	    if (txc.modes & ADJ_TICK)
-	      tick = txc.tick;
-
+	    } /* txc.modes & ADJ_OFFSET */
+	    if (txc.modes & ADJ_TICK) {
+		/* if the quartz is off by more than 10% something is
+		   VERY wrong ! */
+		if (txc.tick < 900000/HZ || txc.tick > 1100000/HZ) {
+		    error = -EINVAL;
+		    goto leave;
+		}
+		tick = txc.tick;
+	    }
+	} /* txc.modes */
+leave:	if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0
+	    || ((time_status & (STA_PPSFREQ|STA_PPSTIME)) != 0
+		&& (time_status & STA_PPSSIGNAL) == 0)
+	    /* p. 24, (b) */
+	    || ((time_status & (STA_PPSTIME|STA_PPSJITTER))
+		== (STA_PPSTIME|STA_PPSJITTER))
+	    /* p. 24, (c) */
+	    || ((time_status & STA_PPSFREQ) != 0
+		&& (time_status & (STA_PPSWANDER|STA_PPSERROR)) != 0))
+	    /* p. 24, (d) */
+		time_state = TIME_ERROR;
+	
+	if ((txc.modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT)
+	    txc.offset	   = save_adjust;
+	else {
+	    if (time_offset < 0)
+		txc.offset = -(-time_offset >> SHIFT_UPDATE);
+	    else
+		txc.offset = time_offset >> SHIFT_UPDATE;
 	}
-	txc.offset	   = save_adjust;
-	txc.freq	   = time_freq;
+	txc.freq	   = time_freq + pps_freq;
 	txc.maxerror	   = time_maxerror;
 	txc.esterror	   = time_esterror;
 	txc.status	   = time_status;
 	txc.constant	   = time_constant;
 	txc.precision	   = time_precision;
 	txc.tolerance	   = time_tolerance;
-	txc.time	   = xtime;
+	do_gettimeofday(&txc.time);
 	txc.tick	   = tick;
 	txc.ppsfreq	   = pps_freq;
-	txc.jitter	   = pps_jitter;
+	txc.jitter	   = pps_jitter >> PPS_AVG;
 	txc.shift	   = pps_shift;
 	txc.stabil	   = pps_stabil;
 	txc.jitcnt	   = pps_jitcnt;
@@ -344,5 +389,5 @@
 	sti();
 
 	memcpy_tofs(txc_p, &txc, sizeof(struct timex));
-	return time_state;
+	return(error < 0 ? error : time_state);
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov