summaryrefslogtreecommitdiffstats
path: root/usr.sbin/xntpd/kernel
diff options
context:
space:
mode:
authorwollman <wollman@FreeBSD.org>1993-12-21 18:36:48 +0000
committerwollman <wollman@FreeBSD.org>1993-12-21 18:36:48 +0000
commit8e51e9f1429efc498f923bce8b25b20f47d7c075 (patch)
tree9db10264d45dc397a38276190303093a450d769e /usr.sbin/xntpd/kernel
downloadFreeBSD-src-8e51e9f1429efc498f923bce8b25b20f47d7c075.zip
FreeBSD-src-8e51e9f1429efc498f923bce8b25b20f47d7c075.tar.gz
xntpd 3.3b from UDel
Diffstat (limited to 'usr.sbin/xntpd/kernel')
-rw-r--r--usr.sbin/xntpd/kernel/Makefile.tmpl39
-rw-r--r--usr.sbin/xntpd/kernel/README90
-rw-r--r--usr.sbin/xntpd/kernel/README.kern596
-rw-r--r--usr.sbin/xntpd/kernel/README.streams86
-rw-r--r--usr.sbin/xntpd/kernel/tty_chu.c276
-rw-r--r--usr.sbin/xntpd/kernel/tty_chu_STREAMS.c444
-rw-r--r--usr.sbin/xntpd/kernel/tty_clk.c303
-rw-r--r--usr.sbin/xntpd/kernel/tty_clk_STREAMS.c265
8 files changed, 2099 insertions, 0 deletions
diff --git a/usr.sbin/xntpd/kernel/Makefile.tmpl b/usr.sbin/xntpd/kernel/Makefile.tmpl
new file mode 100644
index 0000000..700efa6
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/Makefile.tmpl
@@ -0,0 +1,39 @@
+#
+# /src/NTP/REPOSITORY/v3/kernel/Makefile.tmpl,v 3.4 1993/11/05 23:51:26 kardel Exp
+#
+#
+# parse routine that could be used in two places
+#
+COMPILER= cc
+COPTS= -O
+AUTHDEFS=-DDES
+LIBDEFS= -DBIG_ENDIAN
+RANLIB= ranlib
+INSTALL= install
+CLOCKDEFS=
+DEFS=
+DEFS_OPT=
+DEFS_LOCAL=
+#
+INCL=-I../include
+CFLAGS= $(COPTS) $(DEFS) $(DEFS_LOCAL) $(INCL)
+CC= $(COMPILER)
+#
+
+all:
+ @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \
+ awk '/-DREFCLOCK/ && ( /-D.*CLK/ || /-D.*PPS/ ) { makeit=1; }\
+ END { if (makeit) \
+ { print ""; \
+ print "### STREAMS kernel modules ppsclock, ppsclocd or line disciplines must"; \
+ print "### be installed manually if needed"; \
+ print "### see kernel/README* for details"; \
+ print "### The parse reclock implementation has their own support in"; \
+ print "### parse/*."; } }'
+
+clean:
+ -@rm -f *~ *.o *.out *.ln make.log Makefile.bak \
+ lintlib.errs lint.errs
+
+distclean: clean
+ -@rm -f *.orig *.rej .version Makefile
diff --git a/usr.sbin/xntpd/kernel/README b/usr.sbin/xntpd/kernel/README
new file mode 100644
index 0000000..cf69b13
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/README
@@ -0,0 +1,90 @@
+This directory contains code for two line disciplines which may
+work with BSD-style terminal drivers. While I'll try to cover
+installation details for the more useful one here as best I can,
+you really should know what you are doing before attempting to
+put one of these in your kernel since the details seem to vary
+from BSD variant to BSD variant.
+
+Tty_clk.c contains a generic clock support line discipline.
+The terminal driver is actually run in raw mode, giving you an
+eight bit data path. Instead of delivering the data
+character-by-character, however, the line discipline collects
+characters until one of two magic characters (your current erase
+and kill characters. Don't throw up) is received. A timestamp
+is then taken (by calling microtime()), inserted in the input
+buffer after the magic character, and the whole mess made available
+for input by the application. Both select() and SIGIO are supported
+by the discipline.
+
+Tty_chu.c is a special purpose line discipline for receiving
+the CHU time code. It understands enough about the format of the
+code CHU transmits to filter out errors, and delivers an entire
+ten character code group to the application all at once, including
+a timestamp for each character. The structure the code group is
+delivered in is defined in chudefs.h. Note that this line discipline
+is old and could use some rewriting for better portability. Please
+drop me a line if you are interested in using this.
+
+To install the clock line discipline, do something like the following:
+
+(1) Copy tty_clk.c into /sys/sys
+
+(2) Edit /sys/sys/tty_conf.c. You will want to include some facsimile
+ of the following lines:
+
+#include "clk.h"
+#if NCLK > 0
+int clkopen(), clkclose(), clkwrite(), clkinput(), clkioctl();
+#endif
+
+#if NCLK > 0
+ { clkopen, clkclose, ttread, clkwrite, clkioctl,
+ clkinput, nodev, nulldev, ttstart, nullmodem, /* 10- CLKLDISC */
+ ttselect },
+#else
+ { nodev, nodev, nodev, nodev, nodev,
+ nodev, nodev, nodev, nodev, nodev,
+ nodev },
+#endif
+
+ In Ultrix 4.2a and 4.3 the file to edit is /sys/data/tty_conf_data.c.
+ The lines should be
+
+#if NCLK > 0
+ clkopen, clkclose, ttread, clkwrite, clkioctl, /* 10 */
+ clkinput, nodev, nulldev, ttstart, nulldev,
+#else
+ nodev, nodev, nodev, nodev, nodev,
+ nodev, nodev, nodev, nodev, nodev,
+#endif
+
+ Note that if your kernel doesn't include the ??select() entry in
+ the structure (i.e. there are only 10 entry points in the structure)
+ just leave it out. Also note that the number you give the line
+ discipline (10 in my kernel) will be specific to your kernel and
+ will depend on what is in there already. The entries sould be in
+ order with no missing space; that is, if there are only seven
+ disciplines already defined and you want to use 10 for good reason,
+ you should define a dummy 9th entry like this
+
+ nodev, nodev, nodev, nodev, nodev, /* 9 */
+ nodev, nodev, nodev, nodev, nodev,
+
+(3) Edit /sys/h/ioctl.h and include a line (somewhere near where other
+ line disciplines are defined) like:
+
+#define CLKLDISC 10 /* clock line discipline */
+
+ The `10' should match what you used in /sys/sys/tty_conf.c.
+
+(4) Edit /sys/conf/files and add a line which looks like:
+
+sys/tty_clk.c optional clk
+
+(5) Edit the configuration file for the machine you want to use
+ the clock line discipline on to include the following:
+
+pseudo-device clk 4
+
+(6) Run config, then make clean, then make depend, then make vmunix.
+ Then reboot the new kernel.
diff --git a/usr.sbin/xntpd/kernel/README.kern b/usr.sbin/xntpd/kernel/README.kern
new file mode 100644
index 0000000..64ba9c5
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/README.kern
@@ -0,0 +1,596 @@
+Precision Time and Frequency Synchronization Using Modified Kernels
+
+1. Introduction
+
+This memo describes replacements for certain SunOS and Ultrix kernel
+routines that manage the system clock and timer functions. They provide
+improved accuracy and stability through the use of a disciplined clock
+interface for use with the Network Time Protocol (NTP) or similar time-
+synchronization protocol. In addition, for certain models of the
+DECstation 5000 product line, the new routines provide improved
+precision to +-1 microsecond (us) (SunOS 4.1.1 already does provide
+precision to +-1 us). The current public NTP distribution cooperates
+with these kernel routines to provide synchronization in principle to
+within a microsecond, but in practice this is limited by the short-term
+stability of the oscillator that drives the timer interrupt.
+
+This memo describes the principles behind the design and operation of
+the software. There are two versions of the software, one that operates
+with the SunOS 4.1.1 kernel and the other that operates with the Ultrix
+4.2a kernel (and probably the 4.3 kernel, although this has not been
+tested). A detailed description of the variables and algorithms is given
+in the hope that similar improvements can be incorporated in Unix
+kernels for other machines. The software itself is not included in this
+memo, since it involves licensed code. Detailed instructions on where to
+obtain it for either SunOS or Ultrix will be given separately.
+
+The principle function added to the SunOS and Ultrix kernels is to
+change the way the system clock is controlled, in order to provide
+precision time and frequency adjustments. Another function utilizes an
+undocumented counter in the DECstation hardware to provide precise time
+to the microsecond. This function can be used only with the DECstation
+5000/240 and possibly others that use the same input/output chipset.
+
+2. Design Principles
+
+In order to understand how these routines work, it is useful to consider
+how most Unix systems maintain the system clock. In the original design
+a hardware timer interrupts the kernel at some fixed rate, such as 100
+Hz in the SunOS kernel and 256 Hz in the Ultrix kernel. Since 256 does
+not evenly divide the second in microseconds, the kernel inserts 64 us
+once each second so that the system clock stays in step with real time.
+The time returned by the gettimeofday() routine is thus characterized by
+255 advances of 3906 us plus one of 3970 us.
+
+Also in the original design it is possible to slew the system clock to a
+new offset using the adjtime() system call. To do this the clock
+frequency is changed by adding or subtracting a fixed amount (tickadj)
+at each timer interrupt (tick) for a calculated number of ticks. Since
+this calculation involves dividing the requested offset by tickadj, it
+is possible to slew to a new offset with a precision only of tickadj,
+which is usually in the neighborhood of 5 us, but sometimes much higher.
+
+In order to maintain the system clock within specified bounds with this
+scheme, it is necessary to call adjtime() on a regular basis. For
+instance, let the bound be set at 100 us, which is a reasonable value
+for NTP-synchronized hosts on a local network, and let the onboard
+oscillator tolerance be 100 ppm, which is a reasonably conservative
+assumption. This requires that adjtime() be called at intervals not
+exceeding 1 second (s), which is in fact what the unmodified NTP
+software daemon does.
+
+In the modified kernel routines this scheme is replaced by another that
+extends the low-order bits of the system clock to provide very precise
+clock adjustments. At each timer interrupt a precisely calibrated time
+adjustment is added to the composite time value and overflows handled as
+required. The quantity to add is computed from the adjtime() call and,
+in addition a frequency adjustment, which is automatically calculated
+from previous time adjustments. This implementation operates as an
+adaptive-parameter, first-order, type-II, phase-lock loop (PLL), which
+in principle provides precision control of the system clock phase to
+within +-1 us and frequency to within +-5 nanoseconds (ns) per day.
+
+This PLL model is identical to the one implemented in NTP, except that
+in NTP the software daemon has to simulate the PLL using only the
+original adjtime() system call. The daemon is considerably complicated
+by the need to parcel time adjustments at frequent intervals in order to
+maintain the accuracy to specified bounds. The kernel routines do this
+directly, allowing vast gobs of ugly daemon code to be avoided at the
+expense of only a small amount of new code in the kernel. In fact, the
+amount of code added to the kernel for the new scheme is about the
+amount removed for the old scheme. The new adjtime() routine needs to be
+called only as each new time update is determined, which in NTP occurs
+at intervals of from 64 s to 1024 s. In addition, doing the frequency
+correction in the kernel means that the system time runs true even if
+the daemon were to cease operation or the network paths to the primary
+reference source fail.
+
+Note that the degree to which the adjtime() adjustment can be made is
+limited to a specific maximum value, presently +-128 milliseconds (ms),
+in order to achieve microsecond resolution. It is the intent in the
+design that settimeofday() be used for changes in system time greater
+than +-128 ms. It has been the Internet experience that the need to
+change the system time in increments greater than +-128 milliseconds is
+extremely rare and is usually associated with a hardware or software
+malfunction. Nevertheless, the limit applies to each adjtime() call and
+it is possible, but not recommended, that this routine is called at
+intervals smaller than 64 seconds, which is the NTP lower limit.
+
+For the most accurate and stable operation, adjtime() should be called
+at specified intervals; however, the PLL is quite forgiving and neither
+moderate loss of updates nor variations in the length of the interval is
+serious. The current engineering parameters have been optimized for
+intervals not greater than about 64 s. For larger intervals the PLL time
+constant can be adjusted to optimize the dynamic response up to
+intervals of 1024 s. Normally, this is automatically done by NTP. In any
+case, if updates are suspended, the PLL coasts at the frequency last
+determinated, which usually results in errors increasing only to a few
+tens of milliseconds over a day.
+
+The new code needs to know the initial frequency offset and time
+constant for the PLL, and the daemon needs to know the current frequency
+offset computed by the kernel for monitoring purposes. This is provided
+by a small change in the second argument of the kernel adjtime() calling
+sequence, which is documented later in this memo. Ordinarily, only the
+daemon will call the adjtime() routine, so the modified calling sequence
+is easily accommodated. Other than this change, the operation of
+adjtime() is transparent to the original.
+
+In the DECstation 5000/240 and possibly other models there happens to be
+an undocumented hardware register that counts system bus cycles at a
+rate of 25 MHz. The new kernel routines test for the CPU type and, in
+the case of the '240, use this register to interpolate system time
+between hardware timer interrupts. This results in a precision of +-1 us
+for all time values obtained via the gettimeofday() system call. This
+routine calls the kernel routine microtime(), which returns the actual
+interpolated value, but does not change the kernel time variable.
+Therefore, other kernel routines that access the kernel time variable
+directly and do not call either gettimeofday() or microtime() will
+continue their present behavior.
+
+The new kernel routines include provisions for error statistics (maximum
+error and estimated error), leap seconds and system clock status. These
+are intended to support applications that need such things; however,
+there are no applications other than the time-synchronization daemon
+itself that presently use them. At issue is the manner in which these
+data can be provided to application clients, such as new system calls
+and data interfaces. While a proposed interface is described later in
+this memo, it has not yet been implemented. This is an area for further
+study.
+
+While any time-synchronization daemon can in principle be modified to
+use the new code, the most likely will be users of the xntp3
+distribution of NTP. The code in the xntp3 distribution determines
+whether the new kernel code is in use and automatically reconfigures as
+required. When the new code is in use, the daemon reads the frequency
+offset from a file and provides it and the initial time constant via
+adjtime(). In subsequent calls to adjtime(), only the time adjustment
+and time constant are affected. The daemon reads the frequency from the
+kernel (returned as the second argument of adjtime()) at intervals of
+one hour and writes it to the file.
+
+3. Technical Description
+
+Following is a technical description of how the new scheme works in
+terms of the variables and algorithms involved. These components are
+discussed as a distinct entity and do not involve coding details
+specific to the Ultrix kernel. The algorithms involve only minor changes
+to the system clock and interval timer routines, but do not in
+themselves provide a conduit for application programs to learn the
+system clock status or statistics of the time-synchronization process.
+In a later section a number of new system calls are proposed to do this,
+along with an interface specification.
+
+The new scheme works like the companion simulator called kern.c and
+included in this directory. This stand-alone simulator includes code
+fragments identical to those in the modified kernel routines and
+operates in the same way. The system clock is implemented in the kernel
+using a set of variables and algorithms defined below and in the
+simulator. The algorithms are driven by explicit calls from the
+synchronization protocol as each time update is computed. The clock is
+read and set using the gettimeofday() and settimeofday() system calls,
+which operate in the same way as the originals, but return a status word
+describing the state of the system clock.
+
+Once the system clock has been set, the adjtime() system call is used to
+provide periodic updates including the time offset and possibly
+frequency offset and time constant. With NTP this occurs at intervals of
+from 64 s to 1024 s, deending on the time constant value. The kernel
+implements an adaptive-parameter, first-order, type-II, phase-lock loop
+(PLL) in order to integrate this offset into the phase and frequency of
+the system clock. The kernel keeps track of the time of the last update
+and adjusts the maximum error to grow by an amount equal to the
+oscillator frequency tolerance times the elapsed time since the last
+update.
+
+Occasionally, it is necessary to adjust the PLL parameters in response
+to environmental conditions, such as leap-second warning and oscillator
+stability observations. While the interface to do this has not yet been
+implemented, proposals to to that are included in a later section. A
+system call (setloop()) is used on such occasions to communicate these
+data. In addition, a system call (getloop())) is used to extract these
+data from the kernel for monitoring purposes.
+
+All programs utilize the system clock status variable time_status, which
+records whether the clock is synchronized, waiting for a leap second,
+etc. The value of this variable is returned by each system call. It can
+be set explicitly by the setloop() system call and implicitly by the
+settimeofday() system call and in the timer-interrupt routine. Values
+presently defined in the header file timex.h are as follows:
+
+int time_status = TIME_BAD; /* clock synchronization status */
+
+#define TIME_UNS 0 /* unspecified or unknown */
+#define TIME_OK 1 /* operation succeeded */
+#define TIME_INS 1 /* insert leap second at end of current day */
+#define TIME_DEL 2 /* delete leap second at end of current day */
+#define TIME_OOP 3 /* leap second in progress */
+#define TIME_BAD 4 /* system clock is not synchronized */
+#define TIME_ADR -1 /* operation failed: invalid address */
+#define TIME_VAL -2 /* operation failed: invalid argument */
+#define TIME_PRV -3 /* operation failed: priviledged operation */
+
+In case of a negative result code, the operation has failed; however,
+some variables may have been modified before the error was detected.
+Note that the new system calls never return a value of zero, so it is
+possible to determine whether the old routines or the new ones are in
+use. The syntax of the modified adjtime() is as follows:
+
+/*
+ * adjtime - adjuts system time
+ */
+#include <sys/timex.h>
+
+int gettimexofday(tp, fiddle)
+
+struct timeval *tp; /* system time adjustment*/
+struct timeval *fiddle; /* sneak path */
+
+On entry the "timeval" sneak path is coded:
+
+struct timeval {
+ long tv_sec = time_constant; /* time constant */
+ long tv_usec = time_freq; /* new frequency offset */
+}
+
+However, the sneak is ignored if fiddle is the null pointer and the new
+frequency offset is ignored if zero.
+
+The value returned on exit is the system clock status defined above. The
+"timeval" sneak path is modified as follows:
+
+struct timeval {
+ long tv_sec = time_precision; /* system clock precision */
+ long tv_usec = time_freq; /* current frequency offset */
+}
+
+3.1. Kernel Variables
+
+The following variables are used by the new code:
+
+long time_offset = 0; /* time adjustment (us) */
+
+This variable is used by the PLL to adjust the system time in small
+increments. It is scaled by (1 << SHIFT_UPDATE) in binary microseconds.
+The maximum value that can be represented is about +-130 ms and the
+minimum value or precision is about one nanosecond.
+
+long time_constant = SHIFT_TAU; /* pll time constant */
+
+This variable determines the bandwidth or "stiffness" of the PLL. It is
+used as a shift, with the effective value in positive powers of two. The
+optimum value for this variable is equal to 1/64 times the update
+interval. The default value SHIFT_TAU (0) corresponds to a PLL time
+constant of about one hour or an update interval of about one minute,
+which is appropriate for typical uncompensated quartz oscillators used
+in most computing equipment. Values larger than four are not useful,
+unless the local clock timebase is derived from a precision oscillator.
+
+long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */
+
+This variable represents the maximum frequency error or tolerance of the
+particular platform and is a property of the architecture. It is
+expressed as a positive number greater than zero in parts-per-million
+(ppm). The default MAXFREQ (100) is appropriate for conventional
+workstations.
+
+long time_precision = 1000000 / HZ; /* clock precision (us) */
+
+This variable represents the maximum error in reading the system clock.
+It is expressed as a positive number greater than zero in microseconds
+and is usually based on the number of microseconds between timer
+interrupts, in the case of the Ultrix kernel, 3906. However, in cases
+where the time can be interpolated between timer interrupts with
+microsecond resolution, the precision is specified as 1. This variable
+is computed by the kernel for use by the time-synchronization daemon,
+but is otherwise not used by the kernel.
+
+struct timeval time_maxerror; /* maximum error */
+
+This variable represents the maximum error, expressed as a Unix timeval,
+of the system clock. For NTP, it is computed as the synchronization
+distance, which is equal to one-half the root delay plus the root
+dispersion. It is increased by a small amount (time_tolerance) each
+second to reflect the clock frequency tolerance. This variable is
+computed by the time-synchronization daemon and the kernel for use by
+the application program, but is otherwise not used by the kernel.
+
+struct timeval time_esterror; /* estimated error */
+
+This variable represents the best estimate of the actual error,
+expressed as a Unix timeval, of the system clock based on its past
+behavior, together with observations of multiple clocks within the peer
+group. This variable is computed by the time-synchronization daemon for
+use by the application program, but is otherwise not used by the kernel.
+
+The PLL itself is controlled by the following variables:
+
+long time_phase = 0; /* phase offset (scaled us) */
+long time_freq = 0; /* frequency offset (scaled ppm) */long
+time_adj = 0; /* tick adjust (scaled 1 / HZ) */
+
+These variables control the phase increment and the frequency increment
+of the system clock at each tick of the clock. The time_phase variable
+is scaled by (1 << SHIFT_SCALE) in binary microseconds, giving a minimum
+value (time resolution) of 9.3e-10 us. The time_freq variable is scaled
+by (1 << SHIFT_KF) in parts-per-million (ppm), giving it a maximum value
+of about +-130 ppm and a minimum value (frequency resolution) of 6e-8
+ppm. The time_adj variable is the actual phase increment in scaled
+microseconds to add to time_phase once each tick. It is computed from
+time_phase and time_freq once per second.
+
+long time_reftime = 0; /* time at last adjustment (s) */
+
+This variable is the second's portion of the system time on the last
+call to adjtime(). It is used to adjust the time_freq variable as the
+time since the last update increases.
+
+The HZ define establishes the timer interrupt frequency, 256 Hz for the
+Ultrix kernel and 100 Hz for the SunOS kernel. The SHIFT_HZ define
+expresses the same value as the nearest power of two in order to avoid
+hardware multiply operations. These are the only parameters that need to
+be changed for different timer interrupt rates.
+
+#define HZ 256 /* timer interrupt frequency (Hz) */
+#define SHIFT_HZ 8 /* log2(HZ) */
+
+The following defines establish the engineering parameters of the PLL
+model. They are chosen for an initial convergence time of about an hour,
+an overshoot of about seven percent and a final convergence time of
+several hours, depending on initial frequency error.
+
+#define SHIFT_KG 10 /* shift for phase increment */
+#define SHIFT_KF 24 /* shift for frequency increment */
+#define SHIFT_TAU 0 /* default time constant (shift) */
+
+The SHIFT_SCALE define establishes the decimal point on the time_phase
+variable which serves as a an extension to the low-order bits of the
+system clock variable. The SHIFT_UPDATE define establishes the decimal
+point of the phase portion of the adjtime() update. The FINEUSEC define
+represents 1 us in scaled units.
+
+#define SHIFT_SCALE 28 /* shift for scale factor */
+#define SHIFT_UPDATE 14 /* shift for offset scale factor */
+#define FINEUSEC (1 << SHIFT_SCALE) /* 1 us in scaled units */
+
+The FINETUNE define represents the residual, in ppm, to be added to the
+system clock variable in addition to the integral 1-us value given by
+tick. This allows a systematic frequency offset in cases where the timer
+interrupt frequency does not exactly divide the second in microseconds.
+
+#define FINETUNE (1000000 - (1000000 / HZ) * HZ) /* frequency adjustment
+ * for non-isochronous HZ (ppm) */
+
+The following four defines establish the performance envelope of the
+PLL, one to bound the maximum phase error, another to bound the maximum
+frequency error and the last two to bound the minimum and maximum time
+between updates. The intent of these bounds is to force the PLL to
+operate within predefined limits in order to conform to the correctness
+models assumed by time-synchronization protocols like NTP and DTSS. An
+excursion which exceeds these bounds is clamped to the bound and
+operation proceeds accordingly. In practice, this can occur only if
+something has failed or is operating out of tolerance, but otherwise the
+PLL continues to operate in a stable mode. Note that the MAXPHASE define
+conforms to the maximum offset allowed in NTP before the system time is
+reset, rather than incrementally adjusted.
+
+#define MAXPHASE 128000 /* max phase error (us) */
+#define MINSEC 64 /* min interval between updates (s) */
+#define MAXFREQ 100 /* max frequency error (ppm) */
+#define MAXSEC 1024 /* max interval between updates (s) */
+
+3.2. Code Segments
+
+The code segments illustrated in the simulator should make clear the
+operations at various points in the code. These segments are not derived
+from any licensed code. The hardupdate() fragment is called by adjtime()
+to update the system clock phase and frequency. This is an
+implementation of an adaptive-parameter, first-order, type-II phase-lock
+loop. Note that the time constant is in units of powers of two, so that
+multiplies can be done by simple shifts. The phase variable is computed
+as the offset multiplied by the time constant. Then, the time since the
+last update is computed and clamped to a maximum (for robustness) and to
+zero if initializing. The offset is multiplied (sorry about the ugly
+multiply) by the result and by the square of the time constant and then
+added to the frequency variable. Finally, the frequency variable is
+clamped not to exceed the tolerance. Note that all shifts are assumed to
+be positive and that a shift of a signed quantity to the right requires
+a litle dance.
+
+With the defines given, the maximum time offset is determined by the
+size in bits of the long type (32) less the SHIFT_UPDATE (14) scale
+factor or 18 bits (signed). The scale factor is chosen so that there is
+no loss of significance in later steps, which may involve a right shift
+up to 14 bits. This results in a maximum offset of about +-130 ms. Since
+the time_constant must be greater than or equal to zero, the maximum
+frequency offset is determined by the SHIFT_KF (24) scale factor, or
+about +-130 ppm. In the addition step the value of offset * mtemp is
+represented in 18 + 10 = 28 bits, which will not overflow a long add.
+There could be a loss of precision due to the right shift of up to eight
+bits, since time_constant is bounded at four. This results in a net
+worst-case frequency error of about 2^-16 us or well down into the
+oscillator phase noise. While the time_offset value is assumed checked
+before entry, the time_phase variable is an accumulator, so is clamped
+to the tolerance on every call. This helps to damp transients before the
+oscillator frequency has been determined, as well as to satisfy the
+correctness assertions if the time-synchronization protocol comes
+unstuck.
+
+The hardclock() fragment is inserted in the hardware timer interrupt
+routine at the point the system clock is to be incremented. The phase
+adjustment (time_adj) is added to the clock phase (time_phase) and
+tested for overflow of the microsecond. If an overflow occurs, the
+microsecond (tick) in incremented or decremented.
+
+The second_overflow() fragment is inserted at the point where the
+microseconds field of the system time variable is being checked for
+overflow. On rollover of the second the maximum error is increased by
+the tolerance. The time offset is divided by the phase weight (SHIFT_KG)
+and time constant. The time offset is then reduced by the result and the
+result is scaled and becomes the value of the phase adjustment. The
+phase adjustment is then corrected for the calculated frequency offset
+and a fixed offset FINETUNE which is a property of the architecture. On
+rollover of the day the leap-warning indicator is checked and the
+apparent time adjusted +-1 s accordingly. The gettimeofday() routine
+insures that the reported time is always monotonically increasing.
+
+The simulator can be used to check the loop operation over the design
+range of +-128 ms in time error and +-100 ppm in frequency error. This
+confirms that no overflows occur and that the loop initially converges
+in about 50-60 minutes for timer interrupt rates from 50 Hz to 1024 Hz.
+The loop has a normal overshoot of about seven percent and a final
+convergence time of several hours, depending on the initional frequency
+error.
+
+3.3. Leap Seconds
+
+The leap-warning condition is determined by the synchronization protocol
+(if remotely synchronized), by the timecode receiver (if available), or
+by the operator (if awake). The time_status value must be set on the day
+the leap event is to occur (30 June or 31 December) and is automatically
+reset after the event. If the value is TIME_DEL, the kernel adds one
+second to the system time immediately following second 23:59:58 and
+resets time_status to TIME_OK. If the value is TIME_INS, the kernel
+subtracts one second from the system time immediately following second
+23:59:59 and resets time_status to TIME_OOP, in effect causing system
+time to repeat second 59. Immediately following the repeated second, the
+kernel resets time_status to TIME_OK.
+
+Depending upon the system call implementation, the reported time during
+a leap second may repeat (with a return code set to advertise that fact)
+or be monotonically adjusted until system time "catches up" to reported
+time. With the latter scheme the reported time will be correct before
+and after the leap second, but freeze or slowly advance during the leap
+second itself. However, Most programs will probably use the ctime()
+library routine to convert from timeval (seconds, microseconds) format
+to tm format (seconds, minutes,...). If this routine is modified to
+inspect the return code of the gettimeofday() routine, it could simply
+report the leap second as second 60.
+
+To determine local midnight without fuss, the kernel simply finds the
+residue of the time.tv_sec value mod 86,400, but this requires a messy
+divide. Probably a better way to do this is to initialize an auxiliary
+counter in the settimeofday() routine using an ugly divide and increment
+the counter at the same time the time.tv_sec is incremented in the timer
+interrupt routine. For future embellishment.
+
+4. Proposed Application Program Interface
+
+Most programs read the system clock using the gettimeofday() system
+call, which returns the system time and time-zone data. In the modified
+5000/240 kernel, the gettimeofday() routine calls the microtime()
+routine, which interpolates between hardware timer interrupts to a
+precision of +-1 microsecond. However, the synchronization protocol
+provides additional information that will be of interest in many
+applications. For some applications it is necessary to know the maximum
+error of the reported time due to all causes, including those due to the
+system clock reading error, oscillator frequency error and accumulated
+errors due to intervening time servers on the path to a primary
+reference source. However, for those protocols that adjust the system
+clock frequency as well as the time offset, the errors expected in
+actual use will almost always be much less than the maximum error.
+Therefore, it is useful to report the estimated error, as well as the
+maximum error.
+
+It does not seem useful to provide additional details private to the
+kernel and synchronization protocol, such as stratum, reference
+identifier, reference timestamp and so forth. It would in principle be
+possible for the application to independently evaluate the quality of
+time and project into the future how long this time might be "valid."
+However, to do that properly would duplicate the functionality of the
+synchronization protocol and require knowledge of many mundane details
+of the platform architecture, such as the tick value, reachability
+status and related variables. Therefore, the application interface does
+not reveal anything except the time, timezone and error data.
+
+With respect to NTP, the data maintained by the protocol include the
+roundtrip delay and total dispersion to the source of synchronization.
+In terms of the above, the maximum error is computed as half the delay
+plus the dispersion, while the estimated error is equal to the
+dispersion. These are reported in timeval structures. A new system call
+is proposed that includes all the data in the gettimeofday() plus the
+two new timeval structures.
+
+The proposed interface involves modifications to the gettimeofday(),
+settimeofday() and adjtime() system calls, as well as new system calls
+to get and set various system parameters. In order to minimize
+confusion, by convention the new system calls are named with an "x"
+following the "time"; e.g., adjtime() becomes adjtimex(). The operation
+of the modified gettimexofday(), settimexofday() and adjtimex() system
+calls is identical to that of their prototypes, except for the error
+quantities and certain other side effects, as documented below. By
+convention, a NULL pointer can be used in place of any argument, in
+which case the argument is ignored.
+
+The synchronization protocol daemon needs to set and adjust the system
+clock and certain other kernel variables. It needs to read these
+variables for monitoring purposes as well. The present list of these
+include a subset of the variables defined previously:
+
+long time_precision
+long time_timeconstant
+long time_tolerance
+long time_freq
+long time_status
+
+/*
+ * gettimexofday, settimexofday - get/set date and time
+ */
+#include <sys/timex.h>
+
+int gettimexofday(tp, tzp, tmaxp, testp)
+
+struct timeval *tp; /* system time */
+struct timezone *tzp; /* timezone */
+struct timeval *tmaxp; /* maximum error */
+struct timeval *testp; /* estimated error */
+
+The settimeofday() syntax is identical. Note that a call to
+settimexofday() automatically results in the system being declared
+unsynchronized (TIME_BAD return code), since the synchronization
+condition can only be achieved by the synchronization daemon using an
+internal or external primary reference source and the adjtimex() system
+call.
+
+/*
+ * adjtimex - adjust system time
+ */
+#include <sys/timex.h>
+
+int adjtimex(tp, tzp, freq, tc)
+
+struct timeval *tp; /* system time */
+struct timezone *tzp; /* timezone */
+long freq; /* frequency adjustment */
+long tc; /* time constant */
+
+/*
+ * getloop, setloop - get/set kernel time variables
+ */
+#include <sys/timex.h>
+
+int getloop(code, argp)
+
+int code; /* operation code */
+long *argp; /* argument pointer */
+
+The paticular kernal variables affected by these routines are selected
+by the operation code. Values presently defined in the header file
+timex.h are as follows:
+
+#define TIME_PREC 1 /* precision (log2(sec)) */
+#define TIME_TCON 2 /* time constant (log2(sec) */
+#define TIME_FREQ 3 /* frequency tolerance */
+#define TIME_FREQ 4 /* frequency offset (scaled) */
+#define TIME_STAT 5 /* status (see return codes) */
+
+The getloop() syntax is identical.
+
+Comments welcome, but very little support is available:
+
+David L. Mills
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+mills@udel.edu
diff --git a/usr.sbin/xntpd/kernel/README.streams b/usr.sbin/xntpd/kernel/README.streams
new file mode 100644
index 0000000..26c2825
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/README.streams
@@ -0,0 +1,86 @@
+Some kernels don't support additional user defined line disciplines.
+Especially notable in this regard is SunOS and System V. They
+provide similar support in the form of "Streams". Accordingly,
+included in this directory is a pair of STREAMS modules to
+replace the line disciplines that provide clock support for
+xntpd. Notice that the "clkdefs.h" file is not used in the
+original line discipline, but the "chudefs.h" file is the
+same one used in the original line discipline.
+
+TO INSTALL A NEW STREAMS DRIVER:
+
+1. Copy your choice to /sys/os, removing the "_STREAMS" in the
+filename.
+
+2. Copy the appropriate *defs.h file to /usr/include/sys,
+then link it (with ln) to /sys/sys.
+
+In the following steps, substitute "clk" for "chu" if you're
+installing the clk driver.
+
+3. Append to /sys/conf.common/files.cmn:
+
+os/tty_chu.c optional chu
+
+4. Edit /sys/sun/str_conf.c. You'll want to add lines in three
+places. It'll be sort of clear where when you see the file.
+
+.
+.
+.
+#include "chu.h"
+.
+.
+.
+#if NCHU > 0
+extern struct streamtab chuinfo;
+#endif
+.
+.
+.
+#if NCHU > 0
+ { "chu", &chuinfo },
+#endif
+.
+.
+.
+
+At this point, the kernel-making "config [k-name] ; cd ../[k-name] ; make"
+should produce a kernel just as it did before. If it fouls up,
+something's wrong.
+
+5. Edit /sys/[arch]/conf/[k-name] (substituting the architecture and
+kernel name) to stick in:
+
+pseudo-device chu4 # CHU clock support
+
+You can change 4 to anything you like. It will limit the number
+of instantiations of the chu driver you can use at the same time.
+
+6. Make a new kernel and boot it.
+
+HOW TO USE THE CHU STREAMS DRIVER:
+
+The driver should act exactly like the line discipline.
+After setting the raw mode, and exclusive access (if you want),
+pop off all the extra streams, then push the chu module
+on. From then on, any reads from the file in question
+will return chucode structures as defined in chudefs.h.
+Depending on the settings of PEDANTIC and ANAL_RETENTIVE
+used when compiling the kernel, some checking of the
+data may or may not be necessary.
+
+HOW TO USE THE CLK STREAMS DRIVER:
+
+First, it should be noted that a new ioctl() has been defined.
+The CLK_SETSTR ioctl takes a pointer to a string of no more
+than CLK_MAXSTRSIZE characters. Until the first CLK_SETSTR
+is performed, the driver will simply pass through characters.
+Once it is passed a string, then any character in that string
+will be immediately followed by a struct timeval. You can
+change the string whenever you want by doing another
+CLK_SETSTR. The character must be an exact, 8 bit match.
+The character '\000' cannot, unfortunately, be stamped.
+Passing an empty string to CLK_SETSTR turns off stamping.
+Passing NULL will produce undefined results.
+
diff --git a/usr.sbin/xntpd/kernel/tty_chu.c b/usr.sbin/xntpd/kernel/tty_chu.c
new file mode 100644
index 0000000..4615875
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/tty_chu.c
@@ -0,0 +1,276 @@
+/* tty_chu.c,v 3.1 1993/07/06 01:07:30 jbj Exp
+ * tty_chu.c - CHU line driver
+ */
+
+#include "chu.h"
+#if NCHU > 0
+
+#include "../h/param.h"
+#include "../h/types.h"
+#include "../h/systm.h"
+#include "../h/dir.h"
+#include "../h/user.h"
+#include "../h/ioctl.h"
+#include "../h/tty.h"
+#include "../h/proc.h"
+#include "../h/file.h"
+#include "../h/conf.h"
+#include "../h/buf.h"
+#include "../h/uio.h"
+
+#include "../h/chudefs.h"
+
+/*
+ * Line discipline for receiving CHU time codes.
+ * Does elementary noise elimination, takes time stamps after
+ * the arrival of each character, returns a buffer full of the
+ * received 10 character code and the associated time stamps.
+ */
+#define NUMCHUBUFS 3
+
+struct chudata {
+ u_char used; /* Set to 1 when structure in use */
+ u_char lastindex; /* least recently used buffer */
+ u_char curindex; /* buffer to use */
+ u_char sleeping; /* set to 1 when we're sleeping on a buffer */
+ struct chucode chubuf[NUMCHUBUFS];
+} chu_data[NCHU];
+
+/*
+ * Number of microseconds we allow between
+ * character arrivals. The speed is 300 baud
+ * so this should be somewhat more than 30 msec
+ */
+#define CHUMAXUSEC (50*1000) /* 50 msec */
+
+int chu_debug = 0;
+
+/*
+ * Open as CHU time discipline. Called when discipline changed
+ * with ioctl, and changes the interpretation of the information
+ * in the tty structure.
+ */
+/*ARGSUSED*/
+chuopen(dev, tp)
+ dev_t dev;
+ register struct tty *tp;
+{
+ register struct chudata *chu;
+
+ /*
+ * Don't allow multiple opens. This will also protect us
+ * from someone opening /dev/tty
+ */
+ if (tp->t_line == CHULDISC)
+ return (EBUSY);
+ ttywflush(tp);
+ for (chu = chu_data; chu < &chu_data[NCHU]; chu++)
+ if (!chu->used)
+ break;
+ if (chu >= &chu[NCHU])
+ return (EBUSY);
+ chu->used++;
+ chu->lastindex = chu->curindex = 0;
+ chu->sleeping = 0;
+ chu->chubuf[0].ncodechars = 0;
+ tp->T_LINEP = (caddr_t) chu;
+ return (0);
+}
+
+/*
+ * Break down... called when discipline changed or from device
+ * close routine.
+ */
+chuclose(tp)
+ register struct tty *tp;
+{
+ register int s = spl5();
+
+ ((struct chudata *) tp->T_LINEP)->used = 0;
+ tp->t_cp = 0;
+ tp->t_inbuf = 0;
+ tp->t_rawq.c_cc = 0; /* clear queues -- paranoid */
+ tp->t_canq.c_cc = 0;
+ tp->t_line = 0; /* paranoid: avoid races */
+ splx(s);
+}
+
+/*
+ * Read a CHU buffer. Sleep on the current buffer
+ */
+churead(tp, uio)
+ register struct tty *tp;
+ struct uio *uio;
+{
+ register struct chudata *chu;
+ register struct chucode *chucode;
+ register int s;
+
+ if ((tp->t_state&TS_CARR_ON)==0)
+ return (EIO);
+
+ chu = (struct chudata *) (tp->T_LINEP);
+
+ s = spl5();
+ chucode = &(chu->chubuf[chu->lastindex]);
+ while (chu->curindex == chu->lastindex) {
+ chu->sleeping = 1;
+ sleep((caddr_t)chucode, TTIPRI);
+ }
+ chu->sleeping = 0;
+ if (++(chu->lastindex) >= NUMCHUBUFS)
+ chu->lastindex = 0;
+ splx(s);
+
+ return (uiomove((caddr_t)chucode, sizeof(*chucode), UIO_READ, uio));
+}
+
+/*
+ * Low level character input routine.
+ * If the character looks okay, grab a time stamp. If the stuff in
+ * the buffer is too old, dump it and start fresh. If the character is
+ * non-BCDish, everything in the buffer too.
+ */
+chuinput(c, tp)
+ register int c;
+ register struct tty *tp;
+{
+ register struct chudata *chu = (struct chudata *) tp->T_LINEP;
+ register struct chucode *chuc;
+ register int i;
+ long sec, usec;
+ struct timeval tv;
+
+ /*
+ * Do a check on the BSDness of the character. This delays
+ * the time stamp a bit but saves a fair amount of overhead
+ * when the static is bad.
+ */
+ if (((c) & 0xf) > 9 || (((c)>>4) & 0xf) > 9) {
+ chuc = &(chu->chubuf[chu->curindex]);
+ chuc->ncodechars = 0; /* blow all previous away */
+ return;
+ }
+
+ /*
+ * Call microtime() to get the current time of day
+ */
+ microtime(&tv);
+
+ /*
+ * Compute the difference in this character's time stamp
+ * and the last. If it exceeds the margin, blow away all
+ * the characters currently in the buffer.
+ */
+ chuc = &(chu->chubuf[chu->curindex]);
+ i = (int)chuc->ncodechars;
+ if (i > 0) {
+ sec = tv.tv_sec - chuc->codetimes[i-1].tv_sec;
+ usec = tv.tv_usec - chuc->codetimes[i-1].tv_usec;
+ if (usec < 0) {
+ sec -= 1;
+ usec += 1000000;
+ }
+ if (sec != 0 || usec > CHUMAXUSEC) {
+ i = 0;
+ chuc->ncodechars = 0;
+ }
+ }
+
+ /*
+ * Store the character. If we're done, have to tell someone
+ */
+ chuc->codechars[i] = (u_char)c;
+ chuc->codetimes[i] = tv;
+
+ if (++i < NCHUCHARS) {
+ /*
+ * Not much to do here. Save the count and wait
+ * for another character.
+ */
+ chuc->ncodechars = (u_char)i;
+ } else {
+ /*
+ * Mark this buffer full and point at next. If the
+ * next buffer is full we overwrite it by bumping the
+ * next pointer.
+ */
+ chuc->ncodechars = NCHUCHARS;
+ if (++(chu->curindex) >= NUMCHUBUFS)
+ chu->curindex = 0;
+ if (chu->curindex == chu->lastindex)
+ if (++(chu->lastindex) >= NUMCHUBUFS)
+ chu->lastindex = 0;
+ chu->chubuf[chu->curindex].ncodechars = 0;
+
+ /*
+ * Wake up anyone sleeping on this. Also wake up
+ * selectors and/or deliver a SIGIO as required.
+ */
+ if (tp->t_rsel) {
+ selwakeup(tp->t_rsel, tp->t_state&TS_RCOLL);
+ tp->t_state &= ~TS_RCOLL;
+ tp->t_rsel = 0;
+ }
+ if (tp->t_state & TS_ASYNC)
+ gsignal(tp->t_pgrp, SIGIO);
+ if (chu->sleeping)
+ (void) wakeup((caddr_t)chuc);
+ }
+}
+
+/*
+ * Handle ioctls. We reject all tty-style except those that
+ * change the line discipline.
+ */
+chuioctl(tp, cmd, data, flag)
+ struct tty *tp;
+ int cmd;
+ caddr_t data;
+ int flag;
+{
+
+ if ((cmd>>8) != 't')
+ return (-1);
+ switch (cmd) {
+ case TIOCSETD:
+ case TIOCGETD:
+ case TIOCGETP:
+ case TIOCGETC:
+ return (-1);
+ }
+ return (ENOTTY); /* not quite appropriate */
+}
+
+
+chuselect(dev, rw)
+ dev_t dev;
+ int rw;
+{
+ register struct tty *tp = &cdevsw[major(dev)].d_ttys[minor(dev)];
+ struct chudata *chu;
+ int s = spl5();
+
+ chu = (struct chudata *) (tp->T_LINEP);
+
+ switch (rw) {
+
+ case FREAD:
+ if (chu->curindex != chu->lastindex)
+ goto win;
+ if (tp->t_rsel && tp->t_rsel->p_wchan == (caddr_t)&selwait)
+ tp->t_state |= TS_RCOLL;
+ else
+ tp->t_rsel = u.u_procp;
+ break;
+
+ case FWRITE:
+ goto win;
+ }
+ splx(s);
+ return (0);
+win:
+ splx(s);
+ return (1);
+}
+#endif NCHU
diff --git a/usr.sbin/xntpd/kernel/tty_chu_STREAMS.c b/usr.sbin/xntpd/kernel/tty_chu_STREAMS.c
new file mode 100644
index 0000000..eea792d
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/tty_chu_STREAMS.c
@@ -0,0 +1,444 @@
+/* tty_chu_STREAMS.c,v 3.1 1993/07/06 01:07:32 jbj Exp
+ * CHU STREAMS module for SunOS 4.1.x
+ *
+ * Version 2.1
+ *
+ * Copyright 1991-1993, Nick Sayer
+ *
+ * Special thanks to Greg Onufer for his debug assists.
+ * Special thanks to Matthias Urlichs for the loadable driver support
+ * code.
+ *
+ * Should be PUSHed directly on top of a serial I/O channel.
+ * Provides complete chucode structures to user space.
+ *
+ * COMPILATION:
+ *
+ * To make a SunOS 4.1.x compatable loadable module (from the ntp kernel
+ * directory):
+ *
+ * % cc -c -I../include -DLOADABLE tty_chu_STREAMS.c
+ *
+ * The resulting .o file is the loadable module. Modload it
+ * with -entry _chuinit.
+ *
+ * You can also add it into the kernel by hacking it into the streams
+ * table in the kernel, then adding it to config:
+ *
+ * pseudo-device chuN
+ *
+ * where N is the maximum number of concurent chu sessions you expect
+ * to have.
+ *
+ * HISTORY:
+ *
+ * v2.1 - Added 'sixth byte' heuristics.
+ * v2.0 - first version with an actual version number.
+ * Added support for new CHU 'second 31' data format.
+ * Deleted PEDANTIC and ANAL_RETENTIVE.
+ *
+ */
+
+#ifndef LOADABLE
+# include "chu.h"
+#else
+# ifndef NCHU
+# define NCHU 3
+# define KERNEL
+# endif
+#endif
+
+#if NCHU > 0
+
+/*
+ * Number of microseconds we allow between
+ * character arrivals. The speed is 300 baud
+ * so this should be somewhat more than 30 msec
+ */
+#define CHUMAXUSEC (60*1000) /* 60 msec */
+
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/user.h>
+
+#include <sys/chudefs.h>
+
+static struct module_info rminfo = { 0, "chu", 0, INFPSZ, 0, 0 };
+static struct module_info wminfo = { 0, "chu", 0, INFPSZ, 0, 0 };
+static int chuopen(), churput(), chuwput(), chuclose();
+
+static struct qinit rinit = { churput, NULL, chuopen, chuclose, NULL,
+ &rminfo, NULL };
+
+static struct qinit winit = { chuwput, NULL, NULL, NULL, NULL,
+ &wminfo, NULL };
+
+struct streamtab chuinfo = { &rinit, &winit, NULL, NULL };
+
+/*
+ * Here's our private data type and structs
+ */
+struct priv_data
+{
+ char in_use;
+ struct chucode chu_struct;
+} our_priv_data[NCHU];
+
+#ifdef LOADABLE
+
+#ifdef sun
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sundev/mbvar.h>
+#include <sun/autoconf.h>
+#include <sun/vddrv.h>
+
+static struct vdldrv vd =
+{
+ VDMAGIC_PSEUDO,
+ "chu",
+ NULL, NULL, NULL, 0, 0, NULL, NULL, 0, 0,
+};
+
+static struct fmodsw *chu_fmod;
+
+/*ARGSUSED*/
+chuinit (fc, vdp, vdi, vds)
+ unsigned int fc;
+ struct vddrv *vdp;
+ addr_t vdi;
+ struct vdstat *vds;
+{
+ switch (fc) {
+ case VDLOAD:
+ {
+ int dev, i;
+
+ /* Find free entry in fmodsw */
+ for (dev = 0; dev < fmodcnt; dev++) {
+ if (fmodsw[dev].f_str == NULL)
+ break;
+ }
+ if (dev == fmodcnt)
+ return (ENODEV);
+ chu_fmod = &fmodsw[dev];
+
+ /* If you think a kernel would have strcpy() you're mistaken. */
+ for (i = 0; i <= FMNAMESZ; i++)
+ chu_fmod->f_name[i] = wminfo.mi_idname[i];
+
+ chu_fmod->f_str = &chuinfo;
+ }
+ vdp->vdd_vdtab = (struct vdlinkage *) & vd;
+
+ {
+ int i;
+
+ for (i=0; i<NCHU; i++)
+ our_priv_data[i].in_use=0;
+ }
+
+ return 0;
+ case VDUNLOAD:
+ {
+ int dev;
+
+ for (dev = 0; dev < NCHU; dev++)
+ if (our_priv_data[dev].in_use) {
+ /* One of the modules is still open */
+ return (EBUSY);
+ }
+ }
+ chu_fmod->f_name[0] = '\0';
+ chu_fmod->f_str = NULL;
+ return 0;
+ case VDSTAT:
+ return 0;
+ default:
+ return EIO;
+ }
+}
+
+#endif
+
+#else
+
+char chu_first_open=1;
+
+#endif
+
+/*ARGSUSED*/
+static int chuopen(q, dev, flag, sflag)
+queue_t *q;
+dev_t dev;
+int flag;
+int sflag;
+{
+ int i;
+
+#ifndef LOADABLE
+ if (chu_first_open)
+ {
+ chu_first_open=0;
+
+ for(i=0;i<NCHU;i++)
+ our_priv_data[i].in_use=0;
+ }
+#endif
+
+ for(i=0;i<NCHU;i++)
+ if (!our_priv_data[i].in_use)
+ {
+ ((struct priv_data *) (q->q_ptr))=&(our_priv_data[i]);
+ our_priv_data[i].in_use++;
+ our_priv_data[i].chu_struct.ncodechars = 0;
+ return 0;
+ }
+
+ u.u_error = EBUSY;
+ return (OPENFAIL);
+
+}
+
+/*ARGSUSED*/
+static int chuclose(q, flag)
+queue_t *q;
+int flag;
+{
+ ((struct priv_data *) (q->q_ptr))->in_use=0;
+
+ return (0);
+}
+
+/*
+ * Now the crux of the biscuit.
+ *
+ * We will be passed data from the man downstairs. If it's not a data
+ * packet, it must be important, so pass it along unmunged. If, however,
+ * it is a data packet, we're gonna do special stuff to it. We're going
+ * to pass each character we get to the old line discipline code we
+ * include below for just such an occasion. When the old ldisc code
+ * gets a full chucode struct, we'll hand it back upstairs.
+ *
+ * chuinput takes a single character and q (as quickly as possible).
+ * passback takes a pointer to a chucode struct and q and sends it upstream.
+ */
+
+void chuinput();
+void passback();
+
+static int churput(q, mp)
+queue_t *q;
+mblk_t *mp;
+{
+ mblk_t *bp;
+
+ switch(mp->b_datap->db_type)
+ {
+ case M_DATA:
+ for(bp=mp; bp!=NULL; bp=bp->b_cont)
+ {
+ while(bp->b_rptr < bp->b_wptr)
+ chuinput( ((u_char)*(bp->b_rptr++)) , q );
+ }
+ freemsg(mp);
+ break;
+ default:
+ putnext(q,mp);
+ break;
+ }
+
+}
+
+/*
+ * Writing to a chu device doesn't make sense, but we'll pass them
+ * through in case they're important.
+ */
+
+static int chuwput(q, mp)
+queue_t *q;
+mblk_t *mp;
+{
+ putnext(q,mp);
+}
+
+/*
+ * Take a pointer to a filled chucode struct and a queue and
+ * send the chucode stuff upstream
+ */
+
+void passback(outdata,q)
+struct chucode *outdata;
+queue_t *q;
+{
+ mblk_t *mp;
+ int j;
+
+ mp=(mblk_t*) allocb(sizeof(struct chucode),BPRI_LO);
+
+ if (mp==NULL)
+ {
+ log(LOG_ERR,"chu: cannot allocate message");
+ return;
+ }
+
+ for(j=0;j<sizeof(struct chucode); j++)
+ *mp->b_wptr++ = *( ((char*)outdata) + j );
+
+ putnext(q,mp);
+}
+
+/*
+ * This routine was copied nearly verbatim from the old line discipline.
+ */
+void chuinput(c,q)
+register u_char c;
+queue_t *q;
+{
+ register struct chucode *chuc;
+ register int i;
+ long sec, usec;
+ struct timeval tv;
+
+ /*
+ * Quick, Batman, get a timestamp! We need to do this
+ * right away. The time between the end of the stop bit
+ * and this point is critical, and should be as nearly
+ * constant and as short as possible. (Un)fortunately,
+ * the Sun's clock granularity is so big this isn't a
+ * major problem.
+ *
+ * uniqtime() is totally undocumented, but there you are.
+ */
+ uniqtime(&tv);
+
+ /*
+ * Now, locate the chu struct once so we don't have to do it
+ * over and over.
+ */
+ chuc=&(((struct priv_data *) (q->q_ptr))->chu_struct);
+
+ /*
+ * Compute the difference in this character's time stamp
+ * and the last. If it exceeds the margin, blow away all
+ * the characters currently in the buffer.
+ */
+ i = (int)chuc->ncodechars;
+ if (i > 0)
+ {
+ sec = tv.tv_sec - chuc->codetimes[i-1].tv_sec;
+ usec = tv.tv_usec - chuc->codetimes[i-1].tv_usec;
+ if (usec < 0)
+ {
+ sec -= 1;
+ usec += 1000000;
+ }
+ if (sec != 0 || usec > CHUMAXUSEC)
+ {
+ i = 0;
+ chuc->ncodechars = 0;
+ }
+ }
+
+ /*
+ * Store the character.
+ */
+ chuc->codechars[i] = (u_char)c;
+ chuc->codetimes[i] = tv;
+
+ /*
+ * Now we perform the 'sixth byte' heuristics.
+ *
+ * This is a long story.
+ *
+ * We used to be able to count on the first byte of the code
+ * having a '6' in the LSD. This prevented most code framing
+ * errors (garbage before the first byte wouldn't typically
+ * have a 6 in the LSD). That's no longer the case.
+ *
+ * We can get around this, however, by noting that the 6th byte
+ * must be either equal to or one's complement of the first.
+ * If we get a sixth byte that ISN'T like that, then it may
+ * well be that the first byte is garbage. The right thing
+ * to do is to left-shift the whole buffer one count and
+ * continue to wait for the sixth byte.
+ */
+ if (i == NCHUCHARS/2)
+ {
+ register u_char temp_byte;
+
+ temp_byte=chuc->codechars[i] ^ chuc->codechars[0];
+
+ if ( (temp_byte) && (temp_byte!=0xff) )
+ {
+ register int t;
+ /*
+ * No match. Left-shift the buffer and try again
+ */
+ for(t=0;t<=NCHUCHARS/2;t++)
+ {
+ chuc->codechars[t]=chuc->codechars[t+1];
+ chuc->codetimes[t]=chuc->codetimes[t+1];
+ }
+
+ i--; /* This is because of the ++i immediately following */
+ }
+ }
+
+ /*
+ * We done yet?
+ */
+ if (++i < NCHUCHARS)
+ {
+ /*
+ * We're not done. Not much to do here. Save the count and wait
+ * for another character.
+ */
+ chuc->ncodechars = (u_char)i;
+ }
+ else
+ {
+ /*
+ * We are done. Mark this buffer full and pass it along.
+ */
+ chuc->ncodechars = NCHUCHARS;
+
+ /*
+ * Now we have a choice. Either the front half and back half
+ * have to match, or be one's complement of each other.
+ *
+ * So let's try the first byte and see
+ */
+
+ if(chuc->codechars[0] == chuc->codechars[NCHUCHARS/2])
+ {
+ chuc->chutype = CHU_TIME;
+ for( i=0; i<(NCHUCHARS/2); i++)
+ if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)])
+ {
+ chuc->ncodechars = 0;
+ return;
+ }
+ }
+ else
+ {
+ chuc->chutype = CHU_YEAR;
+ for( i=0; i<(NCHUCHARS/2); i++)
+ if (((chuc->codechars[i] ^ chuc->codechars[i+(NCHUCHARS/2)]) & 0xff)
+ != 0xff )
+ {
+ chuc->ncodechars = 0;
+ return;
+ }
+ }
+
+ passback(chuc,q); /* We're done! */
+ chuc->ncodechars = 0; /* Start all over again! */
+ }
+}
+
+#endif
diff --git a/usr.sbin/xntpd/kernel/tty_clk.c b/usr.sbin/xntpd/kernel/tty_clk.c
new file mode 100644
index 0000000..d1b4bbe
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/tty_clk.c
@@ -0,0 +1,303 @@
+/* tty_clk.c,v 3.1 1993/07/06 01:07:33 jbj Exp
+ * tty_clk.c - Generic line driver for receiving radio clock timecodes
+ */
+
+#include "clk.h"
+#if NCLK > 0
+
+#include "../h/param.h"
+#include "../h/types.h"
+#include "../h/systm.h"
+#include "../h/dir.h"
+#include "../h/user.h"
+#include "../h/ioctl.h"
+#include "../h/tty.h"
+#include "../h/proc.h"
+#include "../h/file.h"
+#include "../h/conf.h"
+#include "../h/buf.h"
+#include "../h/uio.h"
+#include "../h/clist.h"
+
+/*
+ * This line discipline is intended to provide well performing
+ * generic support for the reception and time stamping of radio clock
+ * timecodes. Most radio clock devices return a string where a
+ * particular character in the code (usually a \r) is on-time
+ * synchronized with the clock. The idea here is to collect characters
+ * until (one of) the synchronization character(s) (we allow two) is seen.
+ * When the magic character arrives we take a timestamp by calling
+ * microtime() and insert the eight bytes of struct timeval into the
+ * buffer after the magic character. We then wake up anyone waiting
+ * for the buffer and return the whole mess on the next read.
+ *
+ * To use this the calling program is expected to first open the
+ * port, and then to set the port into raw mode with the speed
+ * set appropriately with a TIOCSETP ioctl(), with the erase and kill
+ * characters set to those to be considered magic (yes, I know this
+ * is gross, but they were so convenient). If only one character is
+ * magic you can set then both the same, or perhaps to the alternate
+ * parity versions of said character. After getting all this set,
+ * change the line discipline to CLKLDISC and you are on your way.
+ *
+ * The only other bit of magic we do in here is to flush the receive
+ * buffers on writes if the CRMOD flag is set (hack, hack).
+ */
+
+/*
+ * We run this very much like a raw mode terminal, with the exception
+ * that we store up characters locally until we hit one of the
+ * magic ones and then dump it into the rawq all at once. We keep
+ * the buffered data in clists since we can then often move it to
+ * the rawq without copying. For sanity we limit the number of
+ * characters between specials, and the total number of characters
+ * before we flush the rawq, as follows.
+ */
+#define CLKLINESIZE (256)
+#define NCLKCHARS (CLKLINESIZE*4)
+
+struct clkdata {
+ int inuse;
+ struct clist clkbuf;
+};
+#define clk_cc clkbuf.c_cc
+#define clk_cf clkbuf.c_cf
+#define clk_cl clkbuf.c_cl
+
+struct clkdata clk_data[NCLK];
+
+/*
+ * Routine for flushing the internal clist
+ */
+#define clk_bflush(clk) (ndflush(&((clk)->clkbuf), (clk)->clk_cc))
+
+int clk_debug = 0;
+
+/*ARGSUSED*/
+clkopen(dev, tp)
+ dev_t dev;
+ register struct tty *tp;
+{
+ register struct clkdata *clk;
+
+ /*
+ * Don't allow multiple opens. This will also protect us
+ * from someone opening /dev/tty
+ */
+ if (tp->t_line == CLKLDISC)
+ return (EBUSY);
+ ttywflush(tp);
+ for (clk = clk_data; clk < &clk_data[NCLK]; clk++)
+ if (!clk->inuse)
+ break;
+ if (clk >= &clk_data[NCLK])
+ return (EBUSY);
+ clk->inuse++;
+ clk->clk_cc = 0;
+ clk->clk_cf = clk->clk_cl = NULL;
+ tp->T_LINEP = (caddr_t) clk;
+ return (0);
+}
+
+
+/*
+ * Break down... called when discipline changed or from device
+ * close routine.
+ */
+clkclose(tp)
+ register struct tty *tp;
+{
+ register struct clkdata *clk;
+ register int s = spltty();
+
+ clk = (struct clkdata *)tp->T_LINEP;
+ if (clk->clk_cc > 0)
+ clk_bflush(clk);
+ clk->inuse = 0;
+ tp->t_line = 0; /* paranoid: avoid races */
+ splx(s);
+}
+
+
+/*
+ * Receive a write request. We pass these requests on to the terminal
+ * driver, except that if the CRMOD bit is set in the flags we
+ * first flush the input queues.
+ */
+clkwrite(tp, uio)
+ register struct tty *tp;
+ struct uio *uio;
+{
+ if (tp->t_flags & CRMOD) {
+ register struct clkdata *clk;
+ int s;
+
+ s = spltty();
+ if (tp->t_rawq.c_cc > 0)
+ ndflush(&tp->t_rawq, tp->t_rawq.c_cc);
+ clk = (struct clkdata *) tp->T_LINEP;
+ if (clk->clk_cc > 0)
+ clk_bflush(clk);
+ (void)splx(s);
+ }
+ ttwrite(tp, uio);
+}
+
+
+/*
+ * Low level character input routine.
+ * If the character looks okay, grab a time stamp. If the stuff in
+ * the buffer is too old, dump it and start fresh. If the character is
+ * non-BCDish, everything in the buffer too.
+ */
+clkinput(c, tp)
+ register int c;
+ register struct tty *tp;
+{
+ register struct clkdata *clk;
+ register int i;
+ register long s;
+ struct timeval tv;
+
+ /*
+ * Check to see whether this isn't the magic character. If not,
+ * save the character and return.
+ */
+#ifdef ultrix
+ if (c != tp->t_cc[VERASE] && c != tp->t_cc[VKILL]) {
+#else
+ if (c != tp->t_erase && c != tp->t_kill) {
+#endif
+ clk = (struct clkdata *) tp->T_LINEP;
+ if (clk->clk_cc >= CLKLINESIZE)
+ clk_bflush(clk);
+ if (putc(c, &clk->clkbuf) == -1) {
+ /*
+ * Hopeless, no clists. Flush what we have
+ * and hope things improve.
+ */
+ clk_bflush(clk);
+ }
+ return;
+ }
+
+ /*
+ * Here we have a magic character. Get a timestamp and store
+ * everything.
+ */
+ microtime(&tv);
+ clk = (struct clkdata *) tp->T_LINEP;
+
+ if (putc(c, &clk->clkbuf) == -1)
+ goto flushout;
+
+ s = tv.tv_sec;
+ for (i = 0; i < sizeof(long); i++) {
+ if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
+ goto flushout;
+ s <<= 8;
+ }
+
+ s = tv.tv_usec;
+ for (i = 0; i < sizeof(long); i++) {
+ if (putc((s >> 24) & 0xff, &clk->clkbuf) == -1)
+ goto flushout;
+ s <<= 8;
+ }
+
+ /*
+ * If the length of the rawq exceeds our sanity limit, dump
+ * all the old crap in there before copying this in.
+ */
+ if (tp->t_rawq.c_cc > NCLKCHARS)
+ ndflush(&tp->t_rawq, tp->t_rawq.c_cc);
+
+ /*
+ * Now copy the buffer in. There is a special case optimization
+ * here. If there is nothing on the rawq at present we can
+ * just copy the clists we own over. Otherwise we must concatenate
+ * the present data on the end.
+ */
+ s = (long)spltty();
+ if (tp->t_rawq.c_cc <= 0) {
+ tp->t_rawq = clk->clkbuf;
+ clk->clk_cc = 0;
+ clk->clk_cl = clk->clk_cf = NULL;
+ (void) splx((int)s);
+ } else {
+ (void) splx((int)s);
+ catq(&clk->clkbuf, &tp->t_rawq);
+ clk_bflush(clk);
+ }
+
+ /*
+ * Tell the world
+ */
+ ttwakeup(tp);
+ return;
+
+flushout:
+ /*
+ * It would be nice if this never happened. Flush the
+ * internal clists and hope someone else frees some of them
+ */
+ clk_bflush(clk);
+ return;
+}
+
+
+/*
+ * Handle ioctls. We reject most tty-style except those that
+ * change the line discipline and a couple of others..
+ */
+clkioctl(tp, cmd, data, flag)
+ struct tty *tp;
+ int cmd;
+ caddr_t data;
+ int flag;
+{
+ int flags;
+ struct sgttyb *sg;
+
+ if ((cmd>>8) != 't')
+ return (-1);
+ switch (cmd) {
+ case TIOCSETD:
+ case TIOCGETD:
+ case TIOCGETP:
+ case TIOCGETC:
+ case TIOCOUTQ:
+ return (-1);
+
+ case TIOCSETP:
+ /*
+ * He likely wants to set new magic characters in.
+ * Do this part.
+ */
+ sg = (struct sgttyb *)data;
+#ifdef ultrix
+ tp->t_cc[VERASE] = sg->sg_erase;
+ tp->t_cc[VKILL] = sg->sg_kill;
+#else
+ tp->t_erase = sg->sg_erase;
+ tp->t_kill = sg->sg_kill;
+#endif
+ return (0);
+
+ case TIOCFLUSH:
+ flags = *(int *)data;
+ if (flags == 0 || (flags & FREAD)) {
+ register struct clkdata *clk;
+
+ clk = (struct clkdata *) tp->T_LINEP;
+ if (clk->clk_cc > 0)
+ clk_bflush(clk);
+ }
+ return (-1);
+
+ default:
+ break;
+ }
+ return (ENOTTY); /* not quite appropriate */
+}
+#endif NCLK
diff --git a/usr.sbin/xntpd/kernel/tty_clk_STREAMS.c b/usr.sbin/xntpd/kernel/tty_clk_STREAMS.c
new file mode 100644
index 0000000..f41af28
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/tty_clk_STREAMS.c
@@ -0,0 +1,265 @@
+/* tty_clk_STREAMS.c,v 3.1 1993/07/06 01:07:34 jbj Exp
+ * Timestamp STREAMS module for SunOS 4.1
+ *
+ * Copyright 1991, Nick Sayer
+ *
+ * Special thanks to Greg Onufer for his debug assists.
+ *
+ * Should be PUSHed directly on top of a serial I/O channel.
+ * For any character in a user-designated set, adds a kernel
+ * timestamp to that character.
+ *
+ * BUGS:
+ *
+ * Only so many characters can be timestamped. This number, however,
+ * is adjustable.
+ *
+ * The null character ($00) cannot be timestamped.
+ *
+ * The M_DATA messages passed upstream will not be the same
+ * size as when they arrive from downstream, even if no
+ * timestamp character is in the message. This, however,
+ * should not affect anything.
+ *
+ */
+
+#include "clk.h"
+#if NCLK > 0
+/*
+ * How big should the messages we pass upstream be?
+ */
+#define MESSAGE_SIZE 128
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/user.h>
+#include <sys/errno.h>
+
+#include <sys/clkdefs.h>
+
+static struct module_info rminfo = { 0, "clk", 0, INFPSZ, 0, 0 };
+static struct module_info wminfo = { 0, "clk", 0, INFPSZ, 0, 0 };
+static int clkopen(), clkrput(), clkwput(), clkclose();
+
+static struct qinit rinit = { clkrput, NULL, clkopen, clkclose, NULL,
+ &rminfo, NULL };
+
+static struct qinit winit = { clkwput, NULL, NULL, NULL, NULL,
+ &wminfo, NULL };
+
+struct streamtab clkinfo = { &rinit, &winit, NULL, NULL };
+
+struct priv_data_type
+{
+ char in_use;
+ char string[CLK_MAXSTRSIZE];
+} priv_data[NCLK];
+
+char first_open=1;
+
+/*
+ * God only knows why, but linking with strchr() and index() fail
+ * on my system, so here's a renamed copy.
+ */
+
+u_char *str_chr(s,c)
+u_char *s;
+int c;
+{
+ while (*s)
+ if(*s++ == c)
+ return (s-1);
+ return NULL;
+}
+
+/*ARGSUSED*/
+static int clkopen(q, dev, flag, sflag)
+queue_t *q;
+dev_t dev;
+int flag;
+int sflag;
+{
+ int i;
+
+/* Damn it! We can't even have the global data struct properly
+ initialized! So we have a mark to tell us to init the global
+ data on the first open */
+
+ if (first_open)
+ {
+ first_open=0;
+
+ for(i=0;i<NCLK;i++)
+ priv_data[i].in_use=0;
+ }
+
+ for(i=0;i<NCLK;i++)
+ if(!priv_data[i].in_use)
+ {
+ priv_data[i].in_use++;
+ ((struct priv_data_type *) (q->q_ptr))=priv_data+i;
+ priv_data[i].string[0]=0;
+ return (0);
+ }
+ u.u_error = EBUSY;
+ return (OPENFAIL);
+}
+
+/*ARGSUSED*/
+static int clkclose(q, flag)
+queue_t *q;
+int flag;
+{
+ ((struct priv_data_type *) (q->q_ptr))->in_use=0;
+
+ return (0);
+}
+
+/*
+ * Now the crux of the biscuit.
+ *
+ * If it's an M_DATA package, we take each character and pass
+ * it to clkchar.
+ */
+
+void clkchar();
+
+static int clkrput(q, mp)
+queue_t *q;
+mblk_t *mp;
+{
+ mblk_t *bp;
+
+ switch(mp->b_datap->db_type)
+ {
+ case M_DATA:
+ clkchar(0,q,2);
+ for(bp=mp; bp!=NULL; bp=bp->b_cont)
+ {
+ while(bp->b_rptr < bp->b_wptr)
+ clkchar( ((u_char)*(bp->b_rptr++)) , q , 0 );
+ }
+ clkchar(0,q,1);
+ freemsg(mp);
+ break;
+ default:
+ putnext(q,mp);
+ break;
+ }
+
+}
+
+/*
+ * If it's a matching M_IOCTL, handle it.
+ */
+
+static int clkwput(q, mp)
+queue_t *q;
+mblk_t *mp;
+{
+ struct iocblk *iocp;
+
+ switch(mp->b_datap->db_type)
+ {
+ case M_IOCTL:
+ iocp=(struct iocblk*) mp->b_rptr;
+ if (iocp->ioc_cmd==CLK_SETSTR)
+ {
+ strncpy( ((struct priv_data_type *) (RD(q)->q_ptr))->string,
+ (char *) mp->b_cont->b_rptr,CLK_MAXSTRSIZE);
+ /* make sure it's null terminated */
+ ((struct priv_data_type *) (RD(q)->q_ptr))->string[CLK_MAXSTRSIZE-1]=0;
+ mp->b_datap->db_type = M_IOCACK;
+ qreply(q,mp);
+ }
+ else
+ putnext(q,mp);
+ break;
+ default:
+ putnext(q,mp);
+ break;
+ }
+}
+
+/*
+ * Now clkchar. It takes a character, a queue pointer and an action
+ * flag and depending on the flag either:
+ *
+ * 0 - adds the character to the current message. If there's a
+ * timestamp to be done, do that too. If the message is less than
+ * 8 chars from being full, link in a new one, and set it up for
+ * the next call.
+ *
+ * 1 - sends the whole mess to Valhala.
+ *
+ * 2 - set things up.
+ *
+ * Yeah, it's an ugly hack. Complaints may be filed with /dev/null.
+ */
+
+
+void clkchar(c,q,f)
+ register u_char c;
+ queue_t *q;
+ char f;
+{
+ static char error;
+ static mblk_t *message,*mp;
+ struct timeval tv;
+
+/* Get a timestamp ASAP! */
+ uniqtime(&tv);
+
+ switch(f)
+ {
+ case 1:
+ if (!error)
+ putnext(q,message);
+ break;
+ case 2:
+ mp=message= (mblk_t*) allocb(MESSAGE_SIZE,BPRI_LO);
+ error=(message==NULL);
+ if (error)
+ log(LOG_ERR,"clk: cannot allocate message - data lost");
+ break;
+ case 0:
+ if (error) /* If we had an error, forget it. */
+ return;
+
+ *mp->b_wptr++=c; /* Put the char away first.
+
+ /* If it's in the special string, append a struct timeval */
+
+ if (str_chr( ((struct priv_data_type *) (q->q_ptr))->string ,
+ c )!=NULL)
+ {
+ int i;
+
+ for (i=0;i<sizeof(struct timeval);i++)
+ *mp->b_wptr++= *( ((char*)&tv) + i );
+ }
+
+ /* If we don't have space for a complete struct timeval, and a
+ char, it's time for a new mp block */
+
+ if (((mp->b_wptr-mp->b_rptr)+sizeof(struct timeval)+2)>MESSAGE_SIZE)
+ {
+ mp->b_cont= (mblk_t*) allocb(MESSAGE_SIZE,BPRI_LO);
+ error=(mp->b_cont==NULL);
+ if (error)
+ {
+ log(LOG_ERR,"clk: cannot allocate message - data lost");
+ freemsg(message);
+ }
+ mp=mp->b_cont;
+ }
+
+ break;
+ }
+}
+
+#endif
OpenPOWER on IntegriCloud