summaryrefslogtreecommitdiffstats
path: root/ntpd
diff options
context:
space:
mode:
authorroberto <roberto@FreeBSD.org>2008-08-17 17:37:33 +0000
committerroberto <roberto@FreeBSD.org>2008-08-17 17:37:33 +0000
commit4ded1c1fa0bc21c61f91a2dbe864835986745121 (patch)
tree16d100fbc9dae63888d48b464e471ba0e5065193 /ntpd
parent8b5a86d4fda08a9c68231415812edcb26be52f79 (diff)
downloadFreeBSD-src-4ded1c1fa0bc21c61f91a2dbe864835986745121.zip
FreeBSD-src-4ded1c1fa0bc21c61f91a2dbe864835986745121.tar.gz
Flatten the dist and various 4.n.n trees in preparation of future ntp imports.
Diffstat (limited to 'ntpd')
-rw-r--r--ntpd/Makefile.am60
-rw-r--r--ntpd/Makefile.in960
-rw-r--r--ntpd/check_y2k.c627
-rw-r--r--ntpd/cmd_args.c418
-rw-r--r--ntpd/jupiter.h255
-rw-r--r--ntpd/map_vme.c135
-rw-r--r--ntpd/ntp_config.c2371
-rw-r--r--ntpd/ntp_control.c2928
-rw-r--r--ntpd/ntp_crypto.c4031
-rw-r--r--ntpd/ntp_filegen.c547
-rw-r--r--ntpd/ntp_intres.c1065
-rw-r--r--ntpd/ntp_io.c2257
-rw-r--r--ntpd/ntp_loopfilter.c1000
-rw-r--r--ntpd/ntp_monitor.c335
-rw-r--r--ntpd/ntp_peer.c880
-rw-r--r--ntpd/ntp_proto.c3208
-rw-r--r--ntpd/ntp_refclock.c1129
-rw-r--r--ntpd/ntp_request.c2756
-rw-r--r--ntpd/ntp_restrict.c586
-rw-r--r--ntpd/ntp_timer.c377
-rw-r--r--ntpd/ntp_util.c797
-rw-r--r--ntpd/ntpd.c1274
-rw-r--r--ntpd/ntpsim.c368
-rw-r--r--ntpd/refclock_acts.c984
-rw-r--r--ntpd/refclock_arbiter.c429
-rw-r--r--ntpd/refclock_arc.c1529
-rw-r--r--ntpd/refclock_as2201.c388
-rw-r--r--ntpd/refclock_atom.c504
-rw-r--r--ntpd/refclock_bancomm.c433
-rw-r--r--ntpd/refclock_chronolog.c341
-rw-r--r--ntpd/refclock_chu.c1687
-rw-r--r--ntpd/refclock_conf.c331
-rw-r--r--ntpd/refclock_datum.c869
-rw-r--r--ntpd/refclock_dumbclock.c385
-rw-r--r--ntpd/refclock_fg.c348
-rw-r--r--ntpd/refclock_gpsvme.c622
-rw-r--r--ntpd/refclock_heath.c421
-rw-r--r--ntpd/refclock_hopfpci.c273
-rw-r--r--ntpd/refclock_hopfser.c378
-rw-r--r--ntpd/refclock_hpgps.c604
-rw-r--r--ntpd/refclock_irig.c1049
-rw-r--r--ntpd/refclock_jjy.c710
-rw-r--r--ntpd/refclock_jupiter.c1140
-rw-r--r--ntpd/refclock_leitch.c620
-rw-r--r--ntpd/refclock_local.c264
-rw-r--r--ntpd/refclock_msfees.c1455
-rw-r--r--ntpd/refclock_mx4200.c1654
-rw-r--r--ntpd/refclock_neoclock4x.c1068
-rw-r--r--ntpd/refclock_nmea.c723
-rw-r--r--ntpd/refclock_oncore.c3723
-rw-r--r--ntpd/refclock_palisade.c954
-rw-r--r--ntpd/refclock_palisade.h168
-rw-r--r--ntpd/refclock_parse.c5386
-rw-r--r--ntpd/refclock_pcf.c224
-rw-r--r--ntpd/refclock_pst.c321
-rw-r--r--ntpd/refclock_ptbacts.c16
-rw-r--r--ntpd/refclock_ripencc.c4870
-rw-r--r--ntpd/refclock_shm.c305
-rw-r--r--ntpd/refclock_tpro.c216
-rw-r--r--ntpd/refclock_trak.c359
-rw-r--r--ntpd/refclock_true.c873
-rw-r--r--ntpd/refclock_tt560.c274
-rw-r--r--ntpd/refclock_ulink.c497
-rw-r--r--ntpd/refclock_usno.c674
-rw-r--r--ntpd/refclock_wwv.c2859
-rw-r--r--ntpd/refclock_wwvb.c441
-rw-r--r--ntpd/refclock_zyfer.c346
67 files changed, 70079 insertions, 0 deletions
diff --git a/ntpd/Makefile.am b/ntpd/Makefile.am
new file mode 100644
index 0000000..0fa4e21
--- /dev/null
+++ b/ntpd/Makefile.am
@@ -0,0 +1,60 @@
+#AUTOMAKE_OPTIONS = ../util/ansi2knr no-dependencies
+AUTOMAKE_OPTIONS = ../util/ansi2knr
+bin_PROGRAMS = ntpd @MAKE_NTPDSIM@
+noinst_LIBRARIES = libntpd.a
+INCLUDES = -I$(top_srcdir)/include -I../include
+# LDADD might need RESLIB and ADJLIB.
+# If LIBPARSE, we need libntpd.a 2wagain afterwards...
+LDADD = version.o libntpd.a @LIBPARSE@ libntpd.a
+# ntpd may need:
+# log10 refclock_wwv.o
+# sqrt ntp_control.o
+# floor refclock_wwv.o
+# which are (usually) provided by -lm.
+ntpd_LDADD = $(LDADD) ../libntp/libntp.a -lm @LCRYPTO@
+ntpdsim_LDADD = $(LDADD) ../libntp/libntpsim.a -lm @LCRYPTO@
+ntpdsim_CFLAGS = $(CFLAGS) -DSIM
+check_y2k_LDADD = $(LDADD) ../libntp/libntp.a
+DISTCLEANFILES = .version version.c
+#EXTRA_DIST = ntpd.mak
+ETAGS_ARGS = Makefile.am
+### Y2Kfixes
+check_PROGRAMS = @MAKE_CHECK_Y2K@
+EXTRA_PROGRAMS = check_y2k ntpdsim
+
+check-local: @MAKE_CHECK_Y2K@
+ test -z "@MAKE_CHECK_Y2K@" || ./@MAKE_CHECK_Y2K@
+
+# SIM: cmd_args.c ntp_config.c ntp_io.c ntpd.c + ntpsim.c (include/ntpsim.h)
+# ntp_resolver.c is presently unused...
+ntpd_SOURCES = cmd_args.c ntp_config.c ntp_io.c ntpd.c
+ntpdsim_SOURCES = $(ntpd_SOURCES) ntpsim.c
+libntpd_a_SOURCES = jupiter.h map_vme.c ntp_control.c \
+ ntp_crypto.c ntp_filegen.c \
+ ntp_intres.c ntp_loopfilter.c ntp_monitor.c ntp_peer.c \
+ ntp_proto.c ntp_refclock.c ntp_request.c \
+ ntp_restrict.c ntp_timer.c ntp_util.c \
+ refclock_acts.c refclock_arbiter.c refclock_arc.c refclock_as2201.c \
+ refclock_atom.c refclock_bancomm.c refclock_chronolog.c \
+ refclock_chu.c refclock_conf.c refclock_datum.c refclock_dumbclock.c \
+ refclock_fg.c refclock_gpsvme.c refclock_heath.c refclock_hopfser.c \
+ refclock_hopfpci.c refclock_hpgps.c refclock_irig.c refclock_jjy.c \
+ refclock_jupiter.c refclock_leitch.c refclock_local.c \
+ refclock_msfees.c refclock_mx4200.c refclock_nmea.c refclock_oncore.c \
+ refclock_palisade.c refclock_palisade.h refclock_parse.c \
+ refclock_pcf.c refclock_pst.c refclock_ptbacts.c refclock_shm.c \
+ refclock_tpro.c refclock_trak.c refclock_true.c refclock_tt560.c \
+ refclock_ulink.c refclock_usno.c refclock_wwv.c refclock_wwvb.c \
+ refclock_zyfer.c refclock_ripencc.c refclock_neoclock4x.c
+
+$(PROGRAMS): $(LDADD)
+
+../libntp/libntp.a:
+ cd ../libntp && $(MAKE)
+
+../libparse/libparse.a:
+ cd ../libparse && $(MAKE)
+
+version.o: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ Makefile $(top_srcdir)/version
+ env CSET=`cat $(top_srcdir)/version` $(top_builddir)/scripts/mkver ntpd
+ $(COMPILE) -c version.c
diff --git a/ntpd/Makefile.in b/ntpd/Makefile.in
new file mode 100644
index 0000000..eef0774
--- /dev/null
+++ b/ntpd/Makefile.in
@@ -0,0 +1,960 @@
+# Makefile.in generated by automake 1.7.7 from Makefile.am.
+# @configure_input@
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+# Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+ARLIB_DIR = @ARLIB_DIR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHUTEST = @CHUTEST@
+CLKTEST = @CLKTEST@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DCFD = @DCFD@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EF_LIBS = @EF_LIBS@
+EF_PROGS = @EF_PROGS@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LCRYPTO = @LCRYPTO@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBPARSE = @LIBPARSE@
+LIBS = @LIBS@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MAKE_ADJTIMED = @MAKE_ADJTIMED@
+MAKE_CHECK_Y2K = @MAKE_CHECK_Y2K@
+MAKE_LIBNTPSIM = @MAKE_LIBNTPSIM@
+MAKE_LIBPARSE = @MAKE_LIBPARSE@
+MAKE_LIBPARSE_KERNEL = @MAKE_LIBPARSE_KERNEL@
+MAKE_NTPDSIM = @MAKE_NTPDSIM@
+MAKE_NTPTIME = @MAKE_NTPTIME@
+MAKE_NTP_KEYGEN = @MAKE_NTP_KEYGEN@
+MAKE_PARSEKMODULE = @MAKE_PARSEKMODULE@
+MAKE_SNTP = @MAKE_SNTP@
+MAKE_TICKADJ = @MAKE_TICKADJ@
+MAKE_TIMETRIM = @MAKE_TIMETRIM@
+OBJEXT = @OBJEXT@
+OPENSSL = @OPENSSL@
+OPENSSL_INC = @OPENSSL_INC@
+OPENSSL_LIB = @OPENSSL_LIB@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_PERL = @PATH_PERL@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PATH_SH = @PATH_SH@
+PROPDELAY = @PROPDELAY@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TESTDCF = @TESTDCF@
+U = @U@
+VERSION = @VERSION@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+
+#AUTOMAKE_OPTIONS = ../util/ansi2knr no-dependencies
+AUTOMAKE_OPTIONS = ../util/ansi2knr
+bin_PROGRAMS = ntpd @MAKE_NTPDSIM@
+noinst_LIBRARIES = libntpd.a
+INCLUDES = -I$(top_srcdir)/include -I../include
+# LDADD might need RESLIB and ADJLIB.
+# If LIBPARSE, we need libntpd.a 2wagain afterwards...
+LDADD = version.o libntpd.a @LIBPARSE@ libntpd.a
+# ntpd may need:
+# log10 refclock_wwv.o
+# sqrt ntp_control.o
+# floor refclock_wwv.o
+# which are (usually) provided by -lm.
+ntpd_LDADD = $(LDADD) ../libntp/libntp.a -lm @LCRYPTO@
+ntpdsim_LDADD = $(LDADD) ../libntp/libntpsim.a -lm @LCRYPTO@
+ntpdsim_CFLAGS = $(CFLAGS) -DSIM
+check_y2k_LDADD = $(LDADD) ../libntp/libntp.a
+DISTCLEANFILES = .version version.c
+#EXTRA_DIST = ntpd.mak
+ETAGS_ARGS = Makefile.am
+### Y2Kfixes
+check_PROGRAMS = @MAKE_CHECK_Y2K@
+EXTRA_PROGRAMS = check_y2k ntpdsim
+
+# SIM: cmd_args.c ntp_config.c ntp_io.c ntpd.c + ntpsim.c (include/ntpsim.h)
+# ntp_resolver.c is presently unused...
+ntpd_SOURCES = cmd_args.c ntp_config.c ntp_io.c ntpd.c
+ntpdsim_SOURCES = $(ntpd_SOURCES) ntpsim.c
+libntpd_a_SOURCES = jupiter.h map_vme.c ntp_control.c \
+ ntp_crypto.c ntp_filegen.c \
+ ntp_intres.c ntp_loopfilter.c ntp_monitor.c ntp_peer.c \
+ ntp_proto.c ntp_refclock.c ntp_request.c \
+ ntp_restrict.c ntp_timer.c ntp_util.c \
+ refclock_acts.c refclock_arbiter.c refclock_arc.c refclock_as2201.c \
+ refclock_atom.c refclock_bancomm.c refclock_chronolog.c \
+ refclock_chu.c refclock_conf.c refclock_datum.c refclock_dumbclock.c \
+ refclock_fg.c refclock_gpsvme.c refclock_heath.c refclock_hopfser.c \
+ refclock_hopfpci.c refclock_hpgps.c refclock_irig.c refclock_jjy.c \
+ refclock_jupiter.c refclock_leitch.c refclock_local.c \
+ refclock_msfees.c refclock_mx4200.c refclock_nmea.c refclock_oncore.c \
+ refclock_palisade.c refclock_palisade.h refclock_parse.c \
+ refclock_pcf.c refclock_pst.c refclock_ptbacts.c refclock_shm.c \
+ refclock_tpro.c refclock_trak.c refclock_true.c refclock_tt560.c \
+ refclock_ulink.c refclock_usno.c refclock_wwv.c refclock_wwvb.c \
+ refclock_zyfer.c refclock_ripencc.c refclock_neoclock4x.c
+
+subdir = ntpd
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+
+libntpd_a_AR = $(AR) cru
+libntpd_a_LIBADD =
+am_libntpd_a_OBJECTS = map_vme$U.$(OBJEXT) ntp_control$U.$(OBJEXT) \
+ ntp_crypto$U.$(OBJEXT) ntp_filegen$U.$(OBJEXT) \
+ ntp_intres$U.$(OBJEXT) ntp_loopfilter$U.$(OBJEXT) \
+ ntp_monitor$U.$(OBJEXT) ntp_peer$U.$(OBJEXT) \
+ ntp_proto$U.$(OBJEXT) ntp_refclock$U.$(OBJEXT) \
+ ntp_request$U.$(OBJEXT) ntp_restrict$U.$(OBJEXT) \
+ ntp_timer$U.$(OBJEXT) ntp_util$U.$(OBJEXT) \
+ refclock_acts$U.$(OBJEXT) refclock_arbiter$U.$(OBJEXT) \
+ refclock_arc$U.$(OBJEXT) refclock_as2201$U.$(OBJEXT) \
+ refclock_atom$U.$(OBJEXT) refclock_bancomm$U.$(OBJEXT) \
+ refclock_chronolog$U.$(OBJEXT) refclock_chu$U.$(OBJEXT) \
+ refclock_conf$U.$(OBJEXT) refclock_datum$U.$(OBJEXT) \
+ refclock_dumbclock$U.$(OBJEXT) refclock_fg$U.$(OBJEXT) \
+ refclock_gpsvme$U.$(OBJEXT) refclock_heath$U.$(OBJEXT) \
+ refclock_hopfser$U.$(OBJEXT) refclock_hopfpci$U.$(OBJEXT) \
+ refclock_hpgps$U.$(OBJEXT) refclock_irig$U.$(OBJEXT) \
+ refclock_jjy$U.$(OBJEXT) refclock_jupiter$U.$(OBJEXT) \
+ refclock_leitch$U.$(OBJEXT) refclock_local$U.$(OBJEXT) \
+ refclock_msfees$U.$(OBJEXT) refclock_mx4200$U.$(OBJEXT) \
+ refclock_nmea$U.$(OBJEXT) refclock_oncore$U.$(OBJEXT) \
+ refclock_palisade$U.$(OBJEXT) refclock_parse$U.$(OBJEXT) \
+ refclock_pcf$U.$(OBJEXT) refclock_pst$U.$(OBJEXT) \
+ refclock_ptbacts$U.$(OBJEXT) refclock_shm$U.$(OBJEXT) \
+ refclock_tpro$U.$(OBJEXT) refclock_trak$U.$(OBJEXT) \
+ refclock_true$U.$(OBJEXT) refclock_tt560$U.$(OBJEXT) \
+ refclock_ulink$U.$(OBJEXT) refclock_usno$U.$(OBJEXT) \
+ refclock_wwv$U.$(OBJEXT) refclock_wwvb$U.$(OBJEXT) \
+ refclock_zyfer$U.$(OBJEXT) refclock_ripencc$U.$(OBJEXT) \
+ refclock_neoclock4x$U.$(OBJEXT)
+libntpd_a_OBJECTS = $(am_libntpd_a_OBJECTS)
+EXTRA_PROGRAMS = check_y2k$(EXEEXT) ntpdsim$(EXEEXT)
+bin_PROGRAMS = ntpd$(EXEEXT) @MAKE_NTPDSIM@
+check_PROGRAMS = @MAKE_CHECK_Y2K@
+PROGRAMS = $(bin_PROGRAMS)
+
+check_y2k_SOURCES = check_y2k.c
+check_y2k_OBJECTS = check_y2k$U.$(OBJEXT)
+check_y2k_DEPENDENCIES = version.o libntpd.a libntpd.a \
+ ../libntp/libntp.a
+check_y2k_LDFLAGS =
+am_ntpd_OBJECTS = cmd_args$U.$(OBJEXT) ntp_config$U.$(OBJEXT) \
+ ntp_io$U.$(OBJEXT) ntpd$U.$(OBJEXT)
+ntpd_OBJECTS = $(am_ntpd_OBJECTS)
+ntpd_DEPENDENCIES = version.o libntpd.a libntpd.a ../libntp/libntp.a
+ntpd_LDFLAGS =
+am__objects_1 = ntpdsim-cmd_args$U.$(OBJEXT) \
+ ntpdsim-ntp_config$U.$(OBJEXT) ntpdsim-ntp_io$U.$(OBJEXT) \
+ ntpdsim-ntpd$U.$(OBJEXT)
+am_ntpdsim_OBJECTS = $(am__objects_1) ntpdsim-ntpsim$U.$(OBJEXT)
+ntpdsim_OBJECTS = $(am_ntpdsim_OBJECTS)
+ntpdsim_DEPENDENCIES = version.o libntpd.a libntpd.a \
+ ../libntp/libntpsim.a
+ntpdsim_LDFLAGS =
+
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/check_y2k$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/cmd_args$U.Po ./$(DEPDIR)/map_vme$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_config$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_control$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_crypto$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_filegen$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_intres$U.Po ./$(DEPDIR)/ntp_io$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_loopfilter$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_monitor$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_peer$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_proto$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_refclock$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_request$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_restrict$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_timer$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntp_util$U.Po ./$(DEPDIR)/ntpd$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntpdsim-cmd_args$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntpdsim-ntp_config$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntpdsim-ntp_io$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntpdsim-ntpd$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/ntpdsim-ntpsim$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_acts$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_arbiter$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_arc$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_as2201$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_atom$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_bancomm$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_chronolog$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_chu$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_conf$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_datum$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_dumbclock$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_fg$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_gpsvme$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_heath$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_hopfpci$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_hopfser$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_hpgps$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_irig$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_jjy$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_jupiter$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_leitch$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_local$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_msfees$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_mx4200$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_neoclock4x$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_nmea$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_oncore$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_palisade$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_parse$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_pcf$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_pst$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_ptbacts$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_ripencc$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_shm$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_tpro$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_trak$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_true$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_tt560$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_ulink$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_usno$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_wwv$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_wwvb$U.Po \
+@AMDEP_TRUE@ ./$(DEPDIR)/refclock_zyfer$U.Po
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+DIST_SOURCES = $(libntpd_a_SOURCES) check_y2k.c $(ntpd_SOURCES) \
+ $(ntpdsim_SOURCES)
+DIST_COMMON = $(srcdir)/Makefile.in Makefile.am
+SOURCES = $(libntpd_a_SOURCES) check_y2k.c $(ntpd_SOURCES) $(ntpdsim_SOURCES)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu ntpd/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+
+AR = ar
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libntpd.a: $(libntpd_a_OBJECTS) $(libntpd_a_DEPENDENCIES)
+ -rm -f libntpd.a
+ $(libntpd_a_AR) libntpd.a $(libntpd_a_OBJECTS) $(libntpd_a_LIBADD)
+ $(RANLIB) libntpd.a
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) $$p $(DESTDIR)$(bindir)/$$f"; \
+ $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) $$p $(DESTDIR)$(bindir)/$$f || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f $(DESTDIR)$(bindir)/$$f"; \
+ rm -f $(DESTDIR)$(bindir)/$$f; \
+ done
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+clean-checkPROGRAMS:
+ -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS)
+check_y2k$(EXEEXT): $(check_y2k_OBJECTS) $(check_y2k_DEPENDENCIES)
+ @rm -f check_y2k$(EXEEXT)
+ $(LINK) $(check_y2k_LDFLAGS) $(check_y2k_OBJECTS) $(check_y2k_LDADD) $(LIBS)
+ntpd$(EXEEXT): $(ntpd_OBJECTS) $(ntpd_DEPENDENCIES)
+ @rm -f ntpd$(EXEEXT)
+ $(LINK) $(ntpd_LDFLAGS) $(ntpd_OBJECTS) $(ntpd_LDADD) $(LIBS)
+ntpdsim$(EXEEXT): $(ntpdsim_OBJECTS) $(ntpdsim_DEPENDENCIES)
+ @rm -f ntpdsim$(EXEEXT)
+ $(LINK) $(ntpdsim_LDFLAGS) $(ntpdsim_OBJECTS) $(ntpdsim_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT) core *.core
+
+distclean-compile:
+ -rm -f *.tab.c
+
+ANSI2KNR = ../util/ansi2knr
+../util/ansi2knr:
+ cd ../util && $(MAKE) $(AM_MAKEFLAGS) ansi2knr
+
+mostlyclean-kr:
+ -test "$U" = "" || rm -f *_.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_y2k$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd_args$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map_vme$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_config$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_control$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_crypto$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_filegen$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_intres$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_io$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_loopfilter$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_monitor$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_peer$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_proto$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_refclock$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_request$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_restrict$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_timer$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntp_util$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpd$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-cmd_args$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntp_config$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntp_io$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntpd$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntpdsim-ntpsim$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_acts$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_arbiter$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_arc$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_as2201$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_atom$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_bancomm$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_chronolog$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_chu$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_conf$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_datum$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_dumbclock$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_fg$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_gpsvme$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_heath$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_hopfpci$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_hopfser$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_hpgps$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_irig$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_jjy$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_jupiter$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_leitch$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_local$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_msfees$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_mx4200$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_neoclock4x$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_nmea$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_oncore$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_palisade$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_parse$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_pcf$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_pst$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_ptbacts$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_ripencc$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_shm$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_tpro$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_trak$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_true$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_tt560$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_ulink$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_usno$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_wwv$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_wwvb$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refclock_zyfer$U.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" \
+@am__fastdepCC_TRUE@ -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `test -f '$<' || echo '$(srcdir)/'`$<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" \
+@am__fastdepCC_TRUE@ -c -o $@ `if test -f '$<'; then $(CYGPATH_W) '$<'; else $(CYGPATH_W) '$(srcdir)/$<'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `if test -f '$<'; then $(CYGPATH_W) '$<'; else $(CYGPATH_W) '$(srcdir)/$<'; fi`
+
+ntpdsim-cmd_args$U.o: cmd_args$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-cmd_args$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-cmd_args$U.o `test -f 'cmd_args$U.c' || echo '$(srcdir)/'`cmd_args$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo" "$(DEPDIR)/ntpdsim-cmd_args$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cmd_args$U.c' object='ntpdsim-cmd_args$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-cmd_args$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-cmd_args$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-cmd_args$U.o `test -f 'cmd_args$U.c' || echo '$(srcdir)/'`cmd_args$U.c
+
+ntpdsim-cmd_args$U.obj: cmd_args$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-cmd_args$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-cmd_args$U.obj `if test -f 'cmd_args$U.c'; then $(CYGPATH_W) 'cmd_args$U.c'; else $(CYGPATH_W) '$(srcdir)/cmd_args$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo" "$(DEPDIR)/ntpdsim-cmd_args$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-cmd_args$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cmd_args$U.c' object='ntpdsim-cmd_args$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-cmd_args$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-cmd_args$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-cmd_args$U.obj `if test -f 'cmd_args$U.c'; then $(CYGPATH_W) 'cmd_args$U.c'; else $(CYGPATH_W) '$(srcdir)/cmd_args$U.c'; fi`
+
+ntpdsim-ntp_config$U.o: ntp_config$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_config$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-ntp_config$U.o `test -f 'ntp_config$U.c' || echo '$(srcdir)/'`ntp_config$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo" "$(DEPDIR)/ntpdsim-ntp_config$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_config$U.c' object='ntpdsim-ntp_config$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-ntp_config$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-ntp_config$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_config$U.o `test -f 'ntp_config$U.c' || echo '$(srcdir)/'`ntp_config$U.c
+
+ntpdsim-ntp_config$U.obj: ntp_config$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_config$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-ntp_config$U.obj `if test -f 'ntp_config$U.c'; then $(CYGPATH_W) 'ntp_config$U.c'; else $(CYGPATH_W) '$(srcdir)/ntp_config$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo" "$(DEPDIR)/ntpdsim-ntp_config$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-ntp_config$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_config$U.c' object='ntpdsim-ntp_config$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-ntp_config$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-ntp_config$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_config$U.obj `if test -f 'ntp_config$U.c'; then $(CYGPATH_W) 'ntp_config$U.c'; else $(CYGPATH_W) '$(srcdir)/ntp_config$U.c'; fi`
+
+ntpdsim-ntp_io$U.o: ntp_io$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_io$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-ntp_io$U.o `test -f 'ntp_io$U.c' || echo '$(srcdir)/'`ntp_io$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo" "$(DEPDIR)/ntpdsim-ntp_io$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_io$U.c' object='ntpdsim-ntp_io$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-ntp_io$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-ntp_io$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_io$U.o `test -f 'ntp_io$U.c' || echo '$(srcdir)/'`ntp_io$U.c
+
+ntpdsim-ntp_io$U.obj: ntp_io$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntp_io$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-ntp_io$U.obj `if test -f 'ntp_io$U.c'; then $(CYGPATH_W) 'ntp_io$U.c'; else $(CYGPATH_W) '$(srcdir)/ntp_io$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo" "$(DEPDIR)/ntpdsim-ntp_io$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-ntp_io$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntp_io$U.c' object='ntpdsim-ntp_io$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-ntp_io$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-ntp_io$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntp_io$U.obj `if test -f 'ntp_io$U.c'; then $(CYGPATH_W) 'ntp_io$U.c'; else $(CYGPATH_W) '$(srcdir)/ntp_io$U.c'; fi`
+
+ntpdsim-ntpd$U.o: ntpd$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpd$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-ntpd$U.o `test -f 'ntpd$U.c' || echo '$(srcdir)/'`ntpd$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpd$U.Tpo" "$(DEPDIR)/ntpdsim-ntpd$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-ntpd$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd$U.c' object='ntpdsim-ntpd$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-ntpd$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-ntpd$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd$U.o `test -f 'ntpd$U.c' || echo '$(srcdir)/'`ntpd$U.c
+
+ntpdsim-ntpd$U.obj: ntpd$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpd$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpd$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-ntpd$U.obj `if test -f 'ntpd$U.c'; then $(CYGPATH_W) 'ntpd$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpd$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpd$U.Tpo" "$(DEPDIR)/ntpdsim-ntpd$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-ntpd$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpd$U.c' object='ntpdsim-ntpd$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-ntpd$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-ntpd$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpd$U.obj `if test -f 'ntpd$U.c'; then $(CYGPATH_W) 'ntpd$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpd$U.c'; fi`
+
+ntpdsim-ntpsim$U.o: ntpsim$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpsim$U.o -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-ntpsim$U.o `test -f 'ntpsim$U.c' || echo '$(srcdir)/'`ntpsim$U.c; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo" "$(DEPDIR)/ntpdsim-ntpsim$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpsim$U.c' object='ntpdsim-ntpsim$U.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-ntpsim$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-ntpsim$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpsim$U.o `test -f 'ntpsim$U.c' || echo '$(srcdir)/'`ntpsim$U.c
+
+ntpdsim-ntpsim$U.obj: ntpsim$U.c
+@am__fastdepCC_TRUE@ if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -MT ntpdsim-ntpsim$U.obj -MD -MP -MF "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo" \
+@am__fastdepCC_TRUE@ -c -o ntpdsim-ntpsim$U.obj `if test -f 'ntpsim$U.c'; then $(CYGPATH_W) 'ntpsim$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpsim$U.c'; fi`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo" "$(DEPDIR)/ntpdsim-ntpsim$U.Po"; \
+@am__fastdepCC_TRUE@ else rm -f "$(DEPDIR)/ntpdsim-ntpsim$U.Tpo"; exit 1; \
+@am__fastdepCC_TRUE@ fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ntpsim$U.c' object='ntpdsim-ntpsim$U.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/ntpdsim-ntpsim$U.Po' tmpdepfile='$(DEPDIR)/ntpdsim-ntpsim$U.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ntpdsim_CFLAGS) $(CFLAGS) -c -o ntpdsim-ntpsim$U.obj `if test -f 'ntpsim$U.c'; then $(CYGPATH_W) 'ntpsim$U.c'; else $(CYGPATH_W) '$(srcdir)/ntpsim$U.c'; fi`
+check_y2k_.c: check_y2k.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/check_y2k.c; then echo $(srcdir)/check_y2k.c; else echo check_y2k.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+cmd_args_.c: cmd_args.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/cmd_args.c; then echo $(srcdir)/cmd_args.c; else echo cmd_args.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+map_vme_.c: map_vme.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/map_vme.c; then echo $(srcdir)/map_vme.c; else echo map_vme.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_config_.c: ntp_config.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_config.c; then echo $(srcdir)/ntp_config.c; else echo ntp_config.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_control_.c: ntp_control.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_control.c; then echo $(srcdir)/ntp_control.c; else echo ntp_control.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_crypto_.c: ntp_crypto.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_crypto.c; then echo $(srcdir)/ntp_crypto.c; else echo ntp_crypto.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_filegen_.c: ntp_filegen.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_filegen.c; then echo $(srcdir)/ntp_filegen.c; else echo ntp_filegen.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_intres_.c: ntp_intres.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_intres.c; then echo $(srcdir)/ntp_intres.c; else echo ntp_intres.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_io_.c: ntp_io.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_io.c; then echo $(srcdir)/ntp_io.c; else echo ntp_io.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_loopfilter_.c: ntp_loopfilter.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_loopfilter.c; then echo $(srcdir)/ntp_loopfilter.c; else echo ntp_loopfilter.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_monitor_.c: ntp_monitor.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_monitor.c; then echo $(srcdir)/ntp_monitor.c; else echo ntp_monitor.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_peer_.c: ntp_peer.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_peer.c; then echo $(srcdir)/ntp_peer.c; else echo ntp_peer.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_proto_.c: ntp_proto.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_proto.c; then echo $(srcdir)/ntp_proto.c; else echo ntp_proto.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_refclock_.c: ntp_refclock.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_refclock.c; then echo $(srcdir)/ntp_refclock.c; else echo ntp_refclock.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_request_.c: ntp_request.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_request.c; then echo $(srcdir)/ntp_request.c; else echo ntp_request.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_restrict_.c: ntp_restrict.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_restrict.c; then echo $(srcdir)/ntp_restrict.c; else echo ntp_restrict.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_timer_.c: ntp_timer.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_timer.c; then echo $(srcdir)/ntp_timer.c; else echo ntp_timer.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntp_util_.c: ntp_util.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntp_util.c; then echo $(srcdir)/ntp_util.c; else echo ntp_util.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntpd_.c: ntpd.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntpd.c; then echo $(srcdir)/ntpd.c; else echo ntpd.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+ntpsim_.c: ntpsim.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/ntpsim.c; then echo $(srcdir)/ntpsim.c; else echo ntpsim.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_acts_.c: refclock_acts.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_acts.c; then echo $(srcdir)/refclock_acts.c; else echo refclock_acts.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_arbiter_.c: refclock_arbiter.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_arbiter.c; then echo $(srcdir)/refclock_arbiter.c; else echo refclock_arbiter.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_arc_.c: refclock_arc.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_arc.c; then echo $(srcdir)/refclock_arc.c; else echo refclock_arc.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_as2201_.c: refclock_as2201.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_as2201.c; then echo $(srcdir)/refclock_as2201.c; else echo refclock_as2201.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_atom_.c: refclock_atom.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_atom.c; then echo $(srcdir)/refclock_atom.c; else echo refclock_atom.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_bancomm_.c: refclock_bancomm.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_bancomm.c; then echo $(srcdir)/refclock_bancomm.c; else echo refclock_bancomm.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_chronolog_.c: refclock_chronolog.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_chronolog.c; then echo $(srcdir)/refclock_chronolog.c; else echo refclock_chronolog.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_chu_.c: refclock_chu.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_chu.c; then echo $(srcdir)/refclock_chu.c; else echo refclock_chu.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_conf_.c: refclock_conf.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_conf.c; then echo $(srcdir)/refclock_conf.c; else echo refclock_conf.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_datum_.c: refclock_datum.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_datum.c; then echo $(srcdir)/refclock_datum.c; else echo refclock_datum.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_dumbclock_.c: refclock_dumbclock.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_dumbclock.c; then echo $(srcdir)/refclock_dumbclock.c; else echo refclock_dumbclock.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_fg_.c: refclock_fg.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_fg.c; then echo $(srcdir)/refclock_fg.c; else echo refclock_fg.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_gpsvme_.c: refclock_gpsvme.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_gpsvme.c; then echo $(srcdir)/refclock_gpsvme.c; else echo refclock_gpsvme.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_heath_.c: refclock_heath.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_heath.c; then echo $(srcdir)/refclock_heath.c; else echo refclock_heath.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_hopfpci_.c: refclock_hopfpci.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_hopfpci.c; then echo $(srcdir)/refclock_hopfpci.c; else echo refclock_hopfpci.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_hopfser_.c: refclock_hopfser.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_hopfser.c; then echo $(srcdir)/refclock_hopfser.c; else echo refclock_hopfser.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_hpgps_.c: refclock_hpgps.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_hpgps.c; then echo $(srcdir)/refclock_hpgps.c; else echo refclock_hpgps.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_irig_.c: refclock_irig.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_irig.c; then echo $(srcdir)/refclock_irig.c; else echo refclock_irig.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_jjy_.c: refclock_jjy.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_jjy.c; then echo $(srcdir)/refclock_jjy.c; else echo refclock_jjy.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_jupiter_.c: refclock_jupiter.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_jupiter.c; then echo $(srcdir)/refclock_jupiter.c; else echo refclock_jupiter.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_leitch_.c: refclock_leitch.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_leitch.c; then echo $(srcdir)/refclock_leitch.c; else echo refclock_leitch.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_local_.c: refclock_local.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_local.c; then echo $(srcdir)/refclock_local.c; else echo refclock_local.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_msfees_.c: refclock_msfees.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_msfees.c; then echo $(srcdir)/refclock_msfees.c; else echo refclock_msfees.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_mx4200_.c: refclock_mx4200.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_mx4200.c; then echo $(srcdir)/refclock_mx4200.c; else echo refclock_mx4200.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_neoclock4x_.c: refclock_neoclock4x.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_neoclock4x.c; then echo $(srcdir)/refclock_neoclock4x.c; else echo refclock_neoclock4x.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_nmea_.c: refclock_nmea.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_nmea.c; then echo $(srcdir)/refclock_nmea.c; else echo refclock_nmea.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_oncore_.c: refclock_oncore.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_oncore.c; then echo $(srcdir)/refclock_oncore.c; else echo refclock_oncore.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_palisade_.c: refclock_palisade.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_palisade.c; then echo $(srcdir)/refclock_palisade.c; else echo refclock_palisade.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_parse_.c: refclock_parse.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_parse.c; then echo $(srcdir)/refclock_parse.c; else echo refclock_parse.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_pcf_.c: refclock_pcf.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_pcf.c; then echo $(srcdir)/refclock_pcf.c; else echo refclock_pcf.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_pst_.c: refclock_pst.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_pst.c; then echo $(srcdir)/refclock_pst.c; else echo refclock_pst.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_ptbacts_.c: refclock_ptbacts.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_ptbacts.c; then echo $(srcdir)/refclock_ptbacts.c; else echo refclock_ptbacts.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_ripencc_.c: refclock_ripencc.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_ripencc.c; then echo $(srcdir)/refclock_ripencc.c; else echo refclock_ripencc.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_shm_.c: refclock_shm.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_shm.c; then echo $(srcdir)/refclock_shm.c; else echo refclock_shm.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_tpro_.c: refclock_tpro.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_tpro.c; then echo $(srcdir)/refclock_tpro.c; else echo refclock_tpro.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_trak_.c: refclock_trak.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_trak.c; then echo $(srcdir)/refclock_trak.c; else echo refclock_trak.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_true_.c: refclock_true.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_true.c; then echo $(srcdir)/refclock_true.c; else echo refclock_true.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_tt560_.c: refclock_tt560.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_tt560.c; then echo $(srcdir)/refclock_tt560.c; else echo refclock_tt560.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_ulink_.c: refclock_ulink.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_ulink.c; then echo $(srcdir)/refclock_ulink.c; else echo refclock_ulink.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_usno_.c: refclock_usno.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_usno.c; then echo $(srcdir)/refclock_usno.c; else echo refclock_usno.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_wwv_.c: refclock_wwv.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_wwv.c; then echo $(srcdir)/refclock_wwv.c; else echo refclock_wwv.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_wwvb_.c: refclock_wwvb.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_wwvb.c; then echo $(srcdir)/refclock_wwvb.c; else echo refclock_wwvb.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+refclock_zyfer_.c: refclock_zyfer.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/refclock_zyfer.c; then echo $(srcdir)/refclock_zyfer.c; else echo refclock_zyfer.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+check_y2k_.$(OBJEXT) cmd_args_.$(OBJEXT) map_vme_.$(OBJEXT) \
+ntp_config_.$(OBJEXT) ntp_control_.$(OBJEXT) ntp_crypto_.$(OBJEXT) \
+ntp_filegen_.$(OBJEXT) ntp_intres_.$(OBJEXT) ntp_io_.$(OBJEXT) \
+ntp_loopfilter_.$(OBJEXT) ntp_monitor_.$(OBJEXT) ntp_peer_.$(OBJEXT) \
+ntp_proto_.$(OBJEXT) ntp_refclock_.$(OBJEXT) ntp_request_.$(OBJEXT) \
+ntp_restrict_.$(OBJEXT) ntp_timer_.$(OBJEXT) ntp_util_.$(OBJEXT) \
+ntpd_.$(OBJEXT) ntpsim_.$(OBJEXT) refclock_acts_.$(OBJEXT) \
+refclock_arbiter_.$(OBJEXT) refclock_arc_.$(OBJEXT) \
+refclock_as2201_.$(OBJEXT) refclock_atom_.$(OBJEXT) \
+refclock_bancomm_.$(OBJEXT) refclock_chronolog_.$(OBJEXT) \
+refclock_chu_.$(OBJEXT) refclock_conf_.$(OBJEXT) \
+refclock_datum_.$(OBJEXT) refclock_dumbclock_.$(OBJEXT) \
+refclock_fg_.$(OBJEXT) refclock_gpsvme_.$(OBJEXT) \
+refclock_heath_.$(OBJEXT) refclock_hopfpci_.$(OBJEXT) \
+refclock_hopfser_.$(OBJEXT) refclock_hpgps_.$(OBJEXT) \
+refclock_irig_.$(OBJEXT) refclock_jjy_.$(OBJEXT) \
+refclock_jupiter_.$(OBJEXT) refclock_leitch_.$(OBJEXT) \
+refclock_local_.$(OBJEXT) refclock_msfees_.$(OBJEXT) \
+refclock_mx4200_.$(OBJEXT) refclock_neoclock4x_.$(OBJEXT) \
+refclock_nmea_.$(OBJEXT) refclock_oncore_.$(OBJEXT) \
+refclock_palisade_.$(OBJEXT) refclock_parse_.$(OBJEXT) \
+refclock_pcf_.$(OBJEXT) refclock_pst_.$(OBJEXT) \
+refclock_ptbacts_.$(OBJEXT) refclock_ripencc_.$(OBJEXT) \
+refclock_shm_.$(OBJEXT) refclock_tpro_.$(OBJEXT) \
+refclock_trak_.$(OBJEXT) refclock_true_.$(OBJEXT) \
+refclock_tt560_.$(OBJEXT) refclock_ulink_.$(OBJEXT) \
+refclock_usno_.$(OBJEXT) refclock_wwv_.$(OBJEXT) \
+refclock_wwvb_.$(OBJEXT) refclock_zyfer_.$(OBJEXT) : $(ANSI2KNR)
+uninstall-info-am:
+
+ETAGS = etags
+ETAGSFLAGS =
+
+CTAGS = ctags
+CTAGSFLAGS =
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$tags$$unique" \
+ || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique
+
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkinstalldirs) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-local
+check: check-am
+all-am: Makefile $(LIBRARIES) $(PROGRAMS)
+
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f $(CONFIG_CLEAN_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-noinstLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic mostlyclean-kr
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am check-local clean \
+ clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-noinstLIBRARIES ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am info \
+ info-am install install-am install-binPROGRAMS install-data \
+ install-data-am install-exec install-exec-am install-info \
+ install-info-am install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-kr pdf pdf-am ps ps-am tags \
+ uninstall uninstall-am uninstall-binPROGRAMS uninstall-info-am
+
+
+check-local: @MAKE_CHECK_Y2K@
+ test -z "@MAKE_CHECK_Y2K@" || ./@MAKE_CHECK_Y2K@
+
+$(PROGRAMS): $(LDADD)
+
+../libntp/libntp.a:
+ cd ../libntp && $(MAKE)
+
+../libparse/libparse.a:
+ cd ../libparse && $(MAKE)
+
+version.o: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ Makefile $(top_srcdir)/version
+ env CSET=`cat $(top_srcdir)/version` $(top_builddir)/scripts/mkver ntpd
+ $(COMPILE) -c version.c
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/ntpd/check_y2k.c b/ntpd/check_y2k.c
new file mode 100644
index 0000000..6b83115
--- /dev/null
+++ b/ntpd/check_y2k.c
@@ -0,0 +1,627 @@
+/* check_y2k.c -- test ntp code constructs for Y2K correctness Y2KFixes [*/
+
+ /*
+ Code invoked by `make check`. Not part of ntpd and not to be
+ installed.
+
+ On any code I even wonder about, I've cut and pasted the code
+ here and ran it as a test case just to be sure.
+
+ For code not in "ntpd" proper, we have tried to call most
+ repaired functions from herein to properly test them
+ (something never done before!). This has found several bugs,
+ not normal Y2K bugs, that will strike in Y2K so repair them
+ we did.
+
+ Program exits with 0 on success, 1 on Y2K failure (stdout messages).
+ Exit of 2 indicates internal logic bug detected OR failure of
+ what should be our correct formulas.
+
+ While "make check" should only check logic for source within that
+ specific directory, this check goes outside the scope of the local
+ directory. It's not a perfect world (besides, there is a lot of
+ interdependence here, and it really needs to be tested in
+ a controled order).
+ */
+
+/* { definitions lifted from ntpd.c to allow us to complie with
+ "#include ntp.h". I have not taken the time to reduce the clutter. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <stdio.h>
+#include <errno.h>
+#ifndef SYS_WINNT
+# if !defined(VMS) /*wjm*/
+# include <sys/param.h>
+# endif /* VMS */
+# if HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+# endif /* HAVE_SYS_SIGNAL_H */
+# include <sys/signal.h>
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif /* HAVE_SYS_IOCTL_H */
+# if !defined(VMS) /*wjm*/
+# include <sys/resource.h>
+# endif /* VMS */
+#else
+# include <signal.h>
+# include <process.h>
+# include <io.h>
+# include "../libntp/log.h"
+#endif /* SYS_WINNT */
+#if defined(HAVE_RTPRIO)
+# ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+# endif
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# include <sys/rtprio.h>
+#else
+# ifdef HAVE_PLOCK
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SCHED_SETSCHEDULER)
+# ifdef HAVE_SCHED_H
+# include <sched.h>
+# else
+# ifdef HAVE_SYS_SCHED_H
+# include <sys/sched.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SYS_MMAN_H)
+# include <sys/mman.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+#ifdef SYS_DOMAINOS
+# include <apollo/base.h>
+#endif /* SYS_DOMAINOS */
+
+/* } end definitions lifted from ntpd.c */
+
+#include "ntp_calendar.h"
+#include "parse.h"
+
+#define GoodLeap(Year) (((Year)%4 || (!((Year)%100) && (Year)%400)) ? 0 : 13 )
+
+volatile int debug = 0; /* debugging requests for parse stuff */
+char const *progname = "check_y2k";
+
+long
+Days ( int Year ) /* return number of days since year "0" */
+{
+ long Return;
+ /* this is a known to be good algorithm */
+ Return = Year * 365; /* first aproximation to the value */
+ if ( Year >= 1 )
+ { /* see notes in libparse/parse.c if you want a PROPER
+ * **generic algorithm. */
+ Return += (Year+3) / 4; /* add in (too many) leap days */
+ Return -= (Year-1) / 100; /* reduce by (too many) centurys */
+ Return += (Year-1) / 400; /* get final answer */
+ }
+
+ return Return;
+}
+
+static int year0 = 1900; /* sarting year for NTP time */
+static int yearend; /* ending year we test for NTP time.
+ * 32-bit systems: through 2036, the
+ **year in which NTP time overflows.
+ * 64-bit systems: a reasonable upper
+ **limit (well, maybe somewhat beyond
+ **reasonable, but well before the
+ **max time, by which time the earth
+ **will be dead.) */
+static time_t Time;
+static struct tm LocalTime;
+
+#define Error(year) if ( (year)>=2036 && LocalTime.tm_year < 110 ) \
+ Warnings++; else Fatals++
+
+int
+main( void )
+{
+ int Fatals;
+ int Warnings;
+ int year;
+
+ Time = time( (time_t *)NULL )
+#ifdef TESTTIMEOFFSET
+ + test_time_offset
+#endif
+ ;
+ LocalTime = *localtime( &Time );
+
+ year = ( sizeof( u_long ) > 4 ) /* save max span using year as temp */
+ ? ( 400 * 3 ) /* three greater gregorian cycles */
+ : ((int)(0x7FFFFFFF / 365.242 / 24/60/60)* 2 ); /*32-bit limit*/
+ /* NOTE: will automacially expand test years on
+ * 64 bit machines.... this may cause some of the
+ * existing ntp logic to fail for years beyond
+ * 2036 (the current 32-bit limit). If all checks
+ * fail ONLY beyond year 2036 you may ignore such
+ * errors, at least for a decade or so. */
+ yearend = year0 + year;
+
+ puts( " internal self check" );
+ { /* verify our own logic used to verify repairs */
+ unsigned long days;
+
+ if ( year0 >= yearend )
+ {
+ fprintf( stdout, "year0=%d NOT LESS THAN yearend=%d (span=%d)\n",
+ (int)year0, (int)yearend, (int)year );
+ exit(2);
+ }
+
+ {
+ int save_year;
+
+ save_year = LocalTime.tm_year; /* save current year */
+
+ year = 1980;
+ LocalTime.tm_year = year - 1900;
+ Fatals = Warnings = 0;
+ Error(year); /* should increment Fatals */
+ if ( Fatals == 0 )
+ {
+ fprintf( stdout,
+ "%4d: %s(%d): FATAL DID NOT INCREMENT (Fatals=%d Warnings=%d)\n",
+ (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
+ exit(2);
+ }
+
+ year = 2100; /* test year > limit but CURRENT year < limit */
+ Fatals = Warnings = 0;
+ Error(year); /* should increment Fatals */
+ if ( Warnings == 0 )
+ {
+ fprintf( stdout,
+ "%4d: %s(%d): WARNING DID NOT INCREMENT (Fatals=%d Warnings=%d)\n",
+ (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
+ exit(2);
+ }
+ Fatals = Warnings = 0;
+ LocalTime.tm_year = year - 1900; /* everything > limit */
+ Error(1980); /* should increment Fatals */
+ if ( Fatals == 0 )
+ {
+ fprintf( stdout,
+ "%4d: %s(%d): FATALS DID NOT INCREMENT (Fatals=%d Warnings=%d)\n",
+ (int)year, __FILE__, __LINE__, (int)Fatals, (int)Warnings );
+ exit(2);
+ }
+
+ LocalTime.tm_year = save_year;
+ }
+
+ days = 365+1; /* days in year 0 + 1 more day */
+ for ( year = 1; year <= 2500; year++ )
+ {
+ long Test;
+ Test = Days( year );
+ if ( days != Test )
+ {
+ fprintf( stdout, "%04d: Days() DAY COUNT ERROR: s/b=%ld was=%ld\n",
+ year, (long)days, (long)Test );
+ exit(2); /* would throw off many other tests */
+ }
+
+ Test = julian0(year); /* compare with julian0() macro */
+ if ( days != Test )
+ {
+ fprintf( stdout, "%04d: julian0() DAY COUNT ERROR: s/b=%ld was=%ld\n",
+ year, (long)days, (long)Test );
+ exit(2); /* would throw off many other tests */
+ }
+
+ days += 365;
+ if ( isleap_4(year) ) days++;
+ }
+
+ if ( isleap_4(1999) )
+ {
+ fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" );
+ exit(2);
+ }
+ if ( !isleap_4(2000) )
+ {
+ fprintf( stdout, "isleap_4(2000) REPORTED FALSE\n" );
+ exit(2);
+ }
+ if ( isleap_4(2001) )
+ {
+ fprintf( stdout, "isleap_4(1999) REPORTED TRUE\n" );
+ exit(2);
+ }
+
+ if ( !isleap_tm(2000-1900) )
+ {
+ fprintf( stdout, "isleap_tm(100) REPORTED FALSE\n" );
+ exit(2);
+ }
+ }
+
+ Fatals = Warnings = 0;
+
+ puts( " include/ntp.h" );
+ { /* test our new isleap_*() #define "functions" */
+
+ for ( year = 1400; year <= 2200; year++ )
+ {
+ int LeapSw;
+ int IsLeapSw;
+
+ LeapSw = GoodLeap(year);
+ IsLeapSw = isleap_4(year);
+
+ if ( !!LeapSw != !!IsLeapSw )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw );
+ break;
+ }
+
+ IsLeapSw = isleap_tm(year-1900);
+
+ if ( !!LeapSw != !!IsLeapSw )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d %2d %3d *** ERROR\n", year, LeapSw, IsLeapSw );
+ break;
+ }
+ }
+ }
+
+ puts( " include/ntp_calendar.h" );
+ { /* I belive this is good, but just to be sure... */
+
+ /* we are testing this #define */
+#define is_leapyear(y) (y%4 == 0 && !(y%100 == 0 && !(y%400 == 0)))
+
+ for ( year = 1400; year <= 2200; year++ )
+ {
+ int LeapSw;
+
+ LeapSw = GoodLeap(year);
+
+ if ( !(!LeapSw) != !(!is_leapyear(year)) )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d %2d *** ERROR\n", year, LeapSw );
+ break;
+ }
+ }
+ }
+
+
+ puts( " libparse/parse.c" );
+ {
+ long Days1970; /* days from 1900 to 1970 */
+
+ struct ParseTime /* womp up a test structure to all cut/paste code */
+ {
+ int year;
+ } Clock_Time, *clock_time;
+
+ clock_time = &Clock_Time;
+
+ /* first test this #define */
+#define days_per_year(x) ((x) % 4 ? 365 : ((x % 400) ? ((x % 100) ? 366 : 365) : 366))
+
+ for ( year = 1400; year <= 2200; year++ )
+ {
+ int LeapSw;
+ int DayCnt;
+
+ LeapSw = GoodLeap(year);
+ DayCnt = (int)days_per_year(year);
+
+ if ( ( LeapSw ? 366 : 365 ) != DayCnt )
+ {
+ Error(year);
+ fprintf( stdout,
+ " days_per_year() %4d %2d %3d *** ERROR\n",
+ year, LeapSw, DayCnt );
+ break;
+ }
+ }
+
+ /* test (what is now julian0) calculations */
+
+ Days1970 = Days( 1970 ); /* get days since 1970 using a known good */
+
+ for ( year = 1970; year < yearend; year++ )
+ {
+ unsigned long t;
+ long DaysYear ;
+
+ clock_time->year = year;
+
+ /* here is the code we are testing, cut and pasted out of the source */
+#if 0 /* old BUGGY code that has Y2K (and many other) failures */
+ /* ghealton: this logic FAILED with great frequency when run
+ * over a period of time, including for year 2000. True, it
+ * had more successes than failures, but that's not really good
+ * enough for critical time distribution software.
+ * It is so awful I wonder if it has had a history of failure
+ * and fixes? */
+ t = (clock_time->year - 1970) * 365;
+ t += (clock_time->year >> 2) - (1970 >> 2);
+ t -= clock_time->year / 100 - 1970 / 100;
+ t += clock_time->year / 400 - 1970 / 400;
+
+ /* (immediate feare of rounding errors on integer
+ * **divisions proved well founded) */
+
+#else
+ /* my replacement, based on Days() above */
+ t = julian0(year) - julian0(1970);
+#endif
+
+ /* compare result in t against trusted calculations */
+ DaysYear = Days( year ); /* get days to this year */
+ if ( t != DaysYear - Days1970 )
+ {
+ Error(year);
+ fprintf( stdout,
+ " %4d 1970=%-8ld %4d=%-8ld %-3ld t=%-8ld *** ERROR ***\n",
+ year, (long)Days1970,
+ year,
+ (long)DaysYear,
+ (long)(DaysYear - Days1970),
+ (long)t );
+ }
+ }
+
+#if 1 /* { */
+ {
+ debug = 1; /* enable debugging */
+ for ( year = 1970; year < yearend; year++ )
+ { /* (limited by theory unix 2038 related bug lives by, but
+ * ends in yearend) */
+ clocktime_t ct;
+ time_t Observed;
+ time_t Expected;
+ u_long Flag;
+ unsigned long t;
+
+ ct.day = 1;
+ ct.month = 1;
+ ct.year = year;
+ ct.hour = ct.minute = ct.second = ct.usecond = 0;
+ ct.utcoffset = 0;
+ ct.utctime = 0;
+ ct.flags = 0;
+
+ Flag = 0;
+ Observed = parse_to_unixtime( &ct, &Flag );
+ if ( ct.year != year )
+ {
+ fprintf( stdout,
+ "%04d: parse_to_unixtime(,%d) CORRUPTED ct.year: was %d\n",
+ (int)year, (int)Flag, (int)ct.year );
+ Error(year);
+ break;
+ }
+ t = julian0(year) - julian0(1970); /* Julian day from 1970 */
+ Expected = t * 24 * 60 * 60;
+ if ( Observed != Expected || Flag )
+ { /* time difference */
+ fprintf( stdout,
+ "%04d: parse_to_unixtime(,%d) FAILURE: was=%lu s/b=%lu (%ld)\n",
+ year, (int)Flag,
+ (unsigned long)Observed, (unsigned long)Expected,
+ ((long)Observed - (long)Expected) );
+ Error(year);
+ break;
+ }
+
+ if ( year >= YEAR_PIVOT+1900 )
+ {
+ /* check year % 100 code we put into parse_to_unixtime() */
+ ct.utctime = 0;
+ ct.year = year % 100;
+ Flag = 0;
+
+ Observed = parse_to_unixtime( &ct, &Flag );
+
+ if ( Observed != Expected || Flag )
+ { /* time difference */
+ fprintf( stdout,
+"%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n",
+ year, (int)ct.year, (int)Flag,
+ (unsigned long)Observed, (unsigned long)Expected,
+ ((long)Observed - (long)Expected) );
+ Error(year);
+ break;
+ }
+
+ /* check year - 1900 code we put into parse_to_unixtime() */
+ ct.utctime = 0;
+ ct.year = year - 1900;
+ Flag = 0;
+
+ Observed = parse_to_unixtime( &ct, &Flag );
+
+ if ( Observed != Expected || Flag )
+ { /* time difference */
+ fprintf( stdout,
+"%04d: parse_to_unixtime(%d,%d) FAILURE: was=%lu s/b=%lu (%ld)\n",
+ year, (int)ct.year, (int)Flag,
+ (unsigned long)Observed, (unsigned long)Expected,
+ ((long)Observed - (long)Expected) );
+ Error(year);
+ break;
+ }
+
+
+ }
+ }
+#endif /* } */
+ }
+ }
+
+ puts( " libntp/caljulian.c" );
+ { /* test caljulian() */
+ struct calendar ot;
+ u_long ntp_time; /* NTP time */
+
+ year = year0; /* calculate the basic year */
+ printf( " starting year %04d\n", (int)year0 );
+ printf( " ending year %04d\n", (int)yearend );
+
+
+ ntp_time = julian0( year0 ); /* NTP starts in 1900-01-01 */
+#if DAY_NTP_STARTS == 693596
+ ntp_time -= 365; /* BIAS required for successful test */
+#endif
+ if ( DAY_NTP_STARTS != ntp_time )
+ {
+ Error(year);
+ fprintf( stdout,
+ "%04d: DAY_NTP_STARTS (%ld) NOT TRUE VALUE OF %ld (%ld)\n",
+ (int)year0,
+ (long)DAY_NTP_STARTS, (long)ntp_time,
+ (long)DAY_NTP_STARTS - (long)ntp_time );
+ }
+
+ for ( ; year < yearend; year++ )
+ {
+
+ /* 01-01 for the current year */
+ ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */
+ ntp_time *= 24 * 60 * 60; /* convert into seconds */
+ caljulian( ntp_time, &ot ); /* convert January 1 */
+ if ( ot.year != year
+ || ot.month != 1
+ || ot.monthday != 1 )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ntp_time,
+ year,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+
+ ntp_time += (31 + 28-1) * ( 24 * 60 * 60 ); /* advance to 02-28 */
+ caljulian( ntp_time, &ot ); /* convert Feb 28 */
+ if ( ot.year != year
+ || ot.month != 2
+ || ot.monthday != 28 )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: EXPECTED %04d-02-28: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ntp_time,
+ year,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+
+ {
+ int m; /* expected month */
+ int d; /* expected day */
+
+ m = isleap_4(year) ? 2 : 3;
+ d = isleap_4(year) ? 29 : 1;
+
+ ntp_time += ( 24 * 60 * 60 ); /* advance to the next day */
+ caljulian( ntp_time, &ot ); /* convert this day */
+ if ( ot.year != year
+ || ot.month != m
+ || ot.monthday != d )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: EXPECTED %04d-%02d-%02d: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ntp_time,
+ year, m, d,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+
+ }
+ }
+ }
+
+ puts( " libntp/caltontp.c" );
+ { /* test caltontp() */
+ struct calendar ot;
+ u_long ntp_time; /* NTP time */
+
+ year = year0; /* calculate the basic year */
+ printf( " starting year %04d\n", (int)year0 );
+ printf( " ending year %04d\n", (int)yearend );
+
+
+ for ( ; year < yearend; year++ )
+ {
+ u_long ObservedNtp;
+
+ /* 01-01 for the current year */
+ ot.year = year;
+ ot.month = ot.monthday = 1; /* unused, but set anyway JIC */
+ ot.yearday = 1; /* this is the magic value used by caltontp() */
+ ot.hour = ot.minute = ot.second = 0;
+
+ ntp_time = Days( year ) - Days( year0 ); /* days into NTP time */
+ ntp_time *= 24 * 60 * 60; /* convert into seconds */
+ ObservedNtp = caltontp( &ot );
+ if ( ntp_time != ObservedNtp )
+ {
+ Error(year);
+ fprintf( stdout, "%d: EXPECTED %lu: FOUND %lu (%ld)\n",
+ (int)year,
+ (unsigned long)ntp_time, (unsigned long)ObservedNtp ,
+ (long)ntp_time - (long)ObservedNtp );
+
+ break;
+ }
+
+ /* now call caljulian as a type of failsafe supercheck */
+ caljulian( ObservedNtp, &ot ); /* convert January 1 */
+ if ( ot.year != year
+ || ot.month != 1
+ || ot.monthday != 1 )
+ {
+ Error(year);
+ fprintf( stdout, "%lu: caljulian FAILSAFE EXPECTED %04d-01-01: FOUND %04d-%02d-%02d\n",
+ (unsigned long)ObservedNtp,
+ year,
+ (int)ot.year, (int)ot.month, (int)ot.monthday );
+ break;
+ }
+ }
+ }
+
+ if ( Warnings > 0 )
+ fprintf( stdout, "%d WARNINGS\n", Warnings );
+ if ( Fatals > 0 )
+ fprintf( stdout, "%d FATAL ERRORS\n", Fatals );
+ return Fatals ? 1 : 0;
+}
+ /* Y2KFixes ] */
diff --git a/ntpd/cmd_args.c b/ntpd/cmd_args.c
new file mode 100644
index 0000000..3ed9b66
--- /dev/null
+++ b/ntpd/cmd_args.c
@@ -0,0 +1,418 @@
+/*
+ * cmd_args.c = command-line argument processing
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_cmdargs.h"
+
+#ifdef SIM
+#include "ntpsim.h"
+#endif /* SIM */
+
+/*
+ * Definitions of things either imported from or exported to outside
+ */
+extern char const *progname;
+int listen_to_virtual_ips = 1;
+
+#ifdef SYS_WINNT
+extern BOOL NoWinService;
+#endif
+
+static const char *ntp_options = "aAbB:c:C:dD:f:gi:k:l:LmnNO:p:P:qr:s:S:t:T:W:u:v:V:xY:Z:-:";
+
+#ifdef HAVE_NETINFO
+extern int check_netinfo;
+#endif
+
+
+/*
+ * getstartup - search through the options looking for a debugging flag
+ */
+void
+getstartup(
+ int argc,
+ char *argv[]
+ )
+{
+ int errflg;
+ extern int priority_done;
+ int c;
+
+#ifdef DEBUG
+ debug = 0; /* no debugging by default */
+#endif
+
+ /*
+ * This is a big hack. We don't really want to read command line
+ * configuration until everything else is initialized, since
+ * the ability to configure the system may depend on storage
+ * and the like having been initialized. Except that we also
+ * don't want to initialize anything until after detaching from
+ * the terminal, but we won't know to do that until we've
+ * parsed the command line. Do that now, crudely, and do it
+ * again later. Our ntp_getopt() is explicitly reusable, by the
+ * way. Your own mileage may vary.
+ *
+ * This hack is even called twice (to allow complete logging to file)
+ */
+ errflg = 0;
+ progname = argv[0];
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, ntp_options)) != EOF)
+ switch (c) {
+#ifdef DEBUG
+ case 'd':
+ ++debug;
+ break;
+ case 'D':
+ debug = (int)atol(ntp_optarg);
+ printf("Debug1: %s -> %x = %d\n", ntp_optarg, debug, debug);
+ break;
+#else
+ case 'd':
+ case 'D':
+ msyslog(LOG_ERR, "ntpd not compiled with -DDEBUG option - no DEBUG support");
+ fprintf(stderr, "ntpd not compiled with -DDEBUG option - no DEBUG support\n");
+ ++errflg;
+ break;
+#endif
+ case 'L':
+ listen_to_virtual_ips = 0;
+ break;
+ case 'l':
+ {
+ FILE *new_file;
+
+ if(strcmp(ntp_optarg, "stderr") == 0)
+ new_file = stderr;
+ else if(strcmp(ntp_optarg, "stdout") == 0)
+ new_file = stdout;
+ else
+ new_file = fopen(ntp_optarg, "a");
+ if (new_file != NULL) {
+ NLOG(NLOG_SYSINFO)
+ msyslog(LOG_NOTICE, "logging to file %s", ntp_optarg);
+ if (syslog_file != NULL &&
+ fileno(syslog_file) != fileno(new_file))
+ (void)fclose(syslog_file);
+
+ syslog_file = new_file;
+ syslogit = 0;
+ }
+ else
+ msyslog(LOG_ERR,
+ "Cannot open log file %s",
+ ntp_optarg);
+ }
+ break;
+
+ case 'n':
+ case 'q':
+ ++nofork;
+#ifdef SYS_WINNT
+ NoWinService = TRUE;
+#endif
+ break;
+
+ case 'N':
+ priority_done = 0;
+ break;
+
+ case '?':
+ ++errflg;
+ break;
+
+ case '-':
+ if ( ! strcmp(ntp_optarg, "version") ) {
+ printf("%.80s: %.80s\n", progname, Version);
+ exit(0);
+ } else if ( ! strcmp(ntp_optarg, "help") ) {
+ /* usage(); */
+ /* exit(0); */
+ ++errflg;
+ } else if ( ! strcmp(ntp_optarg, "copyright") ) {
+ printf("unknown\n");
+ exit(0);
+ } else {
+ fprintf(stderr, "%.80s: Error unknown argument '--%.80s'\n",
+ progname,
+ ntp_optarg);
+ exit(12);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (errflg || ntp_optind != argc) {
+ (void) fprintf(stderr, "usage: %s [ -abdgmnqx ] [ -c config_file ] [ -e e_delay ]\n", progname);
+ (void) fprintf(stderr, "\t\t[ -f freq_file ] [ -k key_file ] [ -l log_file ]\n");
+ (void) fprintf(stderr, "\t\t[ -p pid_file ] [ -r broad_delay ] [ -s statdir ]\n");
+ (void) fprintf(stderr, "\t\t[ -t trust_key ] [ -v sys_var ] [ -V default_sysvar ]\n");
+#if defined(HAVE_SCHED_SETSCHEDULER)
+ (void) fprintf(stderr, "\t\t[ -P fixed_process_priority ]\n");
+#endif
+#ifdef HAVE_CLOCKCTL
+ (void) fprintf(stderr, "\t\t[ -u user[:group] ] [ -i chrootdir ]\n");
+#endif
+ exit(2);
+ }
+ ntp_optind = 0; /* reset ntp_optind to restart ntp_getopt */
+
+#ifdef DEBUG
+ if (debug) {
+#ifdef HAVE_SETVBUF
+ static char buf[BUFSIZ];
+ setvbuf(stdout, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(stdout);
+#endif
+ }
+#endif
+}
+
+/*
+ * getCmdOpts - get command line options
+ */
+void
+getCmdOpts(
+ int argc,
+ char *argv[]
+ )
+{
+ extern char *config_file;
+ struct sockaddr_in inaddrntp;
+ int errflg;
+ int c;
+
+ /*
+ * Initialize, initialize
+ */
+ errflg = 0;
+#ifdef DEBUG
+ debug = 0;
+#endif /* DEBUG */
+
+ progname = argv[0];
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, ntp_options)) != EOF) {
+ switch (c) {
+ case 'a':
+ proto_config(PROTO_AUTHENTICATE, 1, 0., NULL);
+ break;
+
+ case 'A':
+ proto_config(PROTO_AUTHENTICATE, 0, 0., NULL);
+ break;
+
+ case 'b':
+ proto_config(PROTO_BROADCLIENT, 1, 0., NULL);
+ break;
+
+ case 'c':
+ config_file = ntp_optarg;
+#ifdef HAVE_NETINFO
+ check_netinfo = 0;
+#endif
+ break;
+
+ case 'd':
+#ifdef DEBUG
+ debug++;
+#else
+ errflg++;
+#endif /* DEBUG */
+ break;
+
+ case 'D':
+#ifdef DEBUG
+ debug = (int)atol(ntp_optarg);
+ printf("Debug2: %s -> %x = %d\n", ntp_optarg, debug, debug);
+#else
+ errflg++;
+#endif /* DEBUG */
+ break;
+
+ case 'f':
+ stats_config(STATS_FREQ_FILE, ntp_optarg);
+ break;
+
+ case 'g':
+ allow_panic = TRUE;
+ break;
+
+ case 'i':
+#ifdef HAVE_CLOCKCTL
+ if (!ntp_optarg)
+ errflg++;
+ else
+ chrootdir = ntp_optarg;
+ break;
+#else
+ errflg++;
+#endif
+ case 'k':
+ getauthkeys(ntp_optarg);
+ break;
+
+ case 'L': /* already done at pre-scan */
+ case 'l': /* already done at pre-scan */
+ break;
+
+ case 'm':
+ inaddrntp.sin_family = AF_INET;
+ inaddrntp.sin_port = htons(NTP_PORT);
+ inaddrntp.sin_addr.s_addr = htonl(INADDR_NTP);
+ proto_config(PROTO_MULTICAST_ADD, 0, 0., (struct sockaddr_storage*)&inaddrntp);
+ sys_bclient = 1;
+ break;
+
+ case 'n': /* already done at pre-scan */
+ break;
+
+ case 'N': /* already done at pre-scan */
+ break;
+
+ case 'p':
+ stats_config(STATS_PID_FILE, ntp_optarg);
+ break;
+
+ case 'P':
+#if defined(HAVE_SCHED_SETSCHEDULER)
+ config_priority = (int)atol(ntp_optarg);
+ config_priority_override = 1;
+#else
+ errflg++;
+#endif
+ break;
+
+ case 'q':
+ mode_ntpdate = TRUE;
+ break;
+
+ case 'r':
+ do {
+ double tmp;
+
+ if (sscanf(ntp_optarg, "%lf", &tmp) != 1) {
+ msyslog(LOG_ERR,
+ "command line broadcast delay value %s undecodable",
+ ntp_optarg);
+ } else {
+ proto_config(PROTO_BROADDELAY, 0, tmp, NULL);
+ }
+ } while (0);
+ break;
+
+ case 'u':
+#ifdef HAVE_CLOCKCTL
+ user = malloc(strlen(ntp_optarg) + 1);
+ if ((user == NULL) || (ntp_optarg == NULL))
+ errflg++;
+ (void)strncpy(user, ntp_optarg, strlen(ntp_optarg) + 1);
+ group = rindex(user, ':');
+ if (group)
+ *group++ = '\0'; /* get rid of the ':' */
+#else
+ errflg++;
+#endif
+ break;
+ case 's':
+ stats_config(STATS_STATSDIR, ntp_optarg);
+ break;
+
+ case 't':
+ do {
+ u_long tkey;
+
+ tkey = (int)atol(ntp_optarg);
+ if (tkey <= 0 || tkey > NTP_MAXKEY) {
+ msyslog(LOG_ERR,
+ "command line trusted key %s is invalid",
+ ntp_optarg);
+ } else {
+ authtrust(tkey, 1);
+ }
+ } while (0);
+ break;
+
+ case 'v':
+ case 'V':
+ set_sys_var(ntp_optarg, strlen(ntp_optarg)+1,
+ (u_short) (RW | ((c == 'V') ? DEF : 0)));
+ break;
+
+ case 'x':
+ clock_max = 600;
+ break;
+#ifdef SIM
+ case 'B':
+ sscanf(ntp_optarg, "%lf", &ntp_node.bdly);
+ break;
+
+ case 'C':
+ sscanf(ntp_optarg, "%lf", &ntp_node.snse);
+ break;
+
+ case 'H':
+ sscanf(ntp_optarg, "%lf", &ntp_node.slew);
+ break;
+
+ case 'O':
+ sscanf(ntp_optarg, "%lf", &ntp_node.clk_time);
+ break;
+
+ case 'S':
+ sscanf(ntp_optarg, "%lf", &ntp_node.sim_time);
+ break;
+
+ case 'T':
+ sscanf(ntp_optarg, "%lf", &ntp_node.ferr);
+ break;
+
+ case 'W':
+ sscanf(ntp_optarg, "%lf", &ntp_node.fnse);
+ break;
+
+ case 'Y':
+ sscanf(ntp_optarg, "%lf", &ntp_node.ndly);
+ break;
+
+ case 'Z':
+ sscanf(ntp_optarg, "%lf", &ntp_node.pdly);
+ break;
+
+#endif /* SIM */
+ default:
+ errflg++;
+ break;
+ }
+ }
+
+ if (errflg || ntp_optind != argc) {
+ (void) fprintf(stderr, "usage: %s [ -abdgmnx ] [ -c config_file ] [ -e e_delay ]\n", progname);
+ (void) fprintf(stderr, "\t\t[ -f freq_file ] [ -k key_file ] [ -l log_file ]\n");
+ (void) fprintf(stderr, "\t\t[ -p pid_file ] [ -r broad_delay ] [ -s statdir ]\n");
+ (void) fprintf(stderr, "\t\t[ -t trust_key ] [ -v sys_var ] [ -V default_sysvar ]\n");
+#if defined(HAVE_SCHED_SETSCHEDULER)
+ (void) fprintf(stderr, "\t\t[ -P fixed_process_priority ]\n");
+#endif
+#ifdef HAVE_CLOCKCTL
+ (void) fprintf(stderr, "\t\t[ -u user[:group] ] [ -i chrootdir ]\n");
+#endif
+ exit(2);
+ }
+ return;
+}
diff --git a/ntpd/jupiter.h b/ntpd/jupiter.h
new file mode 100644
index 0000000..ed80b0c
--- /dev/null
+++ b/ntpd/jupiter.h
@@ -0,0 +1,255 @@
+/* @(#) $Header$ (LBL) */
+
+/*
+ * Rockwell Jupiter GPS receiver definitions
+ *
+ * This is all based on the "Zodiac GPS Receiver Family Designer's
+ * Guide" (dated 12/96)
+ */
+
+#define JUPITER_SYNC 0x81ff /* sync word (book says 0xff81 !?!?) */
+#define JUPITER_ALL 0xffff /* disable all output messages */
+
+/* Output messages (sent by the Jupiter board) */
+#define JUPITER_O_GPOS 1000 /* geodetic position status */
+#define JUPITER_O_EPOS 1001 /* ECEF position status */
+#define JUPITER_O_CHAN 1002 /* channel summary */
+#define JUPITER_O_VIS 1003 /* visible satellites */
+#define JUPITER_O_DGPS 1005 /* differential GPS status */
+#define JUPITER_O_MEAS 1007 /* channel measurement */
+#define JUPITER_O_ID 1011 /* receiver id */
+#define JUPITER_O_USER 1012 /* user-settings output */
+#define JUPITER_O_TEST 1100 /* built-in test results */
+#define JUPITER_O_MARK 1102 /* measurement time mark */
+#define JUPITER_O_PULSE 1108 /* UTC time mark pulse output */
+#define JUPITER_O_PORT 1130 /* serial port com parameters in use */
+#define JUPITER_O_EUP 1135 /* EEPROM update */
+#define JUPITER_O_ESTAT 1136 /* EEPROM status */
+
+/* Input messages (sent to the Jupiter board) */
+#define JUPITER_I_PVTINIT 1200 /* geodetic position and velocity */
+#define JUPITER_I_USER 1210 /* user-defined datum */
+#define JUPITER_I_MAPSEL 1211 /* map datum select */
+#define JUPITER_I_ELEV 1212 /* satellite elevation mask control */
+#define JUPITER_I_CAND 1213 /* satellite candidate select */
+#define JUPITER_I_DGPS 1214 /* differential GPS control */
+#define JUPITER_I_COLD 1216 /* cold start control */
+#define JUPITER_I_VALID 1217 /* solution validity criteria */
+#define JUPITER_I_ALT 1219 /* user-entered altitude input */
+#define JUPITER_I_PLAT 1220 /* application platform control */
+#define JUPITER_I_NAV 1221 /* nav configuration */
+#define JUPITER_I_TEST 1300 /* preform built-in test command */
+#define JUPITER_I_RESTART 1303 /* restart command */
+#define JUPITER_I_PORT 1330 /* serial port com parameters */
+#define JUPITER_I_PROTO 1331 /* message protocol control */
+#define JUPITER_I_RDGPS 1351 /* raw DGPS RTCM SC-104 data */
+
+struct jheader {
+ u_short sync; /* (JUPITER_SYNC) */
+ u_short id; /* message id */
+ u_short len; /* number of data short wordss (w/o cksum) */
+ u_char reqid; /* JUPITER_REQID_MASK bits available as id */
+ u_char flags; /* flags */
+ u_short hsum; /* header cksum */
+};
+
+#define JUPITER_REQID_MASK 0x3f /* bits available as id */
+#define JUPITER_FLAG_NAK 0x01 /* negative acknowledgement */
+#define JUPITER_FLAG_ACK 0x02 /* acknowledgement */
+#define JUPITER_FLAG_REQUEST 0x04 /* request ACK or NAK */
+#define JUPITER_FLAG_QUERY 0x08 /* request one shot output message */
+#define JUPITER_FLAG_LOG 0x20 /* request periodic output message */
+#define JUPITER_FLAG_CONN 0x40 /* enable periodic message */
+#define JUPITER_FLAG_DISC 0x80 /* disable periodic message */
+
+#define JUPITER_H_FLAG_BITS \
+ "\020\1NAK\2ACK\3REQUEST\4QUERY\5MBZ\6LOG\7CONN\10DISC"
+
+/* Log request messages (data payload when using JUPITER_FLAG_LOG) */
+struct jrequest {
+ u_short trigger; /* if 0, trigger on time trigger on
+ update (e.g. new almanac) */
+ u_short interval; /* frequency in seconds */
+ u_short offset; /* offset into minute */
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_GPOS (1000) */
+struct jgpos {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short sseq; /* sat measurement sequence number */
+ u_short navval; /* navigation soltuion validity */
+ u_short navtype; /* navigation solution type */
+ u_short nmeas; /* # of measurements used in solution */
+ u_short polar; /* if 1 then polar navigation */
+ u_short gweek; /* GPS week number */
+ u_short sweek[2]; /* GPS seconds into week */
+ u_short nsweek[2]; /* GPS nanoseconds into second */
+ u_short utcday; /* 1 to 31 */
+ u_short utcmon; /* 1 to 12 */
+ u_short utcyear; /* 1980 to 2079 */
+ u_short utchour; /* 0 to 23 */
+ u_short utcmin; /* 0 to 59 */
+ u_short utcsec; /* 0 to 59 */
+ u_short utcnsec[2]; /* 0 to 999999999 */
+ u_short lat[2]; /* latitude (radians) */
+ u_short lon[2]; /* longitude (radians) */
+ u_short height[2]; /* height (meters) */
+ u_short gsep; /* geoidal separation */
+ u_short speed[2]; /* ground speed (meters/sec) */
+ u_short course; /* true course (radians) */
+ u_short mvar;
+ u_short climb;
+ u_short mapd;
+ u_short herr[2];
+ u_short verr[2];
+ u_short terr[2];
+ u_short hverr;
+ u_short bias[2];
+ u_short biassd[2];
+ u_short drift[2];
+ u_short driftsd[2];
+ u_short dsum; /* checksum */
+};
+#define JUPITER_O_GPOS_NAV_NOALT 0x01 /* altitude used */
+#define JUPITER_O_GPOS_NAV_NODGPS 0x02 /* no differential GPS */
+#define JUPITER_O_GPOS_NAV_NOSAT 0x04 /* not enough satellites */
+#define JUPITER_O_GPOS_NAV_MAXH 0x08 /* exceeded max EHPE */
+#define JUPITER_O_GPOS_NAV_MAXV 0x10 /* exceeded max EVPE */
+
+/* JUPITER_O_CHAN (1002) */
+struct jchan {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short sseq; /* sat measurement sequence number */
+ u_short gweek; /* GPS week number */
+ u_short sweek[2]; /* GPS seconds into week */
+ u_short gpsns[2]; /* GPS nanoseconds from epoch */
+ struct jchan2 {
+ u_short flags; /* flags */
+ u_short prn; /* satellite PRN */
+ u_short chan; /* channel number */
+ } sat[12];
+ u_short dsum;
+};
+
+/* JUPITER_O_VIS (1003) */
+struct jvis {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short gdop; /* best possible GDOP */
+ u_short pdop; /* best possible PDOP */
+ u_short hdop; /* best possible HDOP */
+ u_short vdop; /* best possible VDOP */
+ u_short tdop; /* best possible TDOP */
+ u_short nvis; /* number of visible satellites */
+ struct jvis2 {
+ u_short prn; /* satellite PRN */
+ u_short azi; /* satellite azimuth (radians) */
+ u_short elev; /* satellite elevation (radians) */
+ } sat[12];
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_ID (1011) */
+struct jid {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ char chans[20]; /* number of channels (ascii) */
+ char vers[20]; /* software version (ascii) */
+ char date[20]; /* software date (ascii) */
+ char opts[20]; /* software options (ascii) */
+ char reserved[20];
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_USER (1012) */
+struct juser {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short status; /* operatinoal status */
+ u_short coldtmo; /* cold start time-out */
+ u_short dgpstmo; /* DGPS correction time-out*/
+ u_short emask; /* elevation mask */
+ u_short selcand[2]; /* selected candidate */
+ u_short solflags; /* solution validity criteria */
+ u_short nsat; /* number of satellites in track */
+ u_short herr[2]; /* minimum expected horizontal error */
+ u_short verr[2]; /* minimum expected vertical error */
+ u_short platform; /* application platform */
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_O_PULSE (1108) */
+struct jpulse {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_short reserved[5];
+ u_short sweek[2]; /* GPS seconds into week */
+ short offs; /* GPS to UTC time offset (seconds) */
+ u_short offns[2]; /* GPS to UTC offset (nanoseconds) */
+ u_short flags; /* flags */
+ u_short dsum; /* checksum */
+};
+#define JUPITER_O_PULSE_VALID 0x1 /* time mark validity */
+#define JUPITER_O_PULSE_UTC 0x2 /* GPS/UTC sync */
+
+/* JUPITER_O_EUP (1135) */
+struct jeup {
+ u_short stime[2]; /* set time (10 ms ticks) */
+ u_short seq; /* sequence number */
+ u_char dataid; /* data id */
+ u_char prn; /* satellite PRN */
+ u_short dsum; /* checksum */
+};
+
+/* JUPITER_I_RESTART (1303) */
+struct jrestart {
+ u_short seq; /* sequence number */
+ u_short flags;
+ u_short dsum; /* checksum */
+};
+#define JUPITER_I_RESTART_INVRAM 0x01
+#define JUPITER_I_RESTART_INVEEPROM 0x02
+#define JUPITER_I_RESTART_INVRTC 0x04
+#define JUPITER_I_RESTART_COLD 0x80
+
+/* JUPITER_I_PVTINIT (1200) */
+struct jpvtinit {
+ u_short flags;
+ u_short gweek; /* GPS week number */
+ u_short sweek[2]; /* GPS seconds into week */
+ u_short utcday; /* 1 to 31 */
+ u_short utcmon; /* 1 to 12 */
+ u_short utcyear; /* 1980 to 2079 */
+ u_short utchour; /* 0 to 23 */
+ u_short utcmin; /* 0 to 59 */
+ u_short utcsec; /* 0 to 59 */
+ u_short lat[2]; /* latitude (radians) */
+ u_short lon[2]; /* longitude (radians) */
+ u_short height[2]; /* height (meters) */
+ u_short speed[2]; /* ground speed (meters/sec) */
+ u_short course; /* true course (radians) */
+ u_short climb;
+ u_short dsum;
+};
+#define JUPITER_I_PVTINIT_FORCE 0x01
+#define JUPITER_I_PVTINIT_GPSVAL 0x02
+#define JUPITER_I_PVTINIT_UTCVAL 0x04
+#define JUPITER_I_PVTINIT_POSVAL 0x08
+#define JUPITER_I_PVTINIT_ALTVAL 0x10
+#define JUPITER_I_PVTINIT_SPDVAL 0x12
+#define JUPITER_I_PVTINIT_MAGVAL 0x14
+#define JUPITER_I_PVTINIT_CLIMBVAL 0x18
+
+/* JUPITER_I_PLAT (1220) */
+struct jplat {
+ u_short seq; /* sequence number */
+ u_short platform; /* application platform */
+ u_short dsum;
+};
+#define JUPITER_I_PLAT_DEFAULT 0 /* default dynamics */
+#define JUPITER_I_PLAT_LOW 2 /* pedestrian */
+#define JUPITER_I_PLAT_MED 5 /* land (e.g. automobile) */
+#define JUPITER_I_PLAT_HIGH 6 /* air */
diff --git a/ntpd/map_vme.c b/ntpd/map_vme.c
new file mode 100644
index 0000000..e4569ce
--- /dev/null
+++ b/ntpd/map_vme.c
@@ -0,0 +1,135 @@
+/********************************************************/
+/* map_vme.c */
+/* VME control of TrueTime VME-SG sync gen card */
+/* and TrueTime GPS-VME receiver card */
+/* Version for 700 series HPUX 9.0 */
+/* Richard E.Schmidt, US Naval Observatory, Washington */
+/* 27 March 94 */
+/********************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_GPSVME)
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/rtprio.h> /* for rtprio */
+#include <sys/lock.h> /* for plock */
+#include "/etc/conf/machine/vme2.h"
+#include "/etc/conf/h/io.h"
+#include "gps.h"
+
+/* GLOBALS */
+void *gps_base;
+unsigned short *greg[NREGS];
+struct vme2_map_addr ma; /* memory mapped structure */
+int fd; /* file descriptor for VME */
+
+void unmap_vme ();
+
+caddr_t
+map_vme (
+ char *filename
+ )
+{
+ int ret;
+ caddr_t base;
+ struct vme2_io_testx tx;
+ caddr_t cp;
+
+#define VME_START_ADDR 0x00000 /* Starting address in A16N VME Space */
+#define VMESIZE 0xFF /* 256 bytes of A16N space length */
+
+ /*
+ To create the HP9000/700 series device file, /dev/vme2:
+ mknod /dev/vme2 c 44 0x0; chmod 600 /dev/vme2
+
+ Then must create /etc/vme.CFG and run /etc/vme_config and reboot.
+ */
+ if ((fd = open (filename, O_RDWR)) < 0) {
+ printf("ERROR: VME bus adapter open failed. errno:%d\n",
+ errno);
+ if(errno == ENODEV) {
+ printf("ENODEV. Is driver in kernel? vme2 in dfile?\n");
+ }
+ exit(errno);
+ }
+ tx.card_type = VME_A16;
+ tx.vme_addr = VME_START_ADDR;
+ tx.width = SHORT_WIDE;
+
+ if(ioctl(fd, VME2_IO_TESTR, &tx)) {
+ printf("ioctl to test VME space failed. Errno: %d\n",
+ errno);
+ exit(errno);
+ }
+ if(tx.error)
+ printf("io_testr failed internal error %d\n",tx.error);
+ if(tx.access_result < 0) {
+ printf("io_testr failed\n");
+ exit(2);
+ }
+
+ /* If successful mmap the device */
+ /* NOW MAP THE CARD */
+ ma.card_type = VME_A16;
+ ma.vme_addr = VME_START_ADDR;
+ ma.size = VMESIZE;
+
+ if(ioctl(fd, VME2_MAP_ADDR, &ma)) {
+ printf("ioctl to map VME space failed. Errno: %d\n",
+ errno);
+ exit(errno);
+ }
+ if(ma.error) {
+ printf("ioctl to map VME failed\n");
+ exit(ENOMEM);
+ }
+ base = ma.user_addr;
+ return(base);
+}
+
+
+void
+unmap_vme(void)
+{
+ if(ioctl(fd, VME2_UNMAP_ADDR, &ma))
+ printf("ioctl to unmap VME space failed. Errno: %d\n",
+ errno);
+ close(fd);
+ return;
+}
+
+
+int
+init_vme(boid)
+{
+ /* set up address offsets */
+
+ gps_base = map_vme (GPS_VME);
+
+/* offsets from base address: */
+
+ greg[0] = (unsigned short *)gps_base + GFRZ1;
+ greg[1] = (unsigned short *)gps_base + GUFRZ1;
+ greg[2] = (unsigned short *)gps_base + GREG1A;
+ greg[3] = (unsigned short *)gps_base + GREG1B;
+ greg[4] = (unsigned short *)gps_base + GREG1C;
+ greg[5] = (unsigned short *)gps_base + GREG1D;
+ greg[6] = (unsigned short *)gps_base + GREG1E;
+
+ return (0);
+}
+
+#else /* not (REFCLOCK && CLOCK_GPSVME) */
+int map_vme_bs;
+#endif /* not (REFCLOCK && CLOCK_GPSVME) */
diff --git a/ntpd/ntp_config.c b/ntpd/ntp_config.c
new file mode 100644
index 0000000..f1428b1
--- /dev/null
+++ b/ntpd/ntp_config.c
@@ -0,0 +1,2371 @@
+/*
+ * ntp_config.c - read and apply configuration information
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_NETINFO
+# include <netinfo/ni.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+#include "ntp_config.h"
+#include "ntp_cmdargs.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <signal.h>
+#ifndef SIGCHLD
+# define SIGCHLD SIGCLD
+#endif
+#if !defined(VMS)
+# ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+# endif
+#endif /* VMS */
+
+#ifdef SYS_WINNT
+# include <io.h>
+extern HANDLE ResolverThreadHandle;
+#endif /* SYS_WINNT */
+
+#include <netdb.h>
+
+extern int priority_done;
+
+/*
+ * These routines are used to read the configuration file at
+ * startup time. An entry in the file must fit on a single line.
+ * Entries are processed as multiple tokens separated by white space
+ * Lines are considered terminated when a '#' is encountered. Blank
+ * lines are ignored.
+ */
+/*
+ * Translation table - keywords to function index
+ */
+struct keyword {
+ const char *text;
+ int keytype;
+};
+
+/*
+ * Command keywords
+ */
+static struct keyword keywords[] = {
+ { "automax", CONFIG_AUTOMAX },
+ { "broadcast", CONFIG_BROADCAST },
+ { "broadcastclient", CONFIG_BROADCASTCLIENT },
+ { "broadcastdelay", CONFIG_BDELAY },
+ { "calldelay", CONFIG_CDELAY},
+#ifdef OPENSSL
+ { "crypto", CONFIG_CRYPTO },
+#endif /* OPENSSL */
+ { "controlkey", CONFIG_CONTROLKEY },
+ { "disable", CONFIG_DISABLE },
+ { "driftfile", CONFIG_DRIFTFILE },
+ { "enable", CONFIG_ENABLE },
+ { "filegen", CONFIG_FILEGEN },
+ { "fudge", CONFIG_FUDGE },
+ { "includefile", CONFIG_INCLUDEFILE },
+ { "keys", CONFIG_KEYS },
+ { "keysdir", CONFIG_KEYSDIR },
+ { "logconfig", CONFIG_LOGCONFIG },
+ { "logfile", CONFIG_LOGFILE },
+ { "manycastclient", CONFIG_MANYCASTCLIENT },
+ { "manycastserver", CONFIG_MANYCASTSERVER },
+ { "multicastclient", CONFIG_MULTICASTCLIENT },
+ { "peer", CONFIG_PEER },
+ { "phone", CONFIG_PHONE },
+ { "pidfile", CONFIG_PIDFILE },
+ { "discard", CONFIG_DISCARD },
+ { "requestkey", CONFIG_REQUESTKEY },
+ { "restrict", CONFIG_RESTRICT },
+ { "revoke", CONFIG_REVOKE },
+ { "server", CONFIG_SERVER },
+ { "setvar", CONFIG_SETVAR },
+ { "statistics", CONFIG_STATISTICS },
+ { "statsdir", CONFIG_STATSDIR },
+ { "tick", CONFIG_ADJ },
+ { "tinker", CONFIG_TINKER },
+ { "tos", CONFIG_TOS },
+ { "trap", CONFIG_TRAP },
+ { "trustedkey", CONFIG_TRUSTEDKEY },
+ { "ttl", CONFIG_TTL },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "peer", "server", "broadcast" modifier keywords
+ */
+static struct keyword mod_keywords[] = {
+ { "autokey", CONF_MOD_SKEY },
+ { "burst", CONF_MOD_BURST },
+ { "iburst", CONF_MOD_IBURST },
+ { "key", CONF_MOD_KEY },
+ { "maxpoll", CONF_MOD_MAXPOLL },
+ { "minpoll", CONF_MOD_MINPOLL },
+ { "mode", CONF_MOD_MODE }, /* refclocks */
+ { "noselect", CONF_MOD_NOSELECT },
+ { "prefer", CONF_MOD_PREFER },
+ { "ttl", CONF_MOD_TTL }, /* NTP peers */
+ { "version", CONF_MOD_VERSION },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "restrict" modifier keywords
+ */
+static struct keyword res_keywords[] = {
+ { "ignore", CONF_RES_IGNORE },
+ { "limited", CONF_RES_LIMITED },
+ { "kod", CONF_RES_DEMOBILIZE },
+ { "lowpriotrap", CONF_RES_LPTRAP },
+ { "mask", CONF_RES_MASK },
+ { "nomodify", CONF_RES_NOMODIFY },
+ { "nopeer", CONF_RES_NOPEER },
+ { "noquery", CONF_RES_NOQUERY },
+ { "noserve", CONF_RES_NOSERVE },
+ { "notrap", CONF_RES_NOTRAP },
+ { "notrust", CONF_RES_NOTRUST },
+ { "ntpport", CONF_RES_NTPPORT },
+ { "version", CONF_RES_VERSION },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "trap" modifier keywords
+ */
+static struct keyword trap_keywords[] = {
+ { "port", CONF_TRAP_PORT },
+ { "interface", CONF_TRAP_INTERFACE },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "fudge" modifier keywords
+ */
+static struct keyword fudge_keywords[] = {
+ { "flag1", CONF_FDG_FLAG1 },
+ { "flag2", CONF_FDG_FLAG2 },
+ { "flag3", CONF_FDG_FLAG3 },
+ { "flag4", CONF_FDG_FLAG4 },
+ { "refid", CONF_FDG_REFID },
+ { "stratum", CONF_FDG_STRATUM },
+ { "time1", CONF_FDG_TIME1 },
+ { "time2", CONF_FDG_TIME2 },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "filegen" modifier keywords
+ */
+static struct keyword filegen_keywords[] = {
+ { "disable", CONF_FGEN_FLAG_DISABLE },
+ { "enable", CONF_FGEN_FLAG_ENABLE },
+ { "file", CONF_FGEN_FILE },
+ { "link", CONF_FGEN_FLAG_LINK },
+ { "nolink", CONF_FGEN_FLAG_NOLINK },
+ { "type", CONF_FGEN_TYPE },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "type" modifier keywords
+ */
+static struct keyword fgen_types[] = {
+ { "age", FILEGEN_AGE },
+ { "day", FILEGEN_DAY },
+ { "month", FILEGEN_MONTH },
+ { "none", FILEGEN_NONE },
+ { "pid", FILEGEN_PID },
+ { "week", FILEGEN_WEEK },
+ { "year", FILEGEN_YEAR },
+ { "", CONFIG_UNKNOWN}
+};
+
+/*
+ * "enable", "disable" modifier keywords
+ */
+static struct keyword flags_keywords[] = {
+ { "auth", PROTO_AUTHENTICATE },
+ { "bclient", PROTO_BROADCLIENT },
+ { "calibrate", PROTO_CAL },
+ { "kernel", PROTO_KERNEL },
+ { "monitor", PROTO_MONITOR },
+ { "ntp", PROTO_NTP },
+ { "pps", PROTO_PPS },
+ { "stats", PROTO_FILEGEN },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "discard" modifier keywords
+ */
+static struct keyword discard_keywords[] = {
+ { "average", CONF_DISCARD_AVERAGE },
+ { "minimum", CONF_DISCARD_MINIMUM },
+ { "monitor", CONF_DISCARD_MONITOR },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "tinker" modifier keywords
+ */
+static struct keyword tinker_keywords[] = {
+ { "step", CONF_CLOCK_MAX },
+ { "panic", CONF_CLOCK_PANIC },
+ { "dispersion", CONF_CLOCK_PHI },
+ { "stepout", CONF_CLOCK_MINSTEP },
+ { "allan", CONF_CLOCK_ALLAN },
+ { "huffpuff", CONF_CLOCK_HUFFPUFF },
+ { "freq", CONF_CLOCK_FREQ },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "tos" modifier keywords
+ */
+static struct keyword tos_keywords[] = {
+ { "minclock", CONF_TOS_MINCLOCK },
+ { "minsane", CONF_TOS_MINSANE },
+ { "floor", CONF_TOS_FLOOR },
+ { "ceiling", CONF_TOS_CEILING },
+ { "cohort", CONF_TOS_COHORT },
+ { "", CONFIG_UNKNOWN }
+};
+
+#ifdef OPENSSL
+/*
+ * "crypto" modifier keywords
+ */
+static struct keyword crypto_keywords[] = {
+ { "cert", CONF_CRYPTO_CERT },
+ { "gqpar", CONF_CRYPTO_GQPAR },
+ { "host", CONF_CRYPTO_RSA },
+ { "iffpar", CONF_CRYPTO_IFFPAR },
+ { "leap", CONF_CRYPTO_LEAP },
+ { "mvpar", CONF_CRYPTO_MVPAR },
+ { "pw", CONF_CRYPTO_PW },
+ { "randfile", CONF_CRYPTO_RAND },
+ { "sign", CONF_CRYPTO_SIGN },
+ { "", CONFIG_UNKNOWN }
+};
+#endif /* OPENSSL */
+
+/*
+ * Address type selection, IPv4 or IPv4.
+ * Used on various lines.
+ */
+static struct keyword addr_type[] = {
+ { "-4", CONF_ADDR_IPV4 },
+ { "-6", CONF_ADDR_IPV6 },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "logconfig" building blocks
+ */
+struct masks {
+ const char *name;
+ unsigned long mask;
+};
+
+static struct masks logcfg_class[] = {
+ { "clock", NLOG_OCLOCK },
+ { "peer", NLOG_OPEER },
+ { "sync", NLOG_OSYNC },
+ { "sys", NLOG_OSYS },
+ { (char *)0, 0 }
+};
+
+static struct masks logcfg_item[] = {
+ { "info", NLOG_INFO },
+ { "allinfo", NLOG_SYSINFO|NLOG_PEERINFO|NLOG_CLOCKINFO|NLOG_SYNCINFO },
+ { "events", NLOG_EVENT },
+ { "allevents", NLOG_SYSEVENT|NLOG_PEEREVENT|NLOG_CLOCKEVENT|NLOG_SYNCEVENT },
+ { "status", NLOG_STATUS },
+ { "allstatus", NLOG_SYSSTATUS|NLOG_PEERSTATUS|NLOG_CLOCKSTATUS|NLOG_SYNCSTATUS },
+ { "statistics", NLOG_STATIST },
+ { "allstatistics", NLOG_SYSSTATIST|NLOG_PEERSTATIST|NLOG_CLOCKSTATIST|NLOG_SYNCSTATIST },
+ { "allclock", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OCLOCK },
+ { "allpeer", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OPEER },
+ { "allsys", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OSYS },
+ { "allsync", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<<NLOG_OSYNC },
+ { "all", NLOG_SYSMASK|NLOG_PEERMASK|NLOG_CLOCKMASK|NLOG_SYNCMASK },
+ { (char *)0, 0 }
+};
+
+/*
+ * Limits on things
+ */
+#define MAXTOKENS 20 /* 20 tokens on line */
+#define MAXLINE 1024 /* maximum length of line */
+#define MAXPHONE 5 /* maximum number of phone strings */
+#define MAXPPS 20 /* maximum length of PPS device string */
+#define MAXINCLUDELEVEL 5 /* maximum include file levels */
+
+/*
+ * Miscellaneous macros
+ */
+#define STRSAME(s1, s2) (*(s1) == *(s2) && strcmp((s1), (s2)) == 0)
+#define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0')
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+#define KEY_TYPE_MD5 4
+
+/*
+ * File descriptor used by the resolver save routines, and temporary file
+ * name.
+ */
+int call_resolver = 1; /* ntp-genkeys sets this to 0, for example */
+static FILE *res_fp;
+#ifndef SYS_WINNT
+static char res_file[20]; /* enough for /tmp/ntpXXXXXX\0 */
+#define RES_TEMPFILE "/tmp/ntpXXXXXX"
+#else
+static char res_file[MAX_PATH];
+#endif /* SYS_WINNT */
+
+/*
+ * Definitions of things either imported from or exported to outside
+ */
+char const *progname;
+char sys_phone[MAXPHONE][MAXDIAL]; /* ACTS phone numbers */
+char *keysdir = NTP_KEYSDIR; /* crypto keys directory */
+char pps_device[MAXPPS + 1]; /* PPS device name */
+#if defined(HAVE_SCHED_SETSCHEDULER)
+int config_priority_override = 0;
+int config_priority;
+#endif
+
+const char *config_file;
+#ifdef HAVE_NETINFO
+ struct netinfo_config_state *config_netinfo = NULL;
+ int check_netinfo = 1;
+#endif /* HAVE_NETINFO */
+#ifdef SYS_WINNT
+ char *alt_config_file;
+ LPTSTR temp;
+ char config_file_storage[MAX_PATH];
+ char alt_config_file_storage[MAX_PATH];
+#endif /* SYS_WINNT */
+
+#ifdef HAVE_NETINFO
+/*
+ * NetInfo configuration state
+ */
+struct netinfo_config_state {
+ void *domain; /* domain with config */
+ ni_id config_dir; /* ID config dir */
+ int prop_index; /* current property */
+ int val_index; /* current value */
+ char **val_list; /* value list */
+};
+#endif
+
+/*
+ * Function prototypes
+ */
+static unsigned long get_pfxmatch P((char **, struct masks *));
+static unsigned long get_match P((char *, struct masks *));
+static unsigned long get_logmask P((char *));
+#ifdef HAVE_NETINFO
+static struct netinfo_config_state *get_netinfo_config P((void));
+static void free_netinfo_config P((struct netinfo_config_state *));
+static int gettokens_netinfo P((struct netinfo_config_state *, char **, int *));
+#endif
+static int gettokens P((FILE *, char *, char **, int *));
+static int matchkey P((char *, struct keyword *, int));
+static int getnetnum P((const char *, struct sockaddr_storage *, int));
+static void save_resolve P((char *, int, int, int, int, u_int, int,
+ keyid_t, u_char *));
+static void do_resolve_internal P((void));
+static void abort_resolve P((void));
+#if !defined(VMS) && !defined(SYS_WINNT)
+static RETSIGTYPE catchchild P((int));
+#endif /* VMS */
+
+/*
+ * get_pfxmatch - find value for prefixmatch
+ * and update char * accordingly
+ */
+static unsigned long
+get_pfxmatch(
+ char ** s,
+ struct masks *m
+ )
+{
+ while (m->name) {
+ if (strncmp(*s, m->name, strlen(m->name)) == 0) {
+ *s += strlen(m->name);
+ return m->mask;
+ } else {
+ m++;
+ }
+ }
+ return 0;
+}
+
+/*
+ * get_match - find logmask value
+ */
+static unsigned long
+get_match(
+ char *s,
+ struct masks *m
+ )
+{
+ while (m->name) {
+ if (strcmp(s, m->name) == 0) {
+ return m->mask;
+ } else {
+ m++;
+ }
+ }
+ return 0;
+}
+
+/*
+ * get_logmask - build bitmask for ntp_syslogmask
+ */
+static unsigned long
+get_logmask(
+ char *s
+ )
+{
+ char *t;
+ unsigned long offset;
+ unsigned long mask;
+
+ t = s;
+ offset = get_pfxmatch(&t, logcfg_class);
+ mask = get_match(t, logcfg_item);
+
+ if (mask)
+ return mask << offset;
+ else
+ msyslog(LOG_ERR, "logconfig: illegal argument %s - ignored", s);
+
+ return 0;
+}
+
+
+/*
+ * getconfig - get command line options and read the configuration file
+ */
+void
+getconfig(
+ int argc,
+ char *argv[]
+ )
+{
+ register int i;
+ int c;
+ int errflg;
+ int istart;
+ int peerversion;
+ int minpoll;
+ int maxpoll;
+ int ttl;
+ long stratum;
+ unsigned long ul;
+ keyid_t peerkey;
+ u_char *peerkeystr;
+ u_long fudgeflag;
+ u_int peerflags;
+ int hmode;
+ struct sockaddr_storage peeraddr;
+ struct sockaddr_storage maskaddr;
+ FILE *fp[MAXINCLUDELEVEL+1];
+ FILE *includefile;
+ int includelevel = 0;
+ char line[MAXLINE];
+ char *(tokens[MAXTOKENS]);
+ int ntokens = 0;
+ int tok = CONFIG_UNKNOWN;
+ struct interface *localaddr;
+ struct refclockstat clock_stat;
+ FILEGEN *filegen;
+
+ /*
+ * Initialize, initialize
+ */
+ errflg = 0;
+ /* HMS: don't initialize debug to 0 here! */
+#ifndef SYS_WINNT
+ config_file = CONFIG_FILE;
+#else
+ temp = CONFIG_FILE;
+ if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)config_file_storage, (DWORD)sizeof(config_file_storage))) {
+ msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m\n");
+ exit(1);
+ }
+ config_file = config_file_storage;
+
+ temp = ALT_CONFIG_FILE;
+ if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)alt_config_file_storage, (DWORD)sizeof(alt_config_file_storage))) {
+ msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m\n");
+ exit(1);
+ }
+ alt_config_file = alt_config_file_storage;
+
+#endif /* SYS_WINNT */
+ progname = argv[0];
+ res_fp = NULL;
+ memset((char *)sys_phone, 0, sizeof(sys_phone));
+ ntp_syslogmask = NLOG_SYNCMASK; /* set more via logconfig */
+
+ /*
+ * install a non default variable with this daemon version
+ */
+ (void) sprintf(line, "daemon_version=\"%s\"", Version);
+ set_sys_var(line, strlen(line)+1, RO);
+
+ /*
+ * Say how we're setting the time of day
+ */
+ (void) sprintf(line, "settimeofday=\"%s\"", set_tod_using);
+ set_sys_var(line, strlen(line)+1, RO);
+
+ /*
+ * Initialize the loop.
+ */
+ loop_config(LOOP_DRIFTINIT, 0.);
+
+ getCmdOpts(argc, argv);
+
+ if (
+ (fp[0] = fopen(FindConfig(config_file), "r")) == NULL
+#ifdef HAVE_NETINFO
+ /* If there is no config_file, try NetInfo. */
+ && check_netinfo && !(config_netinfo = get_netinfo_config())
+#endif /* HAVE_NETINFO */
+ ) {
+ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(config_file));
+ msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(config_file));
+#ifdef SYS_WINNT
+ /* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */
+
+ if ((fp[0] = fopen(FindConfig(alt_config_file), "r")) == NULL) {
+
+ /*
+ * Broadcast clients can sometimes run without
+ * a configuration file.
+ */
+
+ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(alt_config_file));
+ msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(alt_config_file));
+ return;
+ }
+#else /* not SYS_WINNT */
+ return;
+#endif /* not SYS_WINNT */
+ }
+
+ for (;;) {
+ if (fp[includelevel])
+ tok = gettokens(fp[includelevel], line, tokens, &ntokens);
+#ifdef HAVE_NETINFO
+ else
+ tok = gettokens_netinfo(config_netinfo, tokens, &ntokens);
+#endif /* HAVE_NETINFO */
+
+ if (tok == CONFIG_UNKNOWN) {
+ if (includelevel > 0) {
+ fclose(fp[includelevel--]);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ switch(tok) {
+ case CONFIG_PEER:
+ case CONFIG_SERVER:
+ case CONFIG_MANYCASTCLIENT:
+ case CONFIG_BROADCAST:
+ if (tok == CONFIG_PEER)
+ hmode = MODE_ACTIVE;
+ else if (tok == CONFIG_SERVER)
+ hmode = MODE_CLIENT;
+ else if (tok == CONFIG_MANYCASTCLIENT)
+ hmode = MODE_CLIENT;
+ else
+ hmode = MODE_BROADCAST;
+
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "No address for %s, line ignored",
+ tokens[0]);
+ break;
+ }
+
+ istart = 1;
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ switch (matchkey(tokens[istart], addr_type, 0)) {
+ case CONF_ADDR_IPV4:
+ peeraddr.ss_family = AF_INET;
+ istart++;
+ break;
+ case CONF_ADDR_IPV6:
+ peeraddr.ss_family = AF_INET6;
+ istart++;
+ break;
+ }
+
+ if (!getnetnum(tokens[istart], &peeraddr, 0)) {
+ errflg = -1;
+ } else {
+ errflg = 0;
+
+ if (
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ ISBADADR(&peeraddr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ /*
+ * Shouldn't be able to specify multicast
+ * address for server/peer!
+ * and unicast address for manycastclient!
+ */
+ if (peeraddr.ss_family == AF_INET) {
+ if (((tok == CONFIG_SERVER) ||
+ (tok == CONFIG_PEER)) &&
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ IN_CLASSD(ntohl(((struct sockaddr_in*)&peeraddr)->sin_addr.s_addr))) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ if ((tok == CONFIG_MANYCASTCLIENT) &&
+ !IN_CLASSD(ntohl(((struct sockaddr_in*)&peeraddr)->sin_addr.s_addr))) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ }
+ else if(peeraddr.ss_family == AF_INET6) {
+ if (((tok == CONFIG_SERVER) ||
+ (tok == CONFIG_PEER)) &&
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)&peeraddr)->sin6_addr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure in valid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ if ((tok == CONFIG_MANYCASTCLIENT) &&
+ !IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)&peeraddr)->sin6_addr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure in valid address %s",
+ stoa(&peeraddr));
+ break;
+ }
+ }
+ }
+
+ peerversion = NTP_VERSION;
+ minpoll = NTP_MINDPOLL;
+ maxpoll = NTP_MAXDPOLL;
+ peerkey = 0;
+ peerkeystr = (u_char *)"*";
+ peerflags = 0;
+ ttl = 0;
+ istart++;
+ for (i = istart; i < ntokens; i++)
+ switch (matchkey(tokens[i], mod_keywords, 1)) {
+ case CONF_MOD_VERSION:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "peer/server version requires an argument");
+ errflg = 1;
+ break;
+ }
+ peerversion = atoi(tokens[++i]);
+ if ((u_char)peerversion > NTP_VERSION
+ || (u_char)peerversion < NTP_OLDVERSION) {
+ msyslog(LOG_ERR,
+ "inappropriate version number %s, line ignored",
+ tokens[i]);
+ errflg = 1;
+ }
+ break;
+
+ case CONF_MOD_KEY:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "key: argument required");
+ errflg = 1;
+ break;
+ }
+ peerkey = (int)atol(tokens[++i]);
+ peerflags |= FLAG_AUTHENABLE;
+ break;
+
+ case CONF_MOD_MINPOLL:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "minpoll: argument required");
+ errflg = 1;
+ break;
+ }
+ minpoll = atoi(tokens[++i]);
+ if (minpoll < NTP_MINPOLL) {
+ msyslog(LOG_INFO,
+ "minpoll: provided value (%d) is below minimum (%d)",
+ minpoll, NTP_MINPOLL);
+ minpoll = NTP_MINPOLL;
+ }
+ break;
+
+ case CONF_MOD_MAXPOLL:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "maxpoll: argument required"
+ );
+ errflg = 1;
+ break;
+ }
+ maxpoll = atoi(tokens[++i]);
+ if (maxpoll > NTP_MAXPOLL) {
+ msyslog(LOG_INFO,
+ "maxpoll: provided value (%d) is above maximum (%d)",
+ maxpoll, NTP_MAXPOLL);
+ maxpoll = NTP_MAXPOLL;
+ }
+ break;
+
+ case CONF_MOD_PREFER:
+ peerflags |= FLAG_PREFER;
+ break;
+
+ case CONF_MOD_NOSELECT:
+ peerflags |= FLAG_NOSELECT;
+ break;
+
+ case CONF_MOD_BURST:
+ peerflags |= FLAG_BURST;
+ break;
+
+ case CONF_MOD_IBURST:
+ peerflags |= FLAG_IBURST;
+ break;
+#ifdef OPENSSL
+ case CONF_MOD_SKEY:
+ peerflags |= FLAG_SKEY |
+ FLAG_AUTHENABLE;
+ break;
+#endif /* OPENSSL */
+
+ case CONF_MOD_TTL:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "ttl: argument required");
+ errflg = 1;
+ break;
+ }
+ ttl = atoi(tokens[++i]);
+ if (ttl >= MAX_TTL) {
+ msyslog(LOG_ERR,
+ "ttl: invalid argument");
+ errflg = 1;
+ }
+ break;
+
+ case CONF_MOD_MODE:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "mode: argument required");
+ errflg = 1;
+ break;
+ }
+ ttl = atoi(tokens[++i]);
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg = 1;
+ break;
+ }
+ if (minpoll > maxpoll) {
+ msyslog(LOG_ERR,
+ "config error: minpoll > maxpoll");
+ errflg = 1;
+ }
+ if (errflg == 0) {
+ if (peer_config(&peeraddr,
+ ANY_INTERFACE_CHOOSE(&peeraddr), hmode,
+ peerversion, minpoll, maxpoll, peerflags,
+ ttl, peerkey, peerkeystr) == 0) {
+ msyslog(LOG_ERR,
+ "configuration of %s failed",
+ stoa(&peeraddr));
+ }
+ if (tok == CONFIG_MANYCASTCLIENT)
+ proto_config(PROTO_MULTICAST_ADD,
+ 0, 0., &peeraddr);
+
+ } else if (errflg == -1) {
+ save_resolve(tokens[1], hmode, peerversion,
+ minpoll, maxpoll, peerflags, ttl,
+ peerkey, peerkeystr);
+ }
+ break;
+
+ case CONFIG_DRIFTFILE:
+ if (ntokens >= 2)
+ stats_config(STATS_FREQ_FILE, tokens[1]);
+ else
+ stats_config(STATS_FREQ_FILE, (char *)0);
+ break;
+
+ case CONFIG_PIDFILE:
+ if (ntokens >= 2)
+ stats_config(STATS_PID_FILE, tokens[1]);
+ else
+ stats_config(STATS_PID_FILE, (char *)0);
+ break;
+
+ case CONFIG_INCLUDEFILE:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR, "includefile needs one argument");
+ break;
+ }
+ if (includelevel >= MAXINCLUDELEVEL) {
+ fprintf(stderr, "getconfig: Maximum include file level exceeded.\n");
+ msyslog(LOG_INFO, "getconfig: Maximum include file level exceeded.");
+ break;
+ }
+ includefile = fopen(FindConfig(tokens[1]), "r");
+ if (includefile == NULL) {
+ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(tokens[1]));
+ msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(tokens[1]));
+ break;
+ }
+ fp[++includelevel] = includefile;
+ break;
+
+ case CONFIG_LOGFILE:
+ if (ntokens >= 2) {
+ FILE *new_file;
+
+ new_file = fopen(tokens[1], "a");
+ if (new_file != NULL) {
+ NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "logging to file %s", tokens[1]);
+ if (syslog_file != NULL &&
+ fileno(syslog_file) != fileno(new_file))
+ (void)fclose(syslog_file);
+
+ syslog_file = new_file;
+ syslogit = 0;
+ }
+ else
+ msyslog(LOG_ERR,
+ "Cannot open log file %s",
+ tokens[1]);
+ }
+ else
+ msyslog(LOG_ERR, "logfile needs one argument");
+ break;
+
+ case CONFIG_LOGCONFIG:
+ for (i = 1; i < ntokens; i++)
+ {
+ int add = 1;
+ int equals = 0;
+ char * s = &tokens[i][0];
+
+ switch (*s) {
+ case '+':
+ case '-':
+ case '=':
+ add = *s == '+';
+ equals = *s == '=';
+ s++;
+ break;
+
+ default:
+ break;
+ }
+ if (equals) {
+ ntp_syslogmask = get_logmask(s);
+ } else {
+ if (add) {
+ ntp_syslogmask |= get_logmask(s);
+ } else {
+ ntp_syslogmask &= ~get_logmask(s);
+ }
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("ntp_syslogmask = 0x%08lx (%s)\n", ntp_syslogmask, tokens[i]);
+#endif
+ }
+ break;
+
+ case CONFIG_BROADCASTCLIENT:
+ proto_config(PROTO_BROADCLIENT, 1, 0., NULL);
+ break;
+
+ case CONFIG_MULTICASTCLIENT:
+ case CONFIG_MANYCASTSERVER:
+ if (ntokens > 1) {
+ istart = 1;
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ switch (matchkey(tokens[istart],
+ addr_type, 0)) {
+ case CONF_ADDR_IPV4:
+ peeraddr.ss_family = AF_INET;
+ istart++;
+ break;
+ case CONF_ADDR_IPV6:
+ peeraddr.ss_family = AF_INET6;
+ istart++;
+ break;
+ }
+ /*
+ * Abuse maskaddr to store the prefered ip
+ * version.
+ */
+ memset((char *)&maskaddr, 0, sizeof(maskaddr));
+ maskaddr.ss_family = peeraddr.ss_family;
+
+ for (i = istart; i < ntokens; i++) {
+ memset((char *)&peeraddr, 0,
+ sizeof(peeraddr));
+ peeraddr.ss_family = maskaddr.ss_family;
+ if (getnetnum(tokens[i], &peeraddr, 1))
+ proto_config(PROTO_MULTICAST_ADD,
+ 0, 0., &peeraddr);
+ }
+ } else
+ proto_config(PROTO_MULTICAST_ADD,
+ 0, 0., NULL);
+ if (tok == CONFIG_MULTICASTCLIENT)
+ sys_bclient = 1;
+ else if (tok == CONFIG_MANYCASTSERVER)
+ sys_manycastserver = 1;
+ break;
+
+ case CONFIG_KEYS:
+ if (ntokens >= 2) {
+ getauthkeys(tokens[1]);
+ }
+ break;
+
+ case CONFIG_KEYSDIR:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "Keys directory name required");
+ break;
+ }
+ keysdir = emalloc(strlen(tokens[1]) + 1);
+ strcpy(keysdir, tokens[1]);
+ break;
+
+ case CONFIG_TINKER:
+ for (i = 1; i < ntokens; i++) {
+ int temp;
+ double ftemp;
+
+ temp = matchkey(tokens[i++], tinker_keywords, 1);
+ if (i > ntokens - 1) {
+ msyslog(LOG_ERR,
+ "tinker: missing argument");
+ errflg++;
+ break;
+ }
+ sscanf(tokens[i], "%lf", &ftemp);
+ switch(temp) {
+
+ case CONF_CLOCK_MAX:
+ loop_config(LOOP_MAX, ftemp);
+ break;
+
+ case CONF_CLOCK_PANIC:
+ loop_config(LOOP_PANIC, ftemp);
+ break;
+
+ case CONF_CLOCK_PHI:
+ loop_config(LOOP_PHI, ftemp);
+ break;
+
+ case CONF_CLOCK_MINSTEP:
+ loop_config(LOOP_MINSTEP, ftemp);
+ break;
+
+ case CONF_CLOCK_ALLAN:
+ loop_config(LOOP_ALLAN, ftemp);
+ break;
+
+ case CONF_CLOCK_HUFFPUFF:
+ loop_config(LOOP_HUFFPUFF, ftemp);
+ break;
+
+ case CONF_CLOCK_FREQ:
+ loop_config(LOOP_FREQ, ftemp);
+ break;
+ }
+ }
+ break;
+
+ case CONFIG_TOS:
+ for (i = 1; i < ntokens; i++) {
+ int temp;
+ double ftemp;
+
+ temp = matchkey(tokens[i++], tos_keywords, 1);
+ if (i > ntokens - 1) {
+ msyslog(LOG_ERR,
+ "tinker: missing argument");
+ errflg++;
+ break;
+ }
+ sscanf(tokens[i], "%lf", &ftemp);
+ switch(temp) {
+
+ case CONF_TOS_MINCLOCK:
+ proto_config(PROTO_MINCLOCK, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_MINSANE:
+ proto_config(PROTO_MINSANE, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_FLOOR:
+ proto_config(PROTO_FLOOR, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_CEILING:
+ proto_config(PROTO_CEILING, 0, ftemp, NULL);
+ break;
+
+ case CONF_TOS_COHORT:
+ proto_config(PROTO_COHORT, 0, ftemp, NULL);
+ break;
+ }
+ }
+ break;
+
+ case CONFIG_TTL:
+ for (i = 1; i < ntokens && i < MAX_TTL; i++) {
+ sys_ttl[i - 1] = (u_char) atoi(tokens[i]);
+ sys_ttlmax = i - 1;
+ }
+ break;
+
+ case CONFIG_DISCARD:
+ for (i = 1; i < ntokens; i++) {
+ int temp;
+
+ temp = matchkey(tokens[i++],
+ discard_keywords, 1);
+ if (i > ntokens - 1) {
+ msyslog(LOG_ERR,
+ "discard: missing argument");
+ errflg++;
+ break;
+ }
+ switch(temp) {
+ case CONF_DISCARD_AVERAGE:
+ res_avg_interval = atoi(tokens[i++]);
+ break;
+
+ case CONF_DISCARD_MINIMUM:
+ res_min_interval = atoi(tokens[i++]);
+ break;
+
+ case CONF_DISCARD_MONITOR:
+ mon_age = atoi(tokens[i++]);
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "discard: unknown keyword");
+ break;
+ }
+ }
+ break;
+
+#ifdef OPENSSL
+ case CONFIG_REVOKE:
+ if (ntokens >= 2)
+ sys_revoke = (u_char) max(atoi(tokens[1]), KEY_REVOKE);
+ break;
+
+ case CONFIG_AUTOMAX:
+ if (ntokens >= 2)
+ sys_automax = 1 << max(atoi(tokens[1]), 10);
+ break;
+
+ case CONFIG_CRYPTO:
+ if (ntokens == 1) {
+ crypto_config(CRYPTO_CONF_NONE, NULL);
+ break;
+ }
+ for (i = 1; i < ntokens; i++) {
+ int temp;
+
+ temp = matchkey(tokens[i++],
+ crypto_keywords, 1);
+ if (i > ntokens - 1) {
+ msyslog(LOG_ERR,
+ "crypto: missing argument");
+ errflg++;
+ break;
+ }
+ switch(temp) {
+
+ case CONF_CRYPTO_CERT:
+ crypto_config(CRYPTO_CONF_CERT,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_RSA:
+ crypto_config(CRYPTO_CONF_PRIV,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_IFFPAR:
+ crypto_config(CRYPTO_CONF_IFFPAR,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_GQPAR:
+ crypto_config(CRYPTO_CONF_GQPAR,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_MVPAR:
+ crypto_config(CRYPTO_CONF_MVPAR,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_LEAP:
+ crypto_config(CRYPTO_CONF_LEAP,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_PW:
+ crypto_config(CRYPTO_CONF_PW,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_RAND:
+ crypto_config(CRYPTO_CONF_RAND,
+ tokens[i]);
+ break;
+
+ case CONF_CRYPTO_SIGN:
+ crypto_config(CRYPTO_CONF_SIGN,
+ tokens[i]);
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "crypto: unknown keyword");
+ break;
+ }
+ }
+ break;
+#endif /* OPENSSL */
+
+ case CONFIG_RESTRICT:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR, "restrict requires an address");
+ break;
+ }
+ istart = 1;
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ switch (matchkey(tokens[istart], addr_type, 0)) {
+ case CONF_ADDR_IPV4:
+ peeraddr.ss_family = AF_INET;
+ istart++;
+ break;
+ case CONF_ADDR_IPV6:
+ peeraddr.ss_family = AF_INET6;
+ istart++;
+ break;
+ }
+
+ /*
+ * Assume default means an IPv4 address, except
+ * if forced by a -4 or -6.
+ */
+ if (STREQ(tokens[istart], "default")) {
+ if (peeraddr.ss_family == 0)
+ peeraddr.ss_family = AF_INET;
+ } else if (!getnetnum(tokens[istart], &peeraddr, 1))
+ break;
+
+ /*
+ * Use peerversion as flags, peerkey as mflags. Ick.
+ */
+ peerversion = 0;
+ peerkey = 0;
+ errflg = 0;
+ SET_HOSTMASK(&maskaddr, peeraddr.ss_family);
+ istart++;
+ for (i = istart; i < ntokens; i++) {
+ switch (matchkey(tokens[i], res_keywords, 1)) {
+ case CONF_RES_MASK:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "mask keyword needs argument");
+ errflg++;
+ break;
+ }
+ i++;
+ if (!getnetnum(tokens[i], &maskaddr, 1))
+ errflg++;
+ break;
+
+ case CONF_RES_IGNORE:
+ peerversion |= RES_IGNORE;
+ break;
+
+ case CONF_RES_NOSERVE:
+ peerversion |= RES_DONTSERVE;
+ break;
+
+ case CONF_RES_NOTRUST:
+ peerversion |= RES_DONTTRUST;
+ break;
+
+ case CONF_RES_NOQUERY:
+ peerversion |= RES_NOQUERY;
+ break;
+
+ case CONF_RES_NOMODIFY:
+ peerversion |= RES_NOMODIFY;
+ break;
+
+ case CONF_RES_NOPEER:
+ peerversion |= RES_NOPEER;
+ break;
+
+ case CONF_RES_NOTRAP:
+ peerversion |= RES_NOTRAP;
+ break;
+
+ case CONF_RES_LPTRAP:
+ peerversion |= RES_LPTRAP;
+ break;
+
+ case CONF_RES_NTPPORT:
+ peerkey |= RESM_NTPONLY;
+ break;
+
+ case CONF_RES_VERSION:
+ peerversion |= RES_VERSION;
+ break;
+
+ case CONF_RES_DEMOBILIZE:
+ peerversion |= RES_DEMOBILIZE;
+ break;
+
+ case CONF_RES_LIMITED:
+ peerversion |= RES_LIMITED;
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+ }
+ if (SOCKNUL(&peeraddr))
+ ANYSOCK(&maskaddr);
+ if (!errflg)
+ hack_restrict(RESTRICT_FLAGS, &peeraddr, &maskaddr,
+ (int)peerkey, peerversion);
+ break;
+
+ case CONFIG_BDELAY:
+ if (ntokens >= 2) {
+ double tmp;
+
+ if (sscanf(tokens[1], "%lf", &tmp) != 1) {
+ msyslog(LOG_ERR,
+ "broadcastdelay value %s undecodable",
+ tokens[1]);
+ } else {
+ proto_config(PROTO_BROADDELAY, 0, tmp, NULL);
+ }
+ }
+ break;
+
+ case CONFIG_CDELAY:
+ if (ntokens >= 2) {
+ u_long ui;
+
+ if (sscanf(tokens[1], "%ld", &ui) != 1)
+ msyslog(LOG_ERR,
+ "illegal value - line ignored");
+ else
+ proto_config(PROTO_CALLDELAY, ui, 0, NULL);
+ }
+ break;
+
+ case CONFIG_TRUSTEDKEY:
+ for (i = 1; i < ntokens; i++) {
+ keyid_t tkey;
+
+ tkey = atol(tokens[i]);
+ if (tkey == 0) {
+ msyslog(LOG_ERR,
+ "trusted key %s unlikely",
+ tokens[i]);
+ } else {
+ authtrust(tkey, 1);
+ }
+ }
+ break;
+
+ case CONFIG_REQUESTKEY:
+ if (ntokens >= 2) {
+ if (!atouint(tokens[1], &ul)) {
+ msyslog(LOG_ERR,
+ "%s is undecodable as request key",
+ tokens[1]);
+ } else if (ul == 0) {
+ msyslog(LOG_ERR,
+ "%s makes a poor request keyid",
+ tokens[1]);
+ } else {
+#ifdef DEBUG
+ if (debug > 3)
+ printf(
+ "set info_auth_key to %08lx\n", ul);
+#endif
+ info_auth_keyid = (keyid_t)ul;
+ }
+ }
+ break;
+
+ case CONFIG_CONTROLKEY:
+ if (ntokens >= 2) {
+ keyid_t ckey;
+
+ ckey = atol(tokens[1]);
+ if (ckey == 0) {
+ msyslog(LOG_ERR,
+ "%s makes a poor control keyid",
+ tokens[1]);
+ } else {
+ ctl_auth_keyid = ckey;
+ }
+ }
+ break;
+
+ case CONFIG_TRAP:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no address for trap command, line ignored");
+ break;
+ }
+ istart = 1;
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ switch (matchkey(tokens[istart], addr_type, 0)) {
+ case CONF_ADDR_IPV4:
+ peeraddr.ss_family = AF_INET;
+ istart++;
+ break;
+ case CONF_ADDR_IPV6:
+ peeraddr.ss_family = AF_INET6;
+ istart++;
+ break;
+ }
+
+ if (!getnetnum(tokens[istart], &peeraddr, 1))
+ break;
+
+ /*
+ * Use peerversion for port number. Barf.
+ */
+ errflg = 0;
+ peerversion = 0;
+ localaddr = 0;
+ istart++;
+ for (i = istart; i < ntokens-1; i++)
+ switch (matchkey(tokens[i], trap_keywords, 1)) {
+ case CONF_TRAP_PORT:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "trap port requires an argument");
+ errflg = 1;
+ break;
+ }
+ peerversion = atoi(tokens[++i]);
+ if (peerversion <= 0
+ || peerversion > 32767) {
+ msyslog(LOG_ERR,
+ "invalid port number %s, trap ignored",
+ tokens[i]);
+ errflg = 1;
+ }
+ break;
+
+ case CONF_TRAP_INTERFACE:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "trap interface requires an argument");
+ errflg = 1;
+ break;
+ }
+
+ memset((char *)&maskaddr, 0,
+ sizeof(maskaddr));
+ maskaddr.ss_family = peeraddr.ss_family;
+ if (!getnetnum(tokens[++i],
+ &maskaddr, 1)) {
+ errflg = 1;
+ break;
+ }
+
+ localaddr = findinterface(&maskaddr);
+ if (localaddr == NULL) {
+ msyslog(LOG_ERR,
+ "can't find interface with address %s",
+ stoa(&maskaddr));
+ errflg = 1;
+ }
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+
+ if (!errflg) {
+ if (peerversion != 0)
+ ((struct sockaddr_in6*)&peeraddr)->sin6_port = htons( (u_short) peerversion);
+ else
+ ((struct sockaddr_in6*)&peeraddr)->sin6_port = htons(TRAPPORT);
+ if (localaddr == NULL)
+ localaddr = ANY_INTERFACE_CHOOSE(&peeraddr);
+ if (!ctlsettrap(&peeraddr, localaddr, 0,
+ NTP_VERSION))
+ msyslog(LOG_ERR,
+ "can't set trap for %s, no resources",
+ stoa(&peeraddr));
+ }
+ break;
+
+ case CONFIG_FUDGE:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no address for fudge command, line ignored");
+ break;
+ }
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ if (!ISREFCLOCKADR(&peeraddr)) {
+ msyslog(LOG_ERR,
+ "%s is inappropriate address for the fudge command, line ignored",
+ stoa(&peeraddr));
+ break;
+ }
+
+ memset((void *)&clock_stat, 0, sizeof clock_stat);
+ fudgeflag = 0;
+ errflg = 0;
+ for (i = 2; i < ntokens-1; i++) {
+ switch (c = matchkey(tokens[i],
+ fudge_keywords, 1)) {
+ case CONF_FDG_TIME1:
+ if (sscanf(tokens[++i], "%lf",
+ &clock_stat.fudgetime1) != 1) {
+ msyslog(LOG_ERR,
+ "fudge %s time1 value in error",
+ stoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock_stat.haveflags |= CLK_HAVETIME1;
+ break;
+
+ case CONF_FDG_TIME2:
+ if (sscanf(tokens[++i], "%lf",
+ &clock_stat.fudgetime2) != 1) {
+ msyslog(LOG_ERR,
+ "fudge %s time2 value in error",
+ stoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock_stat.haveflags |= CLK_HAVETIME2;
+ break;
+
+
+ case CONF_FDG_STRATUM:
+ if (!atoint(tokens[++i], &stratum))
+ {
+ msyslog(LOG_ERR,
+ "fudge %s stratum value in error",
+ stoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock_stat.fudgeval1 = stratum;
+ clock_stat.haveflags |= CLK_HAVEVAL1;
+ break;
+
+ case CONF_FDG_REFID:
+ /* HMS: Endianness and 0 bytes? */
+ /* XXX */
+ strncpy((char *)&clock_stat.fudgeval2,
+ tokens[++i], 4);
+ clock_stat.haveflags |= CLK_HAVEVAL2;
+ break;
+
+ case CONF_FDG_FLAG1:
+ case CONF_FDG_FLAG2:
+ case CONF_FDG_FLAG3:
+ case CONF_FDG_FLAG4:
+ if (!atouint(tokens[++i], &fudgeflag)
+ || fudgeflag > 1) {
+ msyslog(LOG_ERR,
+ "fudge %s flag value in error",
+ stoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ switch(c) {
+ case CONF_FDG_FLAG1:
+ c = CLK_FLAG1;
+ clock_stat.haveflags|=CLK_HAVEFLAG1;
+ break;
+ case CONF_FDG_FLAG2:
+ c = CLK_FLAG2;
+ clock_stat.haveflags|=CLK_HAVEFLAG2;
+ break;
+ case CONF_FDG_FLAG3:
+ c = CLK_FLAG3;
+ clock_stat.haveflags|=CLK_HAVEFLAG3;
+ break;
+ case CONF_FDG_FLAG4:
+ c = CLK_FLAG4;
+ clock_stat.haveflags|=CLK_HAVEFLAG4;
+ break;
+ }
+ if (fudgeflag == 0)
+ clock_stat.flags &= ~c;
+ else
+ clock_stat.flags |= c;
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg = -1;
+ break;
+ }
+ }
+
+#ifdef REFCLOCK
+ /*
+ * If reference clock support isn't defined the
+ * fudge line will still be accepted and syntax
+ * checked, but will essentially do nothing.
+ */
+ if (!errflg) {
+ refclock_control(&peeraddr, &clock_stat,
+ (struct refclockstat *)0);
+ }
+#endif
+ break;
+
+ case CONFIG_STATSDIR:
+ if (ntokens >= 2)
+ stats_config(STATS_STATSDIR,tokens[1]);
+ break;
+
+ case CONFIG_STATISTICS:
+ for (i = 1; i < ntokens; i++) {
+ filegen = filegen_get(tokens[i]);
+
+ if (filegen == NULL) {
+ msyslog(LOG_ERR,
+ "no statistics named %s available",
+ tokens[i]);
+ continue;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ printf("enabling filegen for %s statistics \"%s%s\"\n",
+ tokens[i], filegen->prefix, filegen->basename);
+#endif
+ filegen->flag |= FGEN_FLAG_ENABLED;
+ }
+ break;
+
+ case CONFIG_FILEGEN:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no id for filegen command, line ignored");
+ break;
+ }
+
+ filegen = filegen_get(tokens[1]);
+ if (filegen == NULL) {
+ msyslog(LOG_ERR,
+ "unknown filegen \"%s\" ignored",
+ tokens[1]);
+ break;
+ }
+ /*
+ * peerversion is (ab)used for filegen file (index)
+ * peerkey is (ab)used for filegen type
+ * peerflags is (ab)used for filegen flags
+ */
+ peerversion = 0;
+ peerkey = filegen->type;
+ peerflags = filegen->flag;
+ errflg = 0;
+
+ for (i = 2; i < ntokens; i++) {
+ switch (matchkey(tokens[i],
+ filegen_keywords, 1)) {
+ case CONF_FGEN_FILE:
+ if (i >= ntokens - 1) {
+ msyslog(LOG_ERR,
+ "filegen %s file requires argument",
+ tokens[1]);
+ errflg = i;
+ break;
+ }
+ peerversion = ++i;
+ break;
+ case CONF_FGEN_TYPE:
+ if (i >= ntokens -1) {
+ msyslog(LOG_ERR,
+ "filegen %s type requires argument",
+ tokens[1]);
+ errflg = i;
+ break;
+ }
+ peerkey = matchkey(tokens[++i],
+ fgen_types, 1);
+ if (peerkey == CONFIG_UNKNOWN) {
+ msyslog(LOG_ERR,
+ "filegen %s unknown type \"%s\"",
+ tokens[1], tokens[i]);
+ errflg = i;
+ break;
+ }
+ break;
+
+ case CONF_FGEN_FLAG_LINK:
+ peerflags |= FGEN_FLAG_LINK;
+ break;
+
+ case CONF_FGEN_FLAG_NOLINK:
+ peerflags &= ~FGEN_FLAG_LINK;
+ break;
+
+ case CONF_FGEN_FLAG_ENABLE:
+ peerflags |= FGEN_FLAG_ENABLED;
+ break;
+
+ case CONF_FGEN_FLAG_DISABLE:
+ peerflags &= ~FGEN_FLAG_ENABLED;
+ break;
+ }
+ }
+ if (!errflg)
+ filegen_config(filegen, tokens[peerversion],
+ (u_char)peerkey, (u_char)peerflags);
+ break;
+
+ case CONFIG_SETVAR:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no value for setvar command - line ignored");
+ } else {
+ set_sys_var(tokens[1], strlen(tokens[1])+1,
+ (u_short) (RW |
+ ((((ntokens > 2)
+ && !strcmp(tokens[2],
+ "default")))
+ ? DEF
+ : 0)));
+ }
+ break;
+
+ case CONFIG_ENABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords, 1);
+ if (flag == CONFIG_UNKNOWN) {
+ msyslog(LOG_ERR,
+ "enable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 1, 0., NULL);
+ }
+ break;
+
+ case CONFIG_DISABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords, 1);
+ if (flag == CONFIG_UNKNOWN) {
+ msyslog(LOG_ERR,
+ "disable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 0, 0., NULL);
+ }
+ break;
+
+ case CONFIG_PHONE:
+ for (i = 1; i < ntokens && i < MAXPHONE; i++) {
+ (void)strncpy(sys_phone[i - 1],
+ tokens[i], MAXDIAL);
+ }
+ sys_phone[i - 1][0] = '\0';
+ break;
+
+ case CONFIG_ADJ: {
+ double ftemp;
+
+ sscanf(tokens[1], "%lf", &ftemp);
+ proto_config(PROTO_ADJ, 0, ftemp, NULL);
+ }
+ break;
+
+ }
+ }
+ if (fp[0])
+ (void)fclose(fp[0]);
+
+#ifdef HAVE_NETINFO
+ if (config_netinfo)
+ free_netinfo_config(config_netinfo);
+#endif /* HAVE_NETINFO */
+
+#if !defined(VMS) && !defined(SYS_VXWORKS)
+ /* find a keyid */
+ if (info_auth_keyid == 0)
+ req_keyid = 65535;
+ else
+ req_keyid = info_auth_keyid;
+
+ /* if doesn't exist, make up one at random */
+ if (!authhavekey(req_keyid)) {
+ char rankey[9];
+ int j;
+
+ for (i = 0; i < 8; i++)
+ for (j = 1; j < 100; ++j) {
+ rankey[i] = (char) (RANDOM & 0xff);
+ if (rankey[i] != 0) break;
+ }
+ rankey[8] = 0;
+ authusekey(req_keyid, KEY_TYPE_MD5, (u_char *)rankey);
+ authtrust(req_keyid, 1);
+ if (!authhavekey(req_keyid)) {
+ msyslog(LOG_ERR, "getconfig: Couldn't generate a valid random key!");
+ /* HMS: Should this be fatal? */
+ }
+ }
+
+ /* save keyid so we will accept config requests with it */
+ info_auth_keyid = req_keyid;
+#endif /* !defined(VMS) && !defined(SYS_VXWORKS) */
+
+ if (res_fp != NULL) {
+ if (call_resolver) {
+ /*
+ * Need name resolution
+ */
+ do_resolve_internal();
+ }
+ }
+}
+
+
+#ifdef HAVE_NETINFO
+
+/*
+ * get_netinfo_config - find the nearest NetInfo domain with an ntp
+ * configuration and initialize the configuration state.
+ */
+static struct netinfo_config_state *
+get_netinfo_config()
+{
+ ni_status status;
+ void *domain;
+ ni_id config_dir;
+ struct netinfo_config_state *config;
+
+ if (ni_open(NULL, ".", &domain) != NI_OK) return NULL;
+
+ while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) {
+ void *next_domain;
+ if (ni_open(domain, "..", &next_domain) != NI_OK) {
+ ni_free(next_domain);
+ break;
+ }
+ ni_free(domain);
+ domain = next_domain;
+ }
+ if (status != NI_OK) {
+ ni_free(domain);
+ return NULL;
+ }
+
+ config = (struct netinfo_config_state *)malloc(sizeof(struct netinfo_config_state));
+ config->domain = domain;
+ config->config_dir = config_dir;
+ config->prop_index = 0;
+ config->val_index = 0;
+ config->val_list = NULL;
+
+ return config;
+}
+
+
+
+/*
+ * free_netinfo_config - release NetInfo configuration state
+ */
+static void
+free_netinfo_config(struct netinfo_config_state *config)
+{
+ ni_free(config->domain);
+ free(config);
+}
+
+
+
+/*
+ * gettokens_netinfo - return tokens from NetInfo
+ */
+static int
+gettokens_netinfo (
+ struct netinfo_config_state *config,
+ char **tokenlist,
+ int *ntokens
+ )
+{
+ int prop_index = config->prop_index;
+ int val_index = config->val_index;
+ char **val_list = config->val_list;
+
+ /*
+ * Iterate through each keyword and look for a property that matches it.
+ */
+ again:
+ if (!val_list) {
+ for (; prop_index < (sizeof(keywords)/sizeof(keywords[0])); prop_index++)
+ {
+ ni_namelist namelist;
+ struct keyword current_prop = keywords[prop_index];
+
+ /*
+ * For each value associated in the property, we're going to return
+ * a separate line. We squirrel away the values in the config state
+ * so the next time through, we don't need to do this lookup.
+ */
+ NI_INIT(&namelist);
+ if (ni_lookupprop(config->domain, &config->config_dir, current_prop.text, &namelist) == NI_OK) {
+ ni_index index;
+
+ /* Found the property, but it has no values */
+ if (namelist.ni_namelist_len == 0) continue;
+
+ if (! (val_list = config->val_list = (char**)malloc(sizeof(char*) * (namelist.ni_namelist_len + 1))))
+ { msyslog(LOG_ERR, "out of memory while configuring"); break; }
+
+ for (index = 0; index < namelist.ni_namelist_len; index++) {
+ char *value = namelist.ni_namelist_val[index];
+
+ if (! (val_list[index] = (char*)malloc(strlen(value)+1)))
+ { msyslog(LOG_ERR, "out of memory while configuring"); break; }
+
+ strcpy(val_list[index], value);
+ }
+ val_list[index] = NULL;
+
+ break;
+ }
+ ni_namelist_free(&namelist);
+ }
+ config->prop_index = prop_index;
+ }
+
+ /* No list; we're done here. */
+ if (!val_list) return CONFIG_UNKNOWN;
+
+ /*
+ * We have a list of values for the current property.
+ * Iterate through them and return each in order.
+ */
+ if (val_list[val_index])
+ {
+ int ntok = 1;
+ int quoted = 0;
+ char *tokens = val_list[val_index];
+
+ msyslog(LOG_INFO, "%s %s", keywords[prop_index].text, val_list[val_index]);
+
+ (const char*)tokenlist[0] = keywords[prop_index].text;
+ for (ntok = 1; ntok < MAXTOKENS; ntok++) {
+ tokenlist[ntok] = tokens;
+ while (!ISEOL(*tokens) && (!ISSPACE(*tokens) || quoted))
+ quoted ^= (*tokens++ == '"');
+
+ if (ISEOL(*tokens)) {
+ *tokens = '\0';
+ break;
+ } else { /* must be space */
+ *tokens++ = '\0';
+ while (ISSPACE(*tokens)) tokens++;
+ if (ISEOL(*tokens)) break;
+ }
+ }
+ *ntokens = ntok + 1;
+
+ config->val_index++;
+
+ return keywords[prop_index].keytype;
+ }
+
+ /* We're done with the current property. */
+ prop_index = ++config->prop_index;
+
+ /* Free val_list and reset counters. */
+ for (val_index = 0; val_list[val_index]; val_index++)
+ free(val_list[val_index]);
+ free(val_list); val_list = config->val_list = NULL; val_index = config->val_index = 0;
+
+ goto again;
+}
+
+#endif /* HAVE_NETINFO */
+
+
+/*
+ * gettokens - read a line and return tokens
+ */
+static int
+gettokens (
+ FILE *fp,
+ char *line,
+ char **tokenlist,
+ int *ntokens
+ )
+{
+ register char *cp;
+ register int ntok;
+ register int quoted = 0;
+
+ /*
+ * Find start of first token
+ */
+ again:
+ while ((cp = fgets(line, MAXLINE, fp)) != NULL) {
+ cp = line;
+ while (ISSPACE(*cp))
+ cp++;
+ if (!ISEOL(*cp))
+ break;
+ }
+ if (cp == NULL) {
+ *ntokens = 0;
+ return CONFIG_UNKNOWN; /* hack. Is recognized as EOF */
+ }
+
+ /*
+ * Now separate out the tokens
+ */
+ for (ntok = 0; ntok < MAXTOKENS; ntok++) {
+ tokenlist[ntok] = cp;
+ while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted))
+ quoted ^= (*cp++ == '"');
+
+ if (ISEOL(*cp)) {
+ *cp = '\0';
+ break;
+ } else { /* must be space */
+ *cp++ = '\0';
+ while (ISSPACE(*cp))
+ cp++;
+ if (ISEOL(*cp))
+ break;
+ }
+ }
+
+ /*
+ * Return the match
+ */
+ *ntokens = ntok + 1;
+ ntok = matchkey(tokenlist[0], keywords, 1);
+ if (ntok == CONFIG_UNKNOWN)
+ goto again;
+ return ntok;
+}
+
+
+
+/*
+ * matchkey - match a keyword to a list
+ */
+static int
+matchkey(
+ register char *word,
+ register struct keyword *keys,
+ int complain
+ )
+{
+ for (;;) {
+ if (keys->keytype == CONFIG_UNKNOWN) {
+ if (complain)
+ msyslog(LOG_ERR,
+ "configure: keyword \"%s\" unknown, line ignored",
+ word);
+ return CONFIG_UNKNOWN;
+ }
+ if (STRSAME(word, keys->text))
+ return keys->keytype;
+ keys++;
+ }
+}
+
+
+/*
+ * getnetnum - return a net number (this is crude, but careful)
+ */
+static int
+getnetnum(
+ const char *num,
+ struct sockaddr_storage *addr,
+ int complain
+ )
+{
+ struct addrinfo hints;
+ struct addrinfo *ptr;
+
+ /* Get host address. Looking for UDP datagram connection */
+ memset(&hints, 0, sizeof (hints));
+ if (addr->ss_family == AF_INET || addr->ss_family == AF_INET6)
+ hints.ai_family = addr->ss_family;
+ else
+ hints.ai_family = AF_UNSPEC;
+
+ hints.ai_socktype = SOCK_DGRAM;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("getaddrinfo %s\n", num);
+#endif
+ if (getaddrinfo(num, "ntp", &hints, &ptr)!=0) {
+ if (complain)
+ msyslog(LOG_ERR,
+ "getaddrinfo: \"%s\" invalid host address, line ignored",
+ num);
+#ifdef DEBUG
+ if (debug > 3)
+ printf(
+ "getaddrinfo: \"%s\" invalid host address%s.\n",
+ num, (complain)
+ ? ", line ignored"
+ : "");
+#endif
+ return 0;
+ }
+
+ memcpy(addr, ptr->ai_addr, ptr->ai_addrlen);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("getnetnum given %s, got %s \n",
+ num, stoa(addr));
+#endif
+ freeaddrinfo(ptr);
+ return 1;
+}
+
+
+#if !defined(VMS) && !defined(SYS_WINNT)
+/*
+ * catchchild - receive the resolver's exit status
+ */
+static RETSIGTYPE
+catchchild(
+ int sig
+ )
+{
+ /*
+ * We only start up one child, and if we're here
+ * it should have already exited. Hence the following
+ * shouldn't hang. If it does, please tell me.
+ */
+#if !defined (SYS_WINNT) && !defined(SYS_VXWORKS)
+ (void) wait(0);
+#endif /* SYS_WINNT && VXWORKS*/
+}
+#endif /* VMS */
+
+
+/*
+ * save_resolve - save configuration info into a file for later name resolution
+ */
+static void
+save_resolve(
+ char *name,
+ int mode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ u_int flags,
+ int ttl,
+ keyid_t keyid,
+ u_char *keystr
+ )
+{
+#ifndef SYS_VXWORKS
+ if (res_fp == NULL) {
+#ifndef SYS_WINNT
+ (void) strcpy(res_file, RES_TEMPFILE);
+#else
+ /* no /tmp directory under NT */
+ {
+ if(!(GetTempPath((DWORD)MAX_PATH, (LPTSTR)res_file))) {
+ msyslog(LOG_ERR, "cannot get pathname for temporary directory: %m");
+ return;
+ }
+ (void) strcat(res_file, "ntpdXXXXXX");
+ }
+#endif /* SYS_WINNT */
+#ifdef HAVE_MKSTEMP
+ {
+ int fd;
+
+ res_fp = NULL;
+ if ((fd = mkstemp(res_file)) != -1)
+ res_fp = fdopen(fd, "r+");
+ }
+#else
+ (void) mktemp(res_file);
+ res_fp = fopen(res_file, "w");
+#endif
+ if (res_fp == NULL) {
+ msyslog(LOG_ERR, "open failed for %s: %m", res_file);
+ return;
+ }
+ }
+#ifdef DEBUG
+ if (debug) {
+ printf("resolving %s\n", name);
+ }
+#endif
+
+ (void)fprintf(res_fp, "%s %d %d %d %d %d %d %u %s\n", name,
+ mode, version, minpoll, maxpoll, flags, ttl, keyid, keystr);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("config: %s %d %d %d %d %x %d %u %s\n", name, mode,
+ version, minpoll, maxpoll, flags, ttl, keyid, keystr);
+#endif
+
+#else /* SYS_VXWORKS */
+ /* save resolve info to a struct */
+#endif /* SYS_VXWORKS */
+}
+
+
+/*
+ * abort_resolve - terminate the resolver stuff and delete the file
+ */
+static void
+abort_resolve(void)
+{
+ /*
+ * In an ideal world we would might reread the file and
+ * log the hosts which aren't getting configured. Since
+ * this is too much work, however, just close and delete
+ * the temp file.
+ */
+ if (res_fp != NULL)
+ (void) fclose(res_fp);
+ res_fp = NULL;
+
+#ifndef SYS_VXWORKS /* we don't open the file to begin with */
+#if !defined(VMS)
+ (void) unlink(res_file);
+#else
+ (void) delete(res_file);
+#endif /* VMS */
+#endif /* SYS_VXWORKS */
+}
+
+
+/*
+ * do_resolve_internal - start up the resolver function (not program)
+ */
+/*
+ * On VMS, this routine will simply refuse to resolve anything.
+ *
+ * Possible implementation: keep `res_file' in memory, do async
+ * name resolution via QIO, update from within completion AST.
+ * I'm unlikely to find the time for doing this, though. -wjm
+ */
+static void
+do_resolve_internal(void)
+{
+ int i;
+
+ if (res_fp == NULL) {
+ /* belch */
+ msyslog(LOG_ERR,
+ "do_resolve_internal: Fatal: res_fp == NULL");
+ exit(1);
+ }
+
+ /* we are done with this now */
+ (void) fclose(res_fp);
+ res_fp = NULL;
+
+#if !defined(VMS) && !defined (SYS_VXWORKS)
+ req_file = res_file; /* set up pointer to res file */
+#ifndef SYS_WINNT
+ (void) signal_no_reset(SIGCHLD, catchchild);
+
+#ifndef SYS_VXWORKS
+ i = fork();
+ if (i == 0) {
+ /*
+ * this used to close everything
+ * I don't think this is necessary
+ */
+ /*
+ * To the unknown commenter above:
+ * Well, I think it's better to clean up
+ * after oneself. I have had problems with
+ * refclock-io when intres was running - things
+ * where fine again when ntpintres was gone.
+ * So some systems react erratic at least.
+ *
+ * Frank Kardel
+ *
+ * 94-11-16:
+ * Further debugging has proven that the above is
+ * absolutely harmful. The internal resolver
+ * is still in the SIGIO process group and the lingering
+ * async io information causes it to process requests from
+ * all file decriptor causing a race between the NTP daemon
+ * and the resolver. which then eats data when it wins 8-(.
+ * It is absolutly necessary to kill any IO associations
+ * shared with the NTP daemon.
+ *
+ * We also block SIGIO (currently no ports means to
+ * disable the signal handle for IO).
+ *
+ * Thanks to wgstuken@informatik.uni-erlangen.de to notice
+ * that it is the ntp-resolver child running into trouble.
+ *
+ * THUS:
+ */
+
+ closelog();
+ kill_asyncio(0);
+
+ (void) signal_no_reset(SIGCHLD, SIG_DFL);
+
+#ifdef DEBUG
+ if (0)
+ debug = 2;
+#endif
+
+# ifndef LOG_DAEMON
+ openlog("ntpd_initres", LOG_PID);
+# else /* LOG_DAEMON */
+
+# ifndef LOG_NTP
+# define LOG_NTP LOG_DAEMON
+# endif
+ openlog("ntpd_initres", LOG_PID | LOG_NDELAY, LOG_NTP);
+#ifndef SYS_CYGWIN32
+# ifdef DEBUG
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ else
+# endif /* DEBUG */
+ setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
+# endif /* LOG_DAEMON */
+#endif
+
+ ntp_intres();
+
+ /*
+ * If we got here, the intres code screwed up.
+ * Print something so we don't die without complaint
+ */
+ msyslog(LOG_ERR, "call to ntp_intres lost");
+ abort_resolve();
+ exit(1);
+ }
+#else
+ /* vxWorks spawns a thread... -casey */
+ i = sp (ntp_intres);
+ /*i = taskSpawn("ntp_intres",100,VX_FP_TASK,20000,ntp_intres);*/
+#endif
+ if (i == -1) {
+ msyslog(LOG_ERR, "fork() failed, can't start ntp_intres: %m");
+ (void) signal_no_reset(SIGCHLD, SIG_DFL);
+ abort_resolve();
+ }
+#else /* SYS_WINNT */
+ {
+ /* NT's equivalent of fork() is _spawn(), but the start point
+ * of the new process is an executable filename rather than
+ * a function name as desired here.
+ */
+ DWORD dwThreadId;
+ fflush(stdout);
+ ResolverThreadHandle = CreateThread(
+ NULL, /* no security attributes */
+ 0, /* use default stack size */
+ (LPTHREAD_START_ROUTINE) ntp_intres, /* thread function */
+ NULL, /* argument to thread function */
+ 0, /* use default creation flags */
+ &dwThreadId); /* returns the thread identifier */
+ if (ResolverThreadHandle == NULL) {
+ msyslog(LOG_ERR, "CreateThread() failed, can't start ntp_intres");
+ abort_resolve();
+ }
+ }
+#endif /* SYS_WINNT */
+#else /* VMS VX_WORKS */
+ msyslog(LOG_ERR,
+ "Name resolution not implemented for VMS - use numeric addresses");
+ abort_resolve();
+#endif /* VMS VX_WORKS */
+}
diff --git a/ntpd/ntp_control.c b/ntpd/ntp_control.c
new file mode 100644
index 0000000..0ac0404
--- /dev/null
+++ b/ntpd/ntp_control.c
@@ -0,0 +1,2928 @@
+/*
+ * ntp_control.c - respond to control messages and send async traps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_control.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+
+struct ctl_proc {
+ short control_code; /* defined request code */
+ u_short flags; /* flags word */
+ void (*handler) P((struct recvbuf *, int)); /* handle request */
+};
+
+/*
+ * Only one flag. Authentication required or not.
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+/*
+ * Request processing routines
+ */
+static void ctl_error P((int));
+#ifdef REFCLOCK
+static u_short ctlclkstatus P((struct refclockstat *));
+#endif
+static void ctl_flushpkt P((int));
+static void ctl_putdata P((const char *, unsigned int, int));
+static void ctl_putstr P((const char *, const char *,
+ unsigned int));
+static void ctl_putdbl P((const char *, double));
+static void ctl_putuint P((const char *, u_long));
+static void ctl_puthex P((const char *, u_long));
+static void ctl_putint P((const char *, long));
+static void ctl_putts P((const char *, l_fp *));
+static void ctl_putadr P((const char *, u_int32, struct sockaddr_storage*));
+static void ctl_putid P((const char *, char *));
+static void ctl_putarray P((const char *, double *, int));
+static void ctl_putsys P((int));
+static void ctl_putpeer P((int, struct peer *));
+#ifdef REFCLOCK
+static void ctl_putclock P((int, struct refclockstat *, int));
+#endif /* REFCLOCK */
+static struct ctl_var *ctl_getitem P((struct ctl_var *, char **));
+static u_long count_var P((struct ctl_var *));
+static void control_unspec P((struct recvbuf *, int));
+static void read_status P((struct recvbuf *, int));
+static void read_variables P((struct recvbuf *, int));
+static void write_variables P((struct recvbuf *, int));
+static void read_clock_status P((struct recvbuf *, int));
+static void write_clock_status P((struct recvbuf *, int));
+static void set_trap P((struct recvbuf *, int));
+static void unset_trap P((struct recvbuf *, int));
+static struct ctl_trap *ctlfindtrap P((struct sockaddr_storage *,
+ struct interface *));
+
+static struct ctl_proc control_codes[] = {
+ { CTL_OP_UNSPEC, NOAUTH, control_unspec },
+ { CTL_OP_READSTAT, NOAUTH, read_status },
+ { CTL_OP_READVAR, NOAUTH, read_variables },
+ { CTL_OP_WRITEVAR, AUTH, write_variables },
+ { CTL_OP_READCLOCK, NOAUTH, read_clock_status },
+ { CTL_OP_WRITECLOCK, NOAUTH, write_clock_status },
+ { CTL_OP_SETTRAP, NOAUTH, set_trap },
+ { CTL_OP_UNSETTRAP, NOAUTH, unset_trap },
+ { NO_REQUEST, 0 }
+};
+
+/*
+ * System variable values. The array can be indexed by the variable
+ * index to find the textual name.
+ */
+static struct ctl_var sys_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CS_LEAP, RW, "leap" }, /* 1 */
+ { CS_STRATUM, RO, "stratum" }, /* 2 */
+ { CS_PRECISION, RO, "precision" }, /* 3 */
+ { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */
+ { CS_ROOTDISPERSION, RO, "rootdispersion" }, /* 5 */
+ { CS_REFID, RO, "refid" }, /* 6 */
+ { CS_REFTIME, RO, "reftime" }, /* 7 */
+ { CS_POLL, RO, "poll" }, /* 8 */
+ { CS_PEERID, RO, "peer" }, /* 9 */
+ { CS_STATE, RO, "state" }, /* 10 */
+ { CS_OFFSET, RO, "offset" }, /* 11 */
+ { CS_DRIFT, RO, "frequency" }, /* 12 */
+ { CS_JITTER, RO, "jitter" }, /* 13 */
+ { CS_CLOCK, RO, "clock" }, /* 14 */
+ { CS_PROCESSOR, RO, "processor" }, /* 15 */
+ { CS_SYSTEM, RO, "system" }, /* 16 */
+ { CS_VERSION, RO, "version" }, /* 17 */
+ { CS_STABIL, RO, "stability" }, /* 18 */
+ { CS_VARLIST, RO, "sys_var_list" }, /* 19 */
+#ifdef OPENSSL
+ { CS_FLAGS, RO, "flags" }, /* 20 */
+ { CS_HOST, RO, "hostname" }, /* 21 */
+ { CS_PUBLIC, RO, "hostkey" }, /* 22 */
+ { CS_CERTIF, RO, "cert" }, /* 23 */
+ { CS_REVTIME, RO, "refresh" }, /* 24 */
+ { CS_LEAPTAB, RO, "leapseconds" }, /* 25 */
+ { CS_TAI, RO, "tai" }, /* 26 */
+ { CS_DIGEST, RO, "signature" }, /* 27 */
+#endif /* OPENSSL */
+ { 0, EOV, "" } /* 28 */
+};
+
+static struct ctl_var *ext_sys_var = (struct ctl_var *)0;
+
+/*
+ * System variables we print by default (in fuzzball order,
+ * more-or-less)
+ */
+static u_char def_sys_var[] = {
+ CS_VERSION,
+ CS_PROCESSOR,
+ CS_SYSTEM,
+ CS_LEAP,
+ CS_STRATUM,
+ CS_PRECISION,
+ CS_ROOTDELAY,
+ CS_ROOTDISPERSION,
+ CS_PEERID,
+ CS_REFID,
+ CS_REFTIME,
+ CS_POLL,
+ CS_CLOCK,
+ CS_STATE,
+ CS_OFFSET,
+ CS_DRIFT,
+ CS_JITTER,
+ CS_STABIL,
+#ifdef OPENSSL
+ CS_HOST,
+ CS_DIGEST,
+ CS_FLAGS,
+ CS_PUBLIC,
+ CS_REVTIME,
+ CS_LEAPTAB,
+ CS_CERTIF,
+#endif /* OPENSSL */
+ 0
+};
+
+
+/*
+ * Peer variable list
+ */
+static struct ctl_var peer_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CP_CONFIG, RO, "config" }, /* 1 */
+ { CP_AUTHENABLE, RO, "authenable" }, /* 2 */
+ { CP_AUTHENTIC, RO, "authentic" }, /* 3 */
+ { CP_SRCADR, RO, "srcadr" }, /* 4 */
+ { CP_SRCPORT, RO, "srcport" }, /* 5 */
+ { CP_DSTADR, RO, "dstadr" }, /* 6 */
+ { CP_DSTPORT, RO, "dstport" }, /* 7 */
+ { CP_LEAP, RO, "leap" }, /* 8 */
+ { CP_HMODE, RO, "hmode" }, /* 9 */
+ { CP_STRATUM, RO, "stratum" }, /* 10 */
+ { CP_PPOLL, RO, "ppoll" }, /* 11 */
+ { CP_HPOLL, RO, "hpoll" }, /* 12 */
+ { CP_PRECISION, RO, "precision" }, /* 13 */
+ { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */
+ { CP_ROOTDISPERSION, RO, "rootdispersion" }, /* 15 */
+ { CP_REFID, RO, "refid" }, /* 16 */
+ { CP_REFTIME, RO, "reftime" }, /* 17 */
+ { CP_ORG, RO, "org" }, /* 18 */
+ { CP_REC, RO, "rec" }, /* 19 */
+ { CP_XMT, RO, "xmt" }, /* 20 */
+ { CP_REACH, RO, "reach" }, /* 21 */
+ { CP_VALID, RO, "unreach" }, /* 22 */
+ { CP_TIMER, RO, "timer" }, /* 23 */
+ { CP_DELAY, RO, "delay" }, /* 24 */
+ { CP_OFFSET, RO, "offset" }, /* 25 */
+ { CP_JITTER, RO, "jitter" }, /* 26 */
+ { CP_DISPERSION, RO, "dispersion" }, /* 27 */
+ { CP_KEYID, RO, "keyid" }, /* 28 */
+ { CP_FILTDELAY, RO, "filtdelay=" }, /* 29 */
+ { CP_FILTOFFSET, RO, "filtoffset=" }, /* 30 */
+ { CP_PMODE, RO, "pmode" }, /* 31 */
+ { CP_RECEIVED, RO, "received"}, /* 32 */
+ { CP_SENT, RO, "sent" }, /* 33 */
+ { CP_FILTERROR, RO, "filtdisp=" }, /* 34 */
+ { CP_FLASH, RO, "flash" }, /* 35 */
+ { CP_TTL, RO, "ttl" }, /* 36 */
+ { CP_RANK, RO, "rank" }, /* 37 */
+ { CP_VARLIST, RO, "peer_var_list" }, /* 38 */
+#ifdef OPENSSL
+ { CP_FLAGS, RO, "flags" }, /* 39 */
+ { CP_HOST, RO, "hostname" }, /* 40 */
+ { CP_INITSEQ, RO, "initsequence" }, /* 41 */
+ { CP_INITKEY, RO, "initkey" }, /* 42 */
+ { CP_INITTSP, RO, "timestamp" }, /* 43 */
+ { CP_DIGEST, RO, "signature" }, /* 44 */
+ { CP_IDENT, RO, "identity" }, /* 45 */
+#endif /* OPENSSL */
+ { 0, EOV, "" } /* 39/46 */
+};
+
+
+/*
+ * Peer variables we print by default
+ */
+static u_char def_peer_var[] = {
+ CP_SRCADR,
+ CP_SRCPORT,
+ CP_DSTADR,
+ CP_DSTPORT,
+ CP_LEAP,
+ CP_STRATUM,
+ CP_PRECISION,
+ CP_ROOTDELAY,
+ CP_ROOTDISPERSION,
+ CP_REFID,
+ CP_REACH,
+ CP_VALID,
+ CP_HMODE,
+ CP_PMODE,
+ CP_HPOLL,
+ CP_PPOLL,
+ CP_FLASH,
+ CP_KEYID,
+ CP_TTL,
+ CP_OFFSET,
+ CP_DELAY,
+ CP_DISPERSION,
+ CP_JITTER,
+ CP_REFTIME,
+ CP_ORG,
+ CP_REC,
+ CP_XMT,
+ CP_FILTDELAY,
+ CP_FILTOFFSET,
+ CP_FILTERROR,
+#ifdef OPENSSL
+ CP_HOST,
+ CP_DIGEST,
+ CP_FLAGS,
+ CP_IDENT,
+ CP_INITSEQ,
+#endif /* OPENSSL */
+ 0
+};
+
+
+#ifdef REFCLOCK
+/*
+ * Clock variable list
+ */
+static struct ctl_var clock_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CC_TYPE, RO, "type" }, /* 1 */
+ { CC_TIMECODE, RO, "timecode" }, /* 2 */
+ { CC_POLL, RO, "poll" }, /* 3 */
+ { CC_NOREPLY, RO, "noreply" }, /* 4 */
+ { CC_BADFORMAT, RO, "badformat" }, /* 5 */
+ { CC_BADDATA, RO, "baddata" }, /* 6 */
+ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */
+ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */
+ { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */
+ { CC_FUDGEVAL2, RO, "refid" }, /* 10 */
+ { CC_FLAGS, RO, "flags" }, /* 11 */
+ { CC_DEVICE, RO, "device" }, /* 12 */
+ { CC_VARLIST, RO, "clock_var_list" }, /* 13 */
+ { 0, EOV, "" } /* 14 */
+};
+
+
+/*
+ * Clock variables printed by default
+ */
+static u_char def_clock_var[] = {
+ CC_DEVICE,
+ CC_TYPE, /* won't be output if device = known */
+ CC_TIMECODE,
+ CC_POLL,
+ CC_NOREPLY,
+ CC_BADFORMAT,
+ CC_BADDATA,
+ CC_FUDGETIME1,
+ CC_FUDGETIME2,
+ CC_FUDGEVAL1,
+ CC_FUDGEVAL2,
+ CC_FLAGS,
+ 0
+};
+#endif
+
+
+/*
+ * System and processor definitions.
+ */
+#ifndef HAVE_UNAME
+# ifndef STR_SYSTEM
+# define STR_SYSTEM "UNIX"
+# endif
+# ifndef STR_PROCESSOR
+# define STR_PROCESSOR "unknown"
+# endif
+
+static char str_system[] = STR_SYSTEM;
+static char str_processor[] = STR_PROCESSOR;
+#else
+# include <sys/utsname.h>
+static struct utsname utsnamebuf;
+#endif /* HAVE_UNAME */
+
+/*
+ * Trap structures. We only allow a few of these, and send a copy of
+ * each async message to each live one. Traps time out after an hour, it
+ * is up to the trap receipient to keep resetting it to avoid being
+ * timed out.
+ */
+/* ntp_request.c */
+struct ctl_trap ctl_trap[CTL_MAXTRAPS];
+int num_ctl_traps;
+
+/*
+ * Type bits, for ctlsettrap() call.
+ */
+#define TRAP_TYPE_CONFIG 0 /* used by configuration code */
+#define TRAP_TYPE_PRIO 1 /* priority trap */
+#define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */
+
+
+/*
+ * List relating reference clock types to control message time sources.
+ * Index by the reference clock type. This list will only be used iff
+ * the reference clock driver doesn't set peer->sstclktype to something
+ * different than CTL_SST_TS_UNSPEC.
+ */
+static u_char clocktypes[] = {
+ CTL_SST_TS_NTP, /* REFCLK_NONE (0) */
+ CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_TRAK (2) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */
+ CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */
+ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (5) */
+ CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK (6) */
+ CTL_SST_TS_HF, /* REFCLK_CHU (7) */
+ CTL_SST_TS_LF, /* REFCLOCK_PARSE (default) (8) */
+ CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_ARBITER (11) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */
+ CTL_SST_TS_LF, /* REFCLK_MSF_EES (14) */
+ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (15) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACTS (18) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_VME (21) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_PPS (22) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_PTB_ACTS (23) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_USNO (24) */
+ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (25) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_HP (26) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_ARCRON_MSF (27) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_SHM (28) */
+ CTL_SST_TS_UHF, /* REFCLK_PALISADE (29) */
+ CTL_SST_TS_UHF, /* REFCLK_ONCORE (30) */
+ CTL_SST_TS_UHF, /* REFCLK_JUPITER (31) */
+ CTL_SST_TS_LF, /* REFCLK_CHRONOLOG (32) */
+ CTL_SST_TS_LF, /* REFCLK_DUMBCLOCK (32) */
+ CTL_SST_TS_LF, /* REFCLK_ULINK (33) */
+ CTL_SST_TS_LF, /* REFCLK_PCF (35) */
+ CTL_SST_TS_LF, /* REFCLK_WWV (36) */
+ CTL_SST_TS_LF, /* REFCLK_FG (37) */
+ CTL_SST_TS_UHF, /* REFCLK_HOPF_SERIAL (38) */
+ CTL_SST_TS_UHF, /* REFCLK_HOPF_PCI (39) */
+ CTL_SST_TS_LF, /* REFCLK_JJY (40) */
+ CTL_SST_TS_UHF, /* REFCLK_TT560 (41) */
+ CTL_SST_TS_UHF, /* REFCLK_ZYFER (42) */
+ CTL_SST_TS_UHF, /* REFCLK_RIPENCC (43) */
+ CTL_SST_TS_UHF, /* REFCLK_NEOCLOCK4X (44) */
+};
+
+
+/*
+ * Keyid used for authenticating write requests.
+ */
+keyid_t ctl_auth_keyid;
+
+/*
+ * We keep track of the last error reported by the system internally
+ */
+static u_char ctl_sys_last_event;
+static u_char ctl_sys_num_events;
+
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long ctltimereset; /* time stats reset */
+u_long numctlreq; /* number of requests we've received */
+u_long numctlbadpkts; /* number of bad control packets */
+u_long numctlresponses; /* number of resp packets sent with data */
+u_long numctlfrags; /* number of fragments sent */
+u_long numctlerrors; /* number of error responses sent */
+u_long numctltooshort; /* number of too short input packets */
+u_long numctlinputresp; /* number of responses on input */
+u_long numctlinputfrag; /* number of fragments on input */
+u_long numctlinputerr; /* number of input pkts with err bit set */
+u_long numctlbadoffset; /* number of input pkts with nonzero offset */
+u_long numctlbadversion; /* number of input pkts with unknown version */
+u_long numctldatatooshort; /* data too short for count */
+u_long numctlbadop; /* bad op code found in packet */
+u_long numasyncmsgs; /* number of async messages we've sent */
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct ntp_control rpkt;
+static u_char res_version;
+static u_char res_opcode;
+static associd_t res_associd;
+static int res_offset;
+static u_char * datapt;
+static u_char * dataend;
+static int datalinelen;
+static int datanotbinflag;
+static struct sockaddr_storage *rmt_addr;
+static struct interface *lcl_inter;
+
+static u_char res_authenticate;
+static u_char res_authokay;
+static keyid_t res_keyid;
+
+#define MAXDATALINELEN (72)
+
+static u_char res_async; /* set to 1 if this is async trap response */
+
+/*
+ * Pointers for saving state when decoding request packets
+ */
+static char *reqpt;
+static char *reqend;
+
+/*
+ * init_control - initialize request data
+ */
+void
+init_control(void)
+{
+ int i;
+
+#ifdef HAVE_UNAME
+ uname(&utsnamebuf);
+#endif /* HAVE_UNAME */
+
+ ctl_clr_stats();
+
+ ctl_auth_keyid = 0;
+ ctl_sys_last_event = EVNT_UNSPEC;
+ ctl_sys_num_events = 0;
+
+ num_ctl_traps = 0;
+ for (i = 0; i < CTL_MAXTRAPS; i++)
+ ctl_trap[i].tr_flags = 0;
+}
+
+
+/*
+ * ctl_error - send an error response for the current request
+ */
+static void
+ctl_error(
+ int errcode
+ )
+{
+#ifdef DEBUG
+ if (debug >= 4)
+ printf("sending control error %d\n", errcode);
+#endif
+ /*
+ * Fill in the fields. We assume rpkt.sequence and rpkt.associd
+ * have already been filled in.
+ */
+ rpkt.r_m_e_op = (u_char) (CTL_RESPONSE|CTL_ERROR|(res_opcode &
+ CTL_OP_MASK));
+ rpkt.status = htons((u_short) ((errcode<<8) & 0xff00));
+ rpkt.count = 0;
+
+ /*
+ * send packet and bump counters
+ */
+ if (res_authenticate && sys_authenticate) {
+ int maclen;
+
+ *(u_int32 *)((u_char *)&rpkt + CTL_HEADER_LEN) =
+ htonl(res_keyid);
+ maclen = authencrypt(res_keyid, (u_int32 *)&rpkt,
+ CTL_HEADER_LEN);
+ sendpkt(rmt_addr, lcl_inter, -2, (struct pkt *)&rpkt,
+ CTL_HEADER_LEN + maclen);
+ } else {
+ sendpkt(rmt_addr, lcl_inter, -3, (struct pkt *)&rpkt,
+ CTL_HEADER_LEN);
+ }
+ numctlerrors++;
+}
+
+
+/*
+ * process_control - process an incoming control message
+ */
+void
+process_control(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ register struct ntp_control *pkt;
+ register int req_count;
+ register int req_data;
+ register struct ctl_proc *cc;
+ int properlen;
+ int maclen;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("in process_control()\n");
+#endif
+
+ /*
+ * Save the addresses for error responses
+ */
+ numctlreq++;
+ rmt_addr = &rbufp->recv_srcadr;
+ lcl_inter = rbufp->dstadr;
+ pkt = (struct ntp_control *)&rbufp->recv_pkt;
+
+ /*
+ * If the length is less than required for the header, or
+ * it is a response or a fragment, ignore this.
+ */
+ if (rbufp->recv_length < CTL_HEADER_LEN
+ || pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR)
+ || pkt->offset != 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("invalid format in control packet\n");
+#endif
+ if (rbufp->recv_length < CTL_HEADER_LEN)
+ numctltooshort++;
+ if (pkt->r_m_e_op & CTL_RESPONSE)
+ numctlinputresp++;
+ if (pkt->r_m_e_op & CTL_MORE)
+ numctlinputfrag++;
+ if (pkt->r_m_e_op & CTL_ERROR)
+ numctlinputerr++;
+ if (pkt->offset != 0)
+ numctlbadoffset++;
+ return;
+ }
+ res_version = PKT_VERSION(pkt->li_vn_mode);
+ if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) {
+#ifdef DEBUG
+ if (debug)
+ printf("unknown version %d in control packet\n",
+ res_version);
+#endif
+ numctlbadversion++;
+ return;
+ }
+
+ /*
+ * Pull enough data from the packet to make intelligent
+ * responses
+ */
+ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version,
+ MODE_CONTROL);
+ res_opcode = pkt->r_m_e_op;
+ rpkt.sequence = pkt->sequence;
+ rpkt.associd = pkt->associd;
+ rpkt.status = 0;
+ res_offset = 0;
+ res_associd = htons(pkt->associd);
+ res_async = 0;
+ res_authenticate = 0;
+ res_keyid = 0;
+ res_authokay = 0;
+ req_count = (int)htons(pkt->count);
+ datanotbinflag = 0;
+ datalinelen = 0;
+ datapt = rpkt.data;
+ dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
+
+ /*
+ * We're set up now. Make sure we've got at least enough
+ * incoming data space to match the count.
+ */
+ req_data = rbufp->recv_length - CTL_HEADER_LEN;
+ if (req_data < req_count || rbufp->recv_length & 0x3) {
+ ctl_error(CERR_BADFMT);
+ numctldatatooshort++;
+ return;
+ }
+
+ properlen = req_count + CTL_HEADER_LEN;
+#ifdef DEBUG
+ if (debug > 2 && (rbufp->recv_length & 0x3) != 0)
+ printf("Packet length %d unrounded\n",
+ rbufp->recv_length);
+#endif
+ /* round up proper len to a 8 octet boundary */
+
+ properlen = (properlen + 7) & ~7;
+ maclen = rbufp->recv_length - properlen;
+ if ((rbufp->recv_length & (sizeof(u_long) - 1)) == 0 &&
+ maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN &&
+ sys_authenticate) {
+ res_authenticate = 1;
+ res_keyid = ntohl(*(u_int32 *)((u_char *)pkt +
+ properlen));
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf(
+ "recv_len %d, properlen %d, wants auth with keyid %08x, MAC length=%d\n",
+ rbufp->recv_length, properlen, res_keyid, maclen);
+#endif
+ if (!authistrusted(res_keyid)) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("invalid keyid %08x\n",
+ res_keyid);
+#endif
+ } else if (authdecrypt(res_keyid, (u_int32 *)pkt,
+ rbufp->recv_length - maclen, maclen)) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("authenticated okay\n");
+#endif
+ res_authokay = 1;
+ } else {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("authentication failed\n");
+#endif
+ res_keyid = 0;
+ }
+ }
+
+ /*
+ * Set up translate pointers
+ */
+ reqpt = (char *)pkt->data;
+ reqend = reqpt + req_count;
+
+ /*
+ * Look for the opcode processor
+ */
+ for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) {
+ if (cc->control_code == res_opcode) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("opcode %d, found command handler\n",
+ res_opcode);
+#endif
+ if (cc->flags == AUTH && (!res_authokay ||
+ res_keyid != ctl_auth_keyid)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ (cc->handler)(rbufp, restrict_mask);
+ return;
+ }
+ }
+
+ /*
+ * Can't find this one, return an error.
+ */
+ numctlbadop++;
+ ctl_error(CERR_BADOP);
+ return;
+}
+
+
+/*
+ * ctlpeerstatus - return a status word for this peer
+ */
+u_short
+ctlpeerstatus(
+ register struct peer *peer
+ )
+{
+ register u_short status;
+
+ status = peer->status;
+ if (peer->flags & FLAG_CONFIG)
+ status |= CTL_PST_CONFIG;
+ if (peer->flags & FLAG_AUTHENABLE)
+ status |= CTL_PST_AUTHENABLE;
+ if (peer->flags & FLAG_AUTHENTIC)
+ status |= CTL_PST_AUTHENTIC;
+ if (peer->reach != 0)
+ status |= CTL_PST_REACH;
+ return (u_short)CTL_PEER_STATUS(status, peer->num_events,
+ peer->last_event);
+}
+
+
+/*
+ * ctlclkstatus - return a status word for this clock
+ */
+#ifdef REFCLOCK
+static u_short
+ctlclkstatus(
+ struct refclockstat *this_clock
+ )
+{
+ return ((u_short)(((this_clock->currentstatus) << 8) |
+ (this_clock->lastevent)));
+}
+#endif
+
+
+/*
+ * ctlsysstatus - return the system status word
+ */
+u_short
+ctlsysstatus(void)
+{
+ register u_char this_clock;
+
+ this_clock = CTL_SST_TS_UNSPEC;
+ if (sys_peer != 0) {
+ if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) {
+ this_clock = sys_peer->sstclktype;
+ if (pps_control)
+ this_clock |= CTL_SST_TS_PPS;
+ } else {
+ if (sys_peer->refclktype < sizeof(clocktypes))
+ this_clock =
+ clocktypes[sys_peer->refclktype];
+ if (pps_control)
+ this_clock |= CTL_SST_TS_PPS;
+ }
+ }
+ return (u_short)CTL_SYS_STATUS(sys_leap, this_clock,
+ ctl_sys_num_events, ctl_sys_last_event);
+}
+
+
+/*
+ * ctl_flushpkt - write out the current packet and prepare
+ * another if necessary.
+ */
+static void
+ctl_flushpkt(
+ int more
+ )
+{
+ int dlen;
+ int sendlen;
+
+ if (!more && datanotbinflag) {
+ /*
+ * Big hack, output a trailing \r\n
+ */
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ }
+ dlen = datapt - (u_char *)rpkt.data;
+ sendlen = dlen + CTL_HEADER_LEN;
+
+ /*
+ * Pad to a multiple of 32 bits
+ */
+ while (sendlen & 0x3) {
+ *datapt++ = '\0';
+ sendlen++;
+ }
+
+ /*
+ * Fill in the packet with the current info
+ */
+ rpkt.r_m_e_op = (u_char)(CTL_RESPONSE|more|(res_opcode &
+ CTL_OP_MASK));
+ rpkt.count = htons((u_short) dlen);
+ rpkt.offset = htons( (u_short) res_offset);
+ if (res_async) {
+ register int i;
+
+ for (i = 0; i < CTL_MAXTRAPS; i++) {
+ if (ctl_trap[i].tr_flags & TRAP_INUSE) {
+ rpkt.li_vn_mode =
+ PKT_LI_VN_MODE(sys_leap,
+ ctl_trap[i].tr_version,
+ MODE_CONTROL);
+ rpkt.sequence =
+ htons(ctl_trap[i].tr_sequence);
+ sendpkt(&ctl_trap[i].tr_addr,
+ ctl_trap[i].tr_localaddr, -4,
+ (struct pkt *)&rpkt, sendlen);
+ if (!more)
+ ctl_trap[i].tr_sequence++;
+ numasyncmsgs++;
+ }
+ }
+ } else {
+ if (res_authenticate && sys_authenticate) {
+ int maclen;
+ int totlen = sendlen;
+ keyid_t keyid = htonl(res_keyid);
+
+ /*
+ * If we are going to authenticate, then there
+ * is an additional requirement that the MAC
+ * begin on a 64 bit boundary.
+ */
+ while (totlen & 7) {
+ *datapt++ = '\0';
+ totlen++;
+ }
+ memcpy(datapt, &keyid, sizeof keyid);
+ maclen = authencrypt(res_keyid,
+ (u_int32 *)&rpkt, totlen);
+ sendpkt(rmt_addr, lcl_inter, -5,
+ (struct pkt *)&rpkt, totlen + maclen);
+ } else {
+ sendpkt(rmt_addr, lcl_inter, -6,
+ (struct pkt *)&rpkt, sendlen);
+ }
+ if (more)
+ numctlfrags++;
+ else
+ numctlresponses++;
+ }
+
+ /*
+ * Set us up for another go around.
+ */
+ res_offset += dlen;
+ datapt = (u_char *)rpkt.data;
+}
+
+
+/*
+ * ctl_putdata - write data into the packet, fragmenting and starting
+ * another if this one is full.
+ */
+static void
+ctl_putdata(
+ const char *dp,
+ unsigned int dlen,
+ int bin /* set to 1 when data is binary */
+ )
+{
+ int overhead;
+
+ overhead = 0;
+ if (!bin) {
+ datanotbinflag = 1;
+ overhead = 3;
+ if (datapt != rpkt.data) {
+ *datapt++ = ',';
+ datalinelen++;
+ if ((dlen + datalinelen + 1) >= MAXDATALINELEN)
+ {
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ datalinelen = 0;
+ } else {
+ *datapt++ = ' ';
+ datalinelen++;
+ }
+ }
+ }
+
+ /*
+ * Save room for trailing junk
+ */
+ if (dlen + overhead + datapt > dataend) {
+ /*
+ * Not enough room in this one, flush it out.
+ */
+ ctl_flushpkt(CTL_MORE);
+ }
+ memmove((char *)datapt, dp, (unsigned)dlen);
+ datapt += dlen;
+ datalinelen += dlen;
+}
+
+
+/*
+ * ctl_putstr - write a tagged string into the response packet
+ */
+static void
+ctl_putstr(
+ const char *tag,
+ const char *data,
+ unsigned int len
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[400];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ if (len > 0) {
+ *cp++ = '=';
+ *cp++ = '"';
+ if (len > (int) (sizeof(buffer) - (cp - buffer) - 1))
+ len = sizeof(buffer) - (cp - buffer) - 1;
+ memmove(cp, data, (unsigned)len);
+ cp += len;
+ *cp++ = '"';
+ }
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putdbl - write a tagged, signed double into the response packet
+ */
+static void
+ctl_putdbl(
+ const char *tag,
+ double ts
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ *cp++ = '=';
+ (void)sprintf(cp, "%.3f", ts);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+/*
+ * ctl_putuint - write a tagged unsigned integer into the response
+ */
+static void
+ctl_putuint(
+ const char *tag,
+ u_long uval
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "%lu", uval);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_puthex - write a tagged unsigned integer, in hex, into the response
+ */
+static void
+ctl_puthex(
+ const char *tag,
+ u_long uval
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "0x%lx", uval);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer,(unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putint - write a tagged signed integer into the response
+ */
+static void
+ctl_putint(
+ const char *tag,
+ long ival
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "%ld", ival);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putts - write a tagged timestamp, in hex, into the response
+ */
+static void
+ctl_putts(
+ const char *tag,
+ l_fp *ts
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "0x%08lx.%08lx", ts->l_ui & 0xffffffffL,
+ ts->l_uf & 0xffffffffL);
+ while (*cp != '\0')
+ cp++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putadr - write an IP address into the response
+ */
+static void
+ctl_putadr(
+ const char *tag,
+ u_int32 addr32,
+ struct sockaddr_storage* addr
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ if (addr == NULL)
+ cq = numtoa(addr32);
+ else
+ cq = stoa(addr);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putid - write a tagged clock ID into the response
+ */
+static void
+ctl_putid(
+ const char *tag,
+ char *id
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = id;
+ while (*cq != '\0' && (cq - id) < 4)
+ *cp++ = *cq++;
+ ctl_putdata(buffer, (unsigned)( cp - buffer ), 0);
+}
+
+
+/*
+ * ctl_putarray - write a tagged eight element double array into the response
+ */
+static void
+ctl_putarray(
+ const char *tag,
+ double *arr,
+ int start
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+ int i;
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ i = start;
+ do {
+ if (i == 0)
+ i = NTP_SHIFT;
+ i--;
+ (void)sprintf(cp, " %.2f", arr[i] * 1e3);
+ while (*cp != '\0')
+ cp++;
+ } while(i != start);
+ ctl_putdata(buffer, (unsigned)(cp - buffer), 0);
+}
+
+
+/*
+ * ctl_putsys - output a system variable
+ */
+static void
+ctl_putsys(
+ int varid
+ )
+{
+ l_fp tmp;
+ char str[256];
+#ifdef OPENSSL
+ struct cert_info *cp;
+ char cbuf[256];
+#endif /* OPENSSL */
+
+ switch (varid) {
+
+ case CS_LEAP:
+ ctl_putuint(sys_var[CS_LEAP].text, sys_leap);
+ break;
+
+ case CS_STRATUM:
+ ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum);
+ break;
+
+ case CS_PRECISION:
+ ctl_putint(sys_var[CS_PRECISION].text, sys_precision);
+ break;
+
+ case CS_ROOTDELAY:
+ ctl_putdbl(sys_var[CS_ROOTDELAY].text, sys_rootdelay *
+ 1e3);
+ break;
+
+ case CS_ROOTDISPERSION:
+ ctl_putdbl(sys_var[CS_ROOTDISPERSION].text,
+ sys_rootdispersion * 1e3);
+ break;
+
+ case CS_REFID:
+ if (sys_stratum > 1 && sys_stratum < STRATUM_UNSPEC)
+ ctl_putadr(sys_var[CS_REFID].text, sys_refid, NULL);
+ else
+ ctl_putid(sys_var[CS_REFID].text,
+ (char *)&sys_refid);
+ break;
+
+ case CS_REFTIME:
+ ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime);
+ break;
+
+ case CS_POLL:
+ ctl_putuint(sys_var[CS_POLL].text, sys_poll);
+ break;
+
+ case CS_PEERID:
+ if (sys_peer == NULL)
+ ctl_putuint(sys_var[CS_PEERID].text, 0);
+ else
+ ctl_putuint(sys_var[CS_PEERID].text,
+ sys_peer->associd);
+ break;
+
+ case CS_STATE:
+ ctl_putuint(sys_var[CS_STATE].text, (unsigned)state);
+ break;
+
+ case CS_OFFSET:
+ ctl_putdbl(sys_var[CS_OFFSET].text, last_offset * 1e3);
+ break;
+
+ case CS_DRIFT:
+ ctl_putdbl(sys_var[CS_DRIFT].text, drift_comp * 1e6);
+ break;
+
+ case CS_JITTER:
+ ctl_putdbl(sys_var[CS_JITTER].text, sys_jitter * 1e3);
+ break;
+
+ case CS_CLOCK:
+ get_systime(&tmp);
+ ctl_putts(sys_var[CS_CLOCK].text, &tmp);
+ break;
+
+ case CS_PROCESSOR:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor,
+ sizeof(str_processor) - 1);
+#else
+ ctl_putstr(sys_var[CS_PROCESSOR].text,
+ utsnamebuf.machine, strlen(utsnamebuf.machine));
+#endif /* HAVE_UNAME */
+ break;
+
+ case CS_SYSTEM:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_SYSTEM].text, str_system,
+ sizeof(str_system) - 1);
+#else
+ sprintf(str, "%s/%s", utsnamebuf.sysname, utsnamebuf.release);
+ ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str));
+#endif /* HAVE_UNAME */
+ break;
+
+ case CS_VERSION:
+ ctl_putstr(sys_var[CS_VERSION].text, Version,
+ strlen(Version));
+ break;
+
+ case CS_STABIL:
+ ctl_putdbl(sys_var[CS_STABIL].text, clock_stability *
+ 1e6);
+ break;
+
+ case CS_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *t, *be;
+ register const char *ss;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) -
+ strlen(sys_var[CS_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name */
+
+ strcpy(s, sys_var[CS_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+ for (k = sys_var; !(k->flags &EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+ i = strlen(k->text);
+ if (s+i+1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ for (k = ext_sys_var; k && !(k->flags &EOV);
+ k++) {
+ if (k->flags & PADDING)
+ continue;
+
+ ss = k->text;
+ if (!ss)
+ continue;
+
+ while (*ss && *ss != '=')
+ ss++;
+ i = ss - k->text;
+ if (s + i + 1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strncpy(s, k->text,
+ (unsigned)i);
+ s += i;
+ }
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, (unsigned)( s - buf ),
+ 0);
+ }
+ break;
+
+#ifdef OPENSSL
+ case CS_FLAGS:
+ if (crypto_flags) {
+ ctl_puthex(sys_var[CS_FLAGS].text, crypto_flags);
+ }
+ break;
+
+ case CS_DIGEST:
+ if (crypto_flags) {
+ const EVP_MD *dp;
+
+ dp = EVP_get_digestbynid(crypto_flags >> 16);
+ strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)));
+ ctl_putstr(sys_var[CS_DIGEST].text, str,
+ strlen(str));
+ }
+ break;
+
+ case CS_HOST:
+ if (sys_hostname != NULL)
+ ctl_putstr(sys_var[CS_HOST].text, sys_hostname,
+ strlen(sys_hostname));
+ break;
+
+ case CS_CERTIF:
+ for (cp = cinfo; cp != NULL; cp = cp->link) {
+ sprintf(cbuf, "%s %s 0x%x %u", cp->subject,
+ cp->issuer, cp->flags,
+ ntohl(cp->cert.fstamp));
+ ctl_putstr(sys_var[CS_CERTIF].text, cbuf,
+ strlen(cbuf));
+ }
+ break;
+
+ case CS_PUBLIC:
+ if (hostval.fstamp != 0)
+ ctl_putuint(sys_var[CS_PUBLIC].text,
+ ntohl(hostval.fstamp));
+ break;
+
+ case CS_REVTIME:
+ if (hostval.tstamp != 0)
+ ctl_putuint(sys_var[CS_REVTIME].text,
+ ntohl(hostval.tstamp));
+ break;
+
+ case CS_LEAPTAB:
+ if (tai_leap.fstamp != 0)
+ ctl_putuint(sys_var[CS_LEAPTAB].text,
+ ntohl(tai_leap.fstamp));
+ if (sys_tai != 0)
+ ctl_putuint(sys_var[CS_TAI].text, sys_tai);
+ break;
+#endif /* OPENSSL */
+ }
+}
+
+
+/*
+ * ctl_putpeer - output a peer variable
+ */
+static void
+ctl_putpeer(
+ int varid,
+ struct peer *peer
+ )
+{
+#ifdef OPENSSL
+ char str[256];
+ struct autokey *ap;
+#endif /* OPENSSL */
+
+ switch (varid) {
+
+ case CP_CONFIG:
+ ctl_putuint(peer_var[CP_CONFIG].text,
+ (unsigned)((peer->flags & FLAG_CONFIG) != 0));
+ break;
+
+ case CP_AUTHENABLE:
+ ctl_putuint(peer_var[CP_AUTHENABLE].text,
+ (unsigned)((peer->flags & FLAG_AUTHENABLE) != 0));
+ break;
+
+ case CP_AUTHENTIC:
+ ctl_putuint(peer_var[CP_AUTHENTIC].text,
+ (unsigned)((peer->flags & FLAG_AUTHENTIC) != 0));
+ break;
+
+ case CP_SRCADR:
+ ctl_putadr(peer_var[CP_SRCADR].text, 0,
+ &peer->srcadr);
+ break;
+
+ case CP_SRCPORT:
+ ctl_putuint(peer_var[CP_SRCPORT].text,
+ ntohs(((struct sockaddr_in*)&peer->srcadr)->sin_port));
+ break;
+
+ case CP_DSTADR:
+ ctl_putadr(peer_var[CP_DSTADR].text, 0,
+ &(peer->dstadr->sin));
+ break;
+
+ case CP_DSTPORT:
+ ctl_putuint(peer_var[CP_DSTPORT].text,
+ (u_long)(peer->dstadr ?
+ ntohs(((struct sockaddr_in*)&peer->dstadr->sin)->sin_port) : 0));
+ break;
+
+ case CP_LEAP:
+ ctl_putuint(peer_var[CP_LEAP].text, peer->leap);
+ break;
+
+ case CP_HMODE:
+ ctl_putuint(peer_var[CP_HMODE].text, peer->hmode);
+ break;
+
+ case CP_STRATUM:
+ ctl_putuint(peer_var[CP_STRATUM].text, peer->stratum);
+ break;
+
+ case CP_PPOLL:
+ ctl_putuint(peer_var[CP_PPOLL].text, peer->ppoll);
+ break;
+
+ case CP_HPOLL:
+ ctl_putuint(peer_var[CP_HPOLL].text, peer->hpoll);
+ break;
+
+ case CP_PRECISION:
+ ctl_putint(peer_var[CP_PRECISION].text,
+ peer->precision);
+ break;
+
+ case CP_ROOTDELAY:
+ ctl_putdbl(peer_var[CP_ROOTDELAY].text,
+ peer->rootdelay * 1e3);
+ break;
+
+ case CP_ROOTDISPERSION:
+ ctl_putdbl(peer_var[CP_ROOTDISPERSION].text,
+ peer->rootdispersion * 1e3);
+ break;
+
+ case CP_REFID:
+ if (peer->flags & FLAG_REFCLOCK) {
+ if (peer->stratum > 0 && peer->stratum <
+ STRATUM_UNSPEC)
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->refid, NULL);
+ else
+ ctl_putid(peer_var[CP_REFID].text,
+ (char *)&peer->refid);
+ } else {
+ if (peer->stratum > 1 && peer->stratum <
+ STRATUM_UNSPEC)
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->refid, NULL);
+ else
+ ctl_putid(peer_var[CP_REFID].text,
+ (char *)&peer->refid);
+ }
+ break;
+
+ case CP_REFTIME:
+ ctl_putts(peer_var[CP_REFTIME].text, &peer->reftime);
+ break;
+
+ case CP_ORG:
+ ctl_putts(peer_var[CP_ORG].text, &peer->org);
+ break;
+
+ case CP_REC:
+ ctl_putts(peer_var[CP_REC].text, &peer->rec);
+ break;
+
+ case CP_XMT:
+ ctl_putts(peer_var[CP_XMT].text, &peer->xmt);
+ break;
+
+ case CP_REACH:
+ ctl_puthex(peer_var[CP_REACH].text, peer->reach);
+ break;
+
+ case CP_FLASH:
+ ctl_puthex(peer_var[CP_FLASH].text, peer->flash);
+ break;
+
+ case CP_TTL:
+ ctl_putint(peer_var[CP_TTL].text, sys_ttl[peer->ttl]);
+ break;
+
+ case CP_VALID:
+ ctl_putuint(peer_var[CP_VALID].text, peer->unreach);
+ break;
+
+ case CP_RANK:
+ ctl_putuint(peer_var[CP_RANK].text, peer->rank);
+ break;
+
+ case CP_TIMER:
+ ctl_putuint(peer_var[CP_TIMER].text,
+ peer->nextdate - current_time);
+ break;
+
+ case CP_DELAY:
+ ctl_putdbl(peer_var[CP_DELAY].text, peer->delay * 1e3);
+ break;
+
+ case CP_OFFSET:
+ ctl_putdbl(peer_var[CP_OFFSET].text, peer->offset *
+ 1e3);
+ break;
+
+ case CP_JITTER:
+ ctl_putdbl(peer_var[CP_JITTER].text,
+ SQRT(peer->jitter) * 1e3);
+ break;
+
+ case CP_DISPERSION:
+ ctl_putdbl(peer_var[CP_DISPERSION].text, peer->disp *
+ 1e3);
+ break;
+
+ case CP_KEYID:
+ ctl_putuint(peer_var[CP_KEYID].text, peer->keyid);
+ break;
+
+ case CP_FILTDELAY:
+ ctl_putarray(peer_var[CP_FILTDELAY].text,
+ peer->filter_delay, (int)peer->filter_nextpt);
+ break;
+
+ case CP_FILTOFFSET:
+ ctl_putarray(peer_var[CP_FILTOFFSET].text,
+ peer->filter_offset, (int)peer->filter_nextpt);
+ break;
+
+ case CP_FILTERROR:
+ ctl_putarray(peer_var[CP_FILTERROR].text,
+ peer->filter_disp, (int)peer->filter_nextpt);
+ break;
+
+ case CP_PMODE:
+ ctl_putuint(peer_var[CP_PMODE].text, peer->pmode);
+ break;
+
+ case CP_RECEIVED:
+ ctl_putuint(peer_var[CP_RECEIVED].text, peer->received);
+ break;
+
+ case CP_SENT:
+ ctl_putuint(peer_var[CP_SENT].text, peer->sent);
+ break;
+
+ case CP_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *t, *be;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) -
+ strlen(peer_var[CP_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name */
+
+ strcpy(s, peer_var[CP_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+ for (k = peer_var; !(k->flags &EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s + i + 1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+ ctl_putdata(buf, (unsigned)(s - buf), 0);
+ }
+ break;
+#ifdef OPENSSL
+ case CP_FLAGS:
+ if (peer->crypto)
+ ctl_puthex(peer_var[CP_FLAGS].text, peer->crypto);
+ break;
+
+ case CP_DIGEST:
+ if (peer->crypto) {
+ const EVP_MD *dp;
+
+ dp = EVP_get_digestbynid(peer->crypto >> 16);
+ strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp)));
+ ctl_putstr(peer_var[CP_DIGEST].text, str,
+ strlen(str));
+ }
+ break;
+
+ case CP_HOST:
+ if (peer->subject != NULL)
+ ctl_putstr(peer_var[CP_HOST].text, peer->subject,
+ strlen(peer->subject));
+ break;
+
+ case CP_IDENT:
+ if (peer->issuer != NULL)
+ ctl_putstr(peer_var[CP_IDENT].text, peer->issuer,
+ strlen(peer->issuer));
+ break;
+
+ case CP_INITSEQ:
+ if ((ap = (struct autokey *)peer->recval.ptr) == NULL)
+ break;
+ ctl_putint(peer_var[CP_INITSEQ].text, ap->seq);
+ ctl_puthex(peer_var[CP_INITKEY].text, ap->key);
+ ctl_putuint(peer_var[CP_INITTSP].text,
+ ntohl(peer->recval.tstamp));
+ break;
+#endif /* OPENSSL */
+ }
+}
+
+
+#ifdef REFCLOCK
+/*
+ * ctl_putclock - output clock variables
+ */
+static void
+ctl_putclock(
+ int varid,
+ struct refclockstat *clock_stat,
+ int mustput
+ )
+{
+ switch(varid) {
+
+ case CC_TYPE:
+ if (mustput || clock_stat->clockdesc == NULL
+ || *(clock_stat->clockdesc) == '\0') {
+ ctl_putuint(clock_var[CC_TYPE].text, clock_stat->type);
+ }
+ break;
+ case CC_TIMECODE:
+ ctl_putstr(clock_var[CC_TIMECODE].text,
+ clock_stat->p_lastcode,
+ (unsigned)clock_stat->lencode);
+ break;
+
+ case CC_POLL:
+ ctl_putuint(clock_var[CC_POLL].text, clock_stat->polls);
+ break;
+
+ case CC_NOREPLY:
+ ctl_putuint(clock_var[CC_NOREPLY].text,
+ clock_stat->noresponse);
+ break;
+
+ case CC_BADFORMAT:
+ ctl_putuint(clock_var[CC_BADFORMAT].text,
+ clock_stat->badformat);
+ break;
+
+ case CC_BADDATA:
+ ctl_putuint(clock_var[CC_BADDATA].text,
+ clock_stat->baddata);
+ break;
+
+ case CC_FUDGETIME1:
+ if (mustput || (clock_stat->haveflags & CLK_HAVETIME1))
+ ctl_putdbl(clock_var[CC_FUDGETIME1].text,
+ clock_stat->fudgetime1 * 1e3);
+ break;
+
+ case CC_FUDGETIME2:
+ if (mustput || (clock_stat->haveflags & CLK_HAVETIME2)) ctl_putdbl(clock_var[CC_FUDGETIME2].text,
+ clock_stat->fudgetime2 * 1e3);
+ break;
+
+ case CC_FUDGEVAL1:
+ if (mustput || (clock_stat->haveflags & CLK_HAVEVAL1))
+ ctl_putint(clock_var[CC_FUDGEVAL1].text,
+ clock_stat->fudgeval1);
+ break;
+
+ case CC_FUDGEVAL2:
+ if (mustput || (clock_stat->haveflags & CLK_HAVEVAL2)) {
+ if (clock_stat->fudgeval1 > 1)
+ ctl_putadr(clock_var[CC_FUDGEVAL2].text,
+ (u_int32)clock_stat->fudgeval2, NULL);
+ else
+ ctl_putid(clock_var[CC_FUDGEVAL2].text,
+ (char *)&clock_stat->fudgeval2);
+ }
+ break;
+
+ case CC_FLAGS:
+ if (mustput || (clock_stat->haveflags & (CLK_HAVEFLAG1 |
+ CLK_HAVEFLAG2 | CLK_HAVEFLAG3 | CLK_HAVEFLAG4)))
+ ctl_putuint(clock_var[CC_FLAGS].text,
+ clock_stat->flags);
+ break;
+
+ case CC_DEVICE:
+ if (clock_stat->clockdesc == NULL ||
+ *(clock_stat->clockdesc) == '\0') {
+ if (mustput)
+ ctl_putstr(clock_var[CC_DEVICE].text,
+ "", 0);
+ } else {
+ ctl_putstr(clock_var[CC_DEVICE].text,
+ clock_stat->clockdesc,
+ strlen(clock_stat->clockdesc));
+ }
+ break;
+
+ case CC_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *t, *be;
+ register const char *ss;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf);
+ if (s + strlen(clock_var[CC_VARLIST].text) + 4 >
+ be)
+ break; /* really long var name */
+
+ strcpy(s, clock_var[CC_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+
+ for (k = clock_var; !(k->flags &EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s + i + 1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ for (k = clock_stat->kv_list; k && !(k->flags &
+ EOV); k++) {
+ if (k->flags & PADDING)
+ continue;
+
+ ss = k->text;
+ if (!ss)
+ continue;
+
+ while (*ss && *ss != '=')
+ ss++;
+ i = ss - k->text;
+ if (s+i+1 >= be)
+ break;
+
+ if (s != t)
+ *s++ = ',';
+ strncpy(s, k->text, (unsigned)i);
+ s += i;
+ *s = '\0';
+ }
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+ ctl_putdata(buf, (unsigned)( s - buf ), 0);
+ }
+ break;
+ }
+}
+#endif
+
+
+
+/*
+ * ctl_getitem - get the next data item from the incoming packet
+ */
+static struct ctl_var *
+ctl_getitem(
+ struct ctl_var *var_list,
+ char **data
+ )
+{
+ register struct ctl_var *v;
+ register char *cp;
+ register char *tp;
+ static struct ctl_var eol = { 0, EOV, };
+ static char buf[128];
+
+ /*
+ * Delete leading commas and white space
+ */
+ while (reqpt < reqend && (*reqpt == ',' ||
+ isspace((int)*reqpt)))
+ reqpt++;
+ if (reqpt >= reqend)
+ return (0);
+
+ if (var_list == (struct ctl_var *)0)
+ return (&eol);
+
+ /*
+ * Look for a first character match on the tag. If we find
+ * one, see if it is a full match.
+ */
+ v = var_list;
+ cp = reqpt;
+ while (!(v->flags & EOV)) {
+ if (!(v->flags & PADDING) && *cp == *(v->text)) {
+ tp = v->text;
+ while (*tp != '\0' && *tp != '=' && cp <
+ reqend && *cp == *tp) {
+ cp++;
+ tp++;
+ }
+ if ((*tp == '\0') || (*tp == '=')) {
+ while (cp < reqend && isspace((int)*cp))
+ cp++;
+ if (cp == reqend || *cp == ',') {
+ buf[0] = '\0';
+ *data = buf;
+ if (cp < reqend)
+ cp++;
+ reqpt = cp;
+ return v;
+ }
+ if (*cp == '=') {
+ cp++;
+ tp = buf;
+ while (cp < reqend && isspace((int)*cp))
+ cp++;
+ while (cp < reqend && *cp != ',') {
+ *tp++ = *cp++;
+ if (tp >= buf + sizeof(buf)) {
+ ctl_error(CERR_BADFMT);
+ numctlbadpkts++;
+ msyslog(LOG_WARNING,
+ "Possible 'ntpdx' exploit from %s:%d (possibly spoofed)\n",
+ stoa(rmt_addr), SRCPORT(rmt_addr)
+ );
+ return (0);
+ }
+ }
+ if (cp < reqend)
+ cp++;
+ *tp-- = '\0';
+ while (tp >= buf) {
+ if (!isspace((int)(*tp)))
+ break;
+ *tp-- = '\0';
+ }
+ reqpt = cp;
+ *data = buf;
+ return (v);
+ }
+ }
+ cp = reqpt;
+ }
+ v++;
+ }
+ return v;
+}
+
+
+/*
+ * control_unspec - response to an unspecified op-code
+ */
+/*ARGSUSED*/
+static void
+control_unspec(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ struct peer *peer;
+
+ /*
+ * What is an appropriate response to an unspecified op-code?
+ * I return no errors and no data, unless a specified assocation
+ * doesn't exist.
+ */
+ if (res_associd != 0) {
+ if ((peer = findpeerbyassoc(res_associd)) == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ } else {
+ rpkt.status = htons(ctlsysstatus());
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_status - return either a list of associd's, or a particular
+ * peer's status.
+ */
+/*ARGSUSED*/
+static void
+read_status(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ register int i;
+ register struct peer *peer;
+ u_short ass_stat[CTL_MAX_DATA_LEN / sizeof(u_short)];
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("read_status: ID %d\n", res_associd);
+#endif
+ /*
+ * Two choices here. If the specified association ID is
+ * zero we return all known assocation ID's. Otherwise
+ * we return a bunch of stuff about the particular peer.
+ */
+ if (res_associd == 0) {
+ register int n;
+
+ n = 0;
+ rpkt.status = htons(ctlsysstatus());
+ for (i = 0; i < HASH_SIZE; i++) {
+ for (peer = assoc_hash[i]; peer != 0;
+ peer = peer->ass_next) {
+ ass_stat[n++] = htons(peer->associd);
+ ass_stat[n++] =
+ htons(ctlpeerstatus(peer));
+ if (n ==
+ CTL_MAX_DATA_LEN/sizeof(u_short)) {
+ ctl_putdata((char *)ass_stat,
+ n * sizeof(u_short), 1);
+ n = 0;
+ }
+ }
+ }
+
+ if (n != 0)
+ ctl_putdata((char *)ass_stat, n *
+ sizeof(u_short), 1);
+ ctl_flushpkt(0);
+ } else {
+ peer = findpeerbyassoc(res_associd);
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ } else {
+ register u_char *cp;
+
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ /*
+ * For now, output everything we know about the
+ * peer. May be more selective later.
+ */
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ ctl_flushpkt(0);
+ }
+ }
+}
+
+
+/*
+ * read_variables - return the variables the caller asks for
+ */
+/*ARGSUSED*/
+static void
+read_variables(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ register struct ctl_var *v;
+ register int i;
+ char *valuep;
+ u_char *wants;
+ unsigned int gotvar = (CS_MAXCODE > CP_MAXCODE) ? (CS_MAXCODE +
+ 1) : (CP_MAXCODE + 1);
+ if (res_associd == 0) {
+ /*
+ * Wants system variables. Figure out which he wants
+ * and give them to him.
+ */
+ rpkt.status = htons(ctlsysstatus());
+ if (res_authokay)
+ ctl_sys_num_events = 0;
+ gotvar += count_var(ext_sys_var);
+ wants = (u_char *)emalloc(gotvar);
+ memset((char *)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(ext_sys_var,
+ &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char *)wants);
+ return;
+ }
+ wants[CS_MAXCODE + 1 +
+ v->code] = 1;
+ gotvar = 1;
+ continue;
+ } else {
+ break; /* shouldn't happen ! */
+ }
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+ if (gotvar) {
+ for (i = 1; i <= CS_MAXCODE; i++)
+ if (wants[i])
+ ctl_putsys(i);
+ for (i = 0; ext_sys_var &&
+ !(ext_sys_var[i].flags & EOV); i++)
+ if (wants[i + CS_MAXCODE + 1])
+ ctl_putdata(ext_sys_var[i].text,
+ strlen(ext_sys_var[i].text),
+ 0);
+ } else {
+ register u_char *cs;
+ register struct ctl_var *kv;
+
+ for (cs = def_sys_var; *cs != 0; cs++)
+ ctl_putsys((int)*cs);
+ for (kv = ext_sys_var; kv && !(kv->flags & EOV);
+ kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text,
+ strlen(kv->text), 0);
+ }
+ free((char *)wants);
+ } else {
+ register struct peer *peer;
+
+ /*
+ * Wants info for a particular peer. See if we know
+ * the guy.
+ */
+ peer = findpeerbyassoc(res_associd);
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ wants = (u_char *)emalloc(gotvar);
+ memset((char*)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(peer_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char *)wants);
+ return;
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+ if (gotvar) {
+ for (i = 1; i <= CP_MAXCODE; i++)
+ if (wants[i])
+ ctl_putpeer(i, peer);
+ } else {
+ register u_char *cp;
+
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ }
+ free((char *)wants);
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * write_variables - write into variables. We only allow leap bit
+ * writing this way.
+ */
+/*ARGSUSED*/
+static void
+write_variables(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ register struct ctl_var *v;
+ register int ext_var;
+ char *valuep;
+ long val = 0;
+
+ /*
+ * If he's trying to write into a peer tell him no way
+ */
+ if (res_associd != 0) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Set status
+ */
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * Look through the variables. Dump out at the first sign of
+ * trouble.
+ */
+ while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
+ ext_var = 0;
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(ext_sys_var, &valuep)) !=
+ 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ return;
+ }
+ ext_var = 1;
+ } else {
+ break;
+ }
+ }
+ if (!(v->flags & CAN_WRITE)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ if (!ext_var && (*valuep == '\0' || !atoint(valuep,
+ &val))) {
+ ctl_error(CERR_BADFMT);
+ return;
+ }
+ if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) {
+ ctl_error(CERR_BADVALUE);
+ return;
+ }
+
+ if (ext_var) {
+ char *s = (char *)emalloc(strlen(v->text) +
+ strlen(valuep) + 2);
+ const char *t;
+ char *tt = s;
+
+ t = v->text;
+ while (*t && *t != '=')
+ *tt++ = *t++;
+
+ *tt++ = '=';
+ strcat(tt, valuep);
+ set_sys_var(s, strlen(s)+1, v->flags);
+ free(s);
+ } else {
+ /*
+ * This one seems sane. Save it.
+ */
+ switch(v->code) {
+
+ case CS_LEAP:
+ default:
+ ctl_error(CERR_UNSPEC); /* really */
+ return;
+ }
+ }
+ }
+
+ /*
+ * If we got anything, do it. xxx nothing to do ***
+ */
+ /*
+ if (leapind != ~0 || leapwarn != ~0) {
+ if (!leap_setleap((int)leapind, (int)leapwarn)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ }
+ */
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_clock_status - return clock radio status
+ */
+/*ARGSUSED*/
+static void
+read_clock_status(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+#ifndef REFCLOCK
+ /*
+ * If no refclock support, no data to return
+ */
+ ctl_error(CERR_BADASSOC);
+#else
+ register struct ctl_var *v;
+ register int i;
+ register struct peer *peer;
+ char *valuep;
+ u_char *wants;
+ unsigned int gotvar;
+ struct refclockstat clock_stat;
+
+ if (res_associd == 0) {
+
+ /*
+ * Find a clock for this jerk. If the system peer
+ * is a clock use it, else search the hash tables
+ * for one.
+ */
+ if (sys_peer != 0 && (sys_peer->flags & FLAG_REFCLOCK))
+ {
+ peer = sys_peer;
+ } else {
+ peer = 0;
+ for (i = 0; peer == 0 && i < HASH_SIZE; i++) {
+ for (peer = assoc_hash[i]; peer != 0;
+ peer = peer->ass_next) {
+ if (peer->flags & FLAG_REFCLOCK)
+ break;
+ }
+ }
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ }
+ } else {
+ peer = findpeerbyassoc(res_associd);
+ if (peer == 0 || !(peer->flags & FLAG_REFCLOCK)) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ }
+
+ /*
+ * If we got here we have a peer which is a clock. Get his
+ * status.
+ */
+ clock_stat.kv_list = (struct ctl_var *)0;
+ refclock_control(&peer->srcadr, (struct refclockstat *)0,
+ &clock_stat);
+
+ /*
+ * Look for variables in the packet.
+ */
+ rpkt.status = htons(ctlclkstatus(&clock_stat));
+ gotvar = CC_MAXCODE + 1 + count_var(clock_stat.kv_list);
+ wants = (u_char *)emalloc(gotvar);
+ memset((char*)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(clock_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(clock_stat.kv_list,
+ &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char*)wants);
+ free_varlist(clock_stat.kv_list);
+ return;
+ }
+ wants[CC_MAXCODE + 1 + v->code] = 1;
+ gotvar = 1;
+ continue;
+ } else {
+ break; /* shouldn't happen ! */
+ }
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+
+ if (gotvar) {
+ for (i = 1; i <= CC_MAXCODE; i++)
+ if (wants[i])
+ ctl_putclock(i, &clock_stat, 1);
+ for (i = 0; clock_stat.kv_list &&
+ !(clock_stat.kv_list[i].flags & EOV); i++)
+ if (wants[i + CC_MAXCODE + 1])
+ ctl_putdata(clock_stat.kv_list[i].text,
+ strlen(clock_stat.kv_list[i].text),
+ 0);
+ } else {
+ register u_char *cc;
+ register struct ctl_var *kv;
+
+ for (cc = def_clock_var; *cc != 0; cc++)
+ ctl_putclock((int)*cc, &clock_stat, 0);
+ for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV);
+ kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text),
+ 0);
+ }
+
+ free((char*)wants);
+ free_varlist(clock_stat.kv_list);
+
+ ctl_flushpkt(0);
+#endif
+}
+
+
+/*
+ * write_clock_status - we don't do this
+ */
+/*ARGSUSED*/
+static void
+write_clock_status(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ ctl_error(CERR_PERMISSION);
+}
+
+/*
+ * Trap support from here on down. We send async trap messages when the
+ * upper levels report trouble. Traps can by set either by control
+ * messages or by configuration.
+ */
+/*
+ * set_trap - set a trap in response to a control message
+ */
+static void
+set_trap(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ int traptype;
+
+ /*
+ * See if this guy is allowed
+ */
+ if (restrict_mask & RES_NOTRAP) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Determine his allowed trap type.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict_mask & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlsettrap() to do the work. Return
+ * an error if it can't assign the trap.
+ */
+ if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype,
+ (int)res_version))
+ ctl_error(CERR_NORESOURCE);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * unset_trap - unset a trap in response to a control message
+ */
+static void
+unset_trap(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
+{
+ int traptype;
+
+ /*
+ * We don't prevent anyone from removing his own trap unless the
+ * trap is configured. Note we also must be aware of the
+ * possibility that restriction flags were changed since this
+ * guy last set his trap. Set the trap type based on this.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict_mask & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlclrtrap() to clear this out.
+ */
+ if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype))
+ ctl_error(CERR_BADASSOC);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctlsettrap - called to set a trap
+ */
+int
+ctlsettrap(
+ struct sockaddr_storage *raddr,
+ struct interface *linter,
+ int traptype,
+ int version
+ )
+{
+ register struct ctl_trap *tp;
+ register struct ctl_trap *tptouse;
+
+ /*
+ * See if we can find this trap. If so, we only need update
+ * the flags and the time.
+ */
+ if ((tp = ctlfindtrap(raddr, linter)) != NULL) {
+ switch (traptype) {
+
+ case TRAP_TYPE_CONFIG:
+ tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED;
+ break;
+
+ case TRAP_TYPE_PRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return (1); /* don't change anything */
+ tp->tr_flags = TRAP_INUSE;
+ break;
+
+ case TRAP_TYPE_NONPRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return (1); /* don't change anything */
+ tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO;
+ break;
+ }
+ tp->tr_settime = current_time;
+ tp->tr_resets++;
+ return (1);
+ }
+
+ /*
+ * First we heard of this guy. Try to find a trap structure
+ * for him to use, clearing out lesser priority guys if we
+ * have to. Clear out anyone who's expired while we're at it.
+ */
+ tptouse = NULL;
+ for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
+ if ((tp->tr_flags & TRAP_INUSE) &&
+ !(tp->tr_flags & TRAP_CONFIGURED) &&
+ ((tp->tr_settime + CTL_TRAPTIME) > current_time)) {
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ }
+ if (!(tp->tr_flags & TRAP_INUSE)) {
+ tptouse = tp;
+ } else if (!(tp->tr_flags & TRAP_CONFIGURED)) {
+ switch (traptype) {
+
+ case TRAP_TYPE_CONFIG:
+ if (tptouse == NULL) {
+ tptouse = tp;
+ break;
+ }
+ if (tptouse->tr_flags & TRAP_NONPRIO &&
+ !(tp->tr_flags & TRAP_NONPRIO))
+ break;
+
+ if (!(tptouse->tr_flags & TRAP_NONPRIO)
+ && tp->tr_flags & TRAP_NONPRIO) {
+ tptouse = tp;
+ break;
+ }
+ if (tptouse->tr_origtime <
+ tp->tr_origtime)
+ tptouse = tp;
+ break;
+
+ case TRAP_TYPE_PRIO:
+ if (tp->tr_flags & TRAP_NONPRIO) {
+ if (tptouse == NULL ||
+ (tptouse->tr_flags &
+ TRAP_INUSE &&
+ tptouse->tr_origtime <
+ tp->tr_origtime))
+ tptouse = tp;
+ }
+ break;
+
+ case TRAP_TYPE_NONPRIO:
+ break;
+ }
+ }
+ }
+
+ /*
+ * If we don't have room for him return an error.
+ */
+ if (tptouse == NULL)
+ return (0);
+
+ /*
+ * Set up this structure for him.
+ */
+ tptouse->tr_settime = tptouse->tr_origtime = current_time;
+ tptouse->tr_count = tptouse->tr_resets = 0;
+ tptouse->tr_sequence = 1;
+ tptouse->tr_addr = *raddr;
+ tptouse->tr_localaddr = linter;
+ tptouse->tr_version = (u_char) version;
+ tptouse->tr_flags = TRAP_INUSE;
+ if (traptype == TRAP_TYPE_CONFIG)
+ tptouse->tr_flags |= TRAP_CONFIGURED;
+ else if (traptype == TRAP_TYPE_NONPRIO)
+ tptouse->tr_flags |= TRAP_NONPRIO;
+ num_ctl_traps++;
+ return (1);
+}
+
+
+/*
+ * ctlclrtrap - called to clear a trap
+ */
+int
+ctlclrtrap(
+ struct sockaddr_storage *raddr,
+ struct interface *linter,
+ int traptype
+ )
+{
+ register struct ctl_trap *tp;
+
+ if ((tp = ctlfindtrap(raddr, linter)) == NULL)
+ return (0);
+
+ if (tp->tr_flags & TRAP_CONFIGURED
+ && traptype != TRAP_TYPE_CONFIG)
+ return (0);
+
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ return (1);
+}
+
+
+/*
+ * ctlfindtrap - find a trap given the remote and local addresses
+ */
+static struct ctl_trap *
+ctlfindtrap(
+ struct sockaddr_storage *raddr,
+ struct interface *linter
+ )
+{
+ register struct ctl_trap *tp;
+
+ for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
+ if ((tp->tr_flags & TRAP_INUSE)
+ && (NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr))
+ && SOCKCMP(raddr, &tp->tr_addr)
+ && (linter == tp->tr_localaddr) )
+ return (tp);
+ }
+ return (struct ctl_trap *)NULL;
+}
+
+
+/*
+ * report_event - report an event to the trappers
+ */
+void
+report_event(
+ int err,
+ struct peer *peer
+ )
+{
+ register int i;
+
+ /*
+ * Record error code in proper spots, but have mercy on the
+ * log file.
+ */
+ if (!(err & (PEER_EVENT | CRPT_EVENT))) {
+ if (ctl_sys_num_events < CTL_SYS_MAXEVENTS)
+ ctl_sys_num_events++;
+ if (ctl_sys_last_event != (u_char)err) {
+ NLOG(NLOG_SYSEVENT)
+ msyslog(LOG_INFO, "system event '%s' (0x%02x) status '%s' (0x%02x)",
+ eventstr(err), err,
+ sysstatstr(ctlsysstatus()), ctlsysstatus());
+#ifdef DEBUG
+ if (debug)
+ printf("report_event: system event '%s' (0x%02x) status '%s' (0x%02x)\n",
+ eventstr(err), err,
+ sysstatstr(ctlsysstatus()),
+ ctlsysstatus());
+#endif
+ ctl_sys_last_event = (u_char)err;
+ }
+ } else if (peer != 0) {
+ char *src;
+
+#ifdef REFCLOCK
+ if (ISREFCLOCKADR(&peer->srcadr))
+ src = refnumtoa(&peer->srcadr);
+ else
+#endif
+ src = stoa(&peer->srcadr);
+
+ peer->last_event = (u_char)(err & ~PEER_EVENT);
+ if (peer->num_events < CTL_PEER_MAXEVENTS)
+ peer->num_events++;
+ NLOG(NLOG_PEEREVENT)
+ msyslog(LOG_INFO, "peer %s event '%s' (0x%02x) status '%s' (0x%02x)",
+ src, eventstr(err), err,
+ peerstatstr(ctlpeerstatus(peer)),
+ ctlpeerstatus(peer));
+#ifdef DEBUG
+ if (debug)
+ printf( "peer %s event '%s' (0x%02x) status '%s' (0x%02x)\n",
+ src, eventstr(err), err,
+ peerstatstr(ctlpeerstatus(peer)),
+ ctlpeerstatus(peer));
+#endif
+ } else {
+ msyslog(LOG_ERR,
+ "report_event: err '%s' (0x%02x), no peer",
+ eventstr(err), err);
+#ifdef DEBUG
+ printf(
+ "report_event: peer event '%s' (0x%02x), no peer\n",
+ eventstr(err), err);
+#endif
+ return;
+ }
+
+ /*
+ * If no trappers, return.
+ */
+ if (num_ctl_traps <= 0)
+ return;
+
+ /*
+ * Set up the outgoing packet variables
+ */
+ res_opcode = CTL_OP_ASYNCMSG;
+ res_offset = 0;
+ res_async = 1;
+ res_authenticate = 0;
+ datapt = rpkt.data;
+ dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
+ if (!(err & PEER_EVENT)) {
+ rpkt.associd = 0;
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * For now, put everything we know about system
+ * variables. Don't send crypto strings.
+ */
+ for (i = 1; i <= CS_MAXCODE; i++) {
+#ifdef OPENSSL
+ if (i > CS_VARLIST)
+ continue;
+#endif /* OPENSSL */
+ ctl_putsys(i);
+ }
+#ifdef REFCLOCK
+ /*
+ * for clock exception events: add clock variables to
+ * reflect info on exception
+ */
+ if (err == EVNT_CLOCKEXCPT) {
+ struct refclockstat clock_stat;
+ struct ctl_var *kv;
+
+ clock_stat.kv_list = (struct ctl_var *)0;
+ refclock_control(&peer->srcadr,
+ (struct refclockstat *)0, &clock_stat);
+ ctl_puthex("refclockstatus",
+ ctlclkstatus(&clock_stat));
+ for (i = 1; i <= CC_MAXCODE; i++)
+ ctl_putclock(i, &clock_stat, 0);
+ for (kv = clock_stat.kv_list; kv &&
+ !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text,
+ strlen(kv->text), 0);
+ free_varlist(clock_stat.kv_list);
+ }
+#endif /* REFCLOCK */
+ } else {
+ rpkt.associd = htons(peer->associd);
+ rpkt.status = htons(ctlpeerstatus(peer));
+
+ /*
+ * Dump it all. Later, maybe less.
+ */
+ for (i = 1; i <= CP_MAXCODE; i++) {
+#ifdef OPENSSL
+ if (i > CP_VARLIST)
+ continue;
+#endif /* OPENSSL */
+ ctl_putpeer(i, peer);
+ }
+#ifdef REFCLOCK
+ /*
+ * for clock exception events: add clock variables to
+ * reflect info on exception
+ */
+ if (err == EVNT_PEERCLOCK) {
+ struct refclockstat clock_stat;
+ struct ctl_var *kv;
+
+ clock_stat.kv_list = (struct ctl_var *)0;
+ refclock_control(&peer->srcadr,
+ (struct refclockstat *)0, &clock_stat);
+
+ ctl_puthex("refclockstatus",
+ ctlclkstatus(&clock_stat));
+
+ for (i = 1; i <= CC_MAXCODE; i++)
+ ctl_putclock(i, &clock_stat, 0);
+ for (kv = clock_stat.kv_list; kv &&
+ !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text,
+ strlen(kv->text), 0);
+ free_varlist(clock_stat.kv_list);
+ }
+#endif /* REFCLOCK */
+ }
+
+ /*
+ * We're done, return.
+ */
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctl_clr_stats - clear stat counters
+ */
+void
+ctl_clr_stats(void)
+{
+ ctltimereset = current_time;
+ numctlreq = 0;
+ numctlbadpkts = 0;
+ numctlresponses = 0;
+ numctlfrags = 0;
+ numctlerrors = 0;
+ numctlfrags = 0;
+ numctltooshort = 0;
+ numctlinputresp = 0;
+ numctlinputfrag = 0;
+ numctlinputerr = 0;
+ numctlbadoffset = 0;
+ numctlbadversion = 0;
+ numctldatatooshort = 0;
+ numctlbadop = 0;
+ numasyncmsgs = 0;
+}
+
+static u_long
+count_var(
+ struct ctl_var *k
+ )
+{
+ register u_long c;
+
+ if (!k)
+ return (0);
+
+ c = 0;
+ while (!(k++->flags & EOV))
+ c++;
+ return (c);
+}
+
+char *
+add_var(
+ struct ctl_var **kv,
+ u_long size,
+ u_short def
+ )
+{
+ register u_long c;
+ register struct ctl_var *k;
+
+ c = count_var(*kv);
+
+ k = *kv;
+ *kv = (struct ctl_var *)emalloc((c+2)*sizeof(struct ctl_var));
+ if (k) {
+ memmove((char *)*kv, (char *)k,
+ sizeof(struct ctl_var)*c);
+ free((char *)k);
+ }
+ (*kv)[c].code = (u_short) c;
+ (*kv)[c].text = (char *)emalloc(size);
+ (*kv)[c].flags = def;
+ (*kv)[c+1].code = 0;
+ (*kv)[c+1].text = (char *)0;
+ (*kv)[c+1].flags = EOV;
+ return (char *)(*kv)[c].text;
+}
+
+void
+set_var(
+ struct ctl_var **kv,
+ const char *data,
+ u_long size,
+ u_short def
+ )
+{
+ register struct ctl_var *k;
+ register const char *s;
+ register const char *t;
+ char *td;
+
+ if (!data || !size)
+ return;
+
+ k = *kv;
+ if (k != NULL) {
+ while (!(k->flags & EOV)) {
+ s = data;
+ t = k->text;
+ if (t) {
+ while (*t != '=' && *s - *t == 0) {
+ s++;
+ t++;
+ }
+ if (*s == *t && ((*t == '=') || !*t)) {
+ free((void *)k->text);
+ td = (char *)emalloc(size);
+ memmove(td, data, size);
+ k->text =td;
+ k->flags = def;
+ return;
+ }
+ } else {
+ td = (char *)emalloc(size);
+ memmove(td, data, size);
+ k->text = td;
+ k->flags = def;
+ return;
+ }
+ k++;
+ }
+ }
+ td = add_var(kv, size, def);
+ memmove(td, data, size);
+}
+
+void
+set_sys_var(
+ char *data,
+ u_long size,
+ u_short def
+ )
+{
+ set_var(&ext_sys_var, data, size, def);
+}
+
+void
+free_varlist(
+ struct ctl_var *kv
+ )
+{
+ struct ctl_var *k;
+ if (kv) {
+ for (k = kv; !(k->flags & EOV); k++)
+ free((void *)k->text);
+ free((void *)kv);
+ }
+}
diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c
new file mode 100644
index 0000000..3e67703
--- /dev/null
+++ b/ntpd/ntp_crypto.c
@@ -0,0 +1,4031 @@
+/*
+ * ntp_crypto.c - NTP version 4 public key routines
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef OPENSSL
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+#include "ntp_string.h"
+
+#include "openssl/asn1_mac.h"
+#include "openssl/bn.h"
+#include "openssl/err.h"
+#include "openssl/evp.h"
+#include "openssl/pem.h"
+#include "openssl/rand.h"
+#include "openssl/x509v3.h"
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * Extension field message format
+ *
+ * These are always signed and saved before sending in network byte
+ * order. They must be converted to and from host byte order for
+ * processing.
+ *
+ * +-------+-------+
+ * | op | len | <- extension pointer
+ * +-------+-------+
+ * | assocID |
+ * +---------------+
+ * | timestamp | <- value pointer
+ * +---------------+
+ * | filestamp |
+ * +---------------+
+ * | value len |
+ * +---------------+
+ * | |
+ * = value =
+ * | |
+ * +---------------+
+ * | signature len |
+ * +---------------+
+ * | |
+ * = signature =
+ * | |
+ * +---------------+
+ *
+ * The CRYPTO_RESP bit is set to 0 for requests, 1 for responses.
+ * Requests carry the association ID of the receiver; responses carry
+ * the association ID of the sender. Some messages include only the
+ * operation/length and association ID words and so have length 8
+ * octets. Ohers include the value structure and associated value and
+ * signature fields. These messages include the timestamp, filestamp,
+ * value and signature words and so have length at least 24 octets. The
+ * signature and/or value fields can be empty, in which case the
+ * respective length words are zero. An empty value with nonempty
+ * signature is syntactically valid, but semantically questionable.
+ *
+ * The filestamp represents the time when a cryptographic data file such
+ * as a public/private key pair is created. It follows every reference
+ * depending on that file and serves as a means to obsolete earlier data
+ * of the same type. The timestamp represents the time when the
+ * cryptographic data of the message were last signed. Creation of a
+ * cryptographic data file or signing a message can occur only when the
+ * creator or signor is synchronized to an authoritative source and
+ * proventicated to a trusted authority.
+ *
+ * Note there are four conditions required for server trust. First, the
+ * public key on the certificate must be verified, which involves a
+ * number of format, content and consistency checks. Next, the server
+ * identity must be confirmed by one of four schemes: private
+ * certificate, IFF scheme, GQ scheme or certificate trail hike to a
+ * self signed trusted certificate. Finally, the server signature must
+ * be verified.
+ */
+/*
+ * Cryptodefines
+ */
+#define TAI_1972 10 /* initial TAI offset (s) */
+#define MAX_LEAP 100 /* max UTC leapseconds (s) */
+#define VALUE_LEN (6 * 4) /* min response field length */
+#define YEAR (60 * 60 * 24 * 365) /* seconds in year */
+
+/*
+ * Global cryptodata in host byte order
+ */
+u_int32 crypto_flags = 0x0; /* status word */
+u_int sys_tai; /* current UTC offset from TAI */
+
+/*
+ * Global cryptodata in network byte order
+ */
+struct cert_info *cinfo = NULL; /* certificate info/value */
+struct value hostval; /* host value */
+struct value pubkey; /* public key */
+struct value tai_leap; /* leapseconds table */
+
+/*
+ * Private cryptodata in host byte order
+ */
+static char *passwd = NULL; /* private key password */
+static EVP_PKEY *host_pkey = NULL; /* host key */
+static EVP_PKEY *sign_pkey = NULL; /* sign key */
+static EVP_PKEY *iffpar_pkey = NULL; /* IFF parameters */
+static EVP_PKEY *gqpar_pkey = NULL; /* GQ parameters */
+static EVP_PKEY *mvpar_pkey = NULL; /* MV parameters */
+static const EVP_MD *sign_digest = NULL; /* sign digest */
+static u_int sign_siglen; /* sign key length */
+static char *rand_file = NULL; /* random seed file */
+static char *host_file = NULL; /* host key file */
+static char *sign_file = NULL; /* sign key file */
+static char *iffpar_file = NULL; /* IFF parameters file */
+static char *gqpar_file = NULL; /* GQ parameters file */
+static char *mvpar_file = NULL; /* MV parameters file */
+static char *cert_file = NULL; /* certificate file */
+static char *leap_file = NULL; /* leapseconds file */
+static tstamp_t if_fstamp = 0; /* IFF file stamp */
+static tstamp_t gq_fstamp = 0; /* GQ file stamp */
+static tstamp_t mv_fstamp = 0; /* MV file stamp */
+
+/*
+ * Cryptotypes
+ */
+static int crypto_verify P((struct exten *, struct value *,
+ struct peer *));
+static int crypto_encrypt P((struct exten *, struct value *,
+ keyid_t *));
+static int crypto_alice P((struct peer *, struct value *));
+static int crypto_alice2 P((struct peer *, struct value *));
+static int crypto_alice3 P((struct peer *, struct value *));
+static int crypto_bob P((struct exten *, struct value *));
+static int crypto_bob2 P((struct exten *, struct value *));
+static int crypto_bob3 P((struct exten *, struct value *));
+static int crypto_iff P((struct exten *, struct peer *));
+static int crypto_gq P((struct exten *, struct peer *));
+static int crypto_mv P((struct exten *, struct peer *));
+static u_int crypto_send P((struct exten *, struct value *));
+static tstamp_t crypto_time P((void));
+static u_long asn2ntp P((ASN1_TIME *));
+static struct cert_info *cert_parse P((u_char *, u_int, tstamp_t));
+static int cert_sign P((struct exten *, struct value *));
+static int cert_valid P((struct cert_info *, EVP_PKEY *));
+static int cert_install P((struct exten *, struct peer *));
+static void cert_free P((struct cert_info *));
+static EVP_PKEY *crypto_key P((char *, tstamp_t *));
+static int bighash P((BIGNUM *, BIGNUM *));
+static struct cert_info *crypto_cert P((char *));
+static void crypto_tai P((char *));
+
+#ifdef SYS_WINNT
+int
+readlink(char * link, char * file, int len) {
+ return (-1);
+}
+#endif
+
+/*
+ * session_key - generate session key
+ *
+ * This routine generates a session key from the source address,
+ * destination address, key ID and private value. The value of the
+ * session key is the MD5 hash of these values, while the next key ID is
+ * the first four octets of the hash.
+ *
+ * Returns the next key ID
+ */
+keyid_t
+session_key(
+ struct sockaddr_storage *srcadr, /* source address */
+ struct sockaddr_storage *dstadr, /* destination address */
+ keyid_t keyno, /* key ID */
+ keyid_t private, /* private value */
+ u_long lifetime /* key lifetime */
+ )
+{
+ EVP_MD_CTX ctx; /* message digest context */
+ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */
+ keyid_t keyid; /* key identifer */
+ u_int32 header[10]; /* data in network byte order */
+ u_int hdlen, len;
+
+ /*
+ * Generate the session key and key ID. If the lifetime is
+ * greater than zero, install the key and call it trusted.
+ */
+ hdlen = 0;
+ switch(srcadr->ss_family) {
+ case AF_INET:
+ header[0] = ((struct sockaddr_in *)srcadr)->sin_addr.s_addr;
+ header[1] = ((struct sockaddr_in *)dstadr)->sin_addr.s_addr;
+ header[2] = htonl(keyno);
+ header[3] = htonl(private);
+ hdlen = 4 * sizeof(u_int32);
+ break;
+ case AF_INET6:
+ memcpy(&header[0], &GET_INADDR6(*srcadr),
+ sizeof(struct in6_addr));
+ memcpy(&header[4], &GET_INADDR6(*dstadr),
+ sizeof(struct in6_addr));
+ header[8] = htonl(keyno);
+ header[9] = htonl(private);
+ hdlen = 10 * sizeof(u_int32);
+ break;
+ }
+ EVP_DigestInit(&ctx, EVP_md5());
+ EVP_DigestUpdate(&ctx, (u_char *)header, hdlen);
+ EVP_DigestFinal(&ctx, dgst, &len);
+ memcpy(&keyid, dgst, 4);
+ keyid = ntohl(keyid);
+ if (lifetime != 0) {
+ MD5auth_setkey(keyno, dgst, len);
+ authtrust(keyno, lifetime);
+ }
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "session_key: %s > %s %08x %08x hash %08x life %lu\n",
+ stoa(srcadr), stoa(dstadr), keyno,
+ private, keyid, lifetime);
+#endif
+ return (keyid);
+}
+
+
+/*
+ * make_keylist - generate key list
+ *
+ * This routine constructs a pseudo-random sequence by repeatedly
+ * hashing the session key starting from a given source address,
+ * destination address, private value and the next key ID of the
+ * preceeding session key. The last entry on the list is saved along
+ * with its sequence number and public signature.
+ */
+void
+make_keylist(
+ struct peer *peer, /* peer structure pointer */
+ struct interface *dstadr /* interface */
+ )
+{
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ struct autokey *ap; /* autokey pointer */
+ struct value *vp; /* value pointer */
+ keyid_t keyid = 0; /* next key ID */
+ keyid_t cookie; /* private value */
+ u_long lifetime;
+ u_int len;
+ int i;
+
+ /*
+ * Allocate the key list if necessary.
+ */
+ tstamp = crypto_time();
+ if (peer->keylist == NULL)
+ peer->keylist = emalloc(sizeof(keyid_t) *
+ NTP_MAXSESSION);
+
+ /*
+ * Generate an initial key ID which is unique and greater than
+ * NTP_MAXKEY.
+ */
+ while (1) {
+ keyid = (u_long)RANDOM & 0xffffffff;
+ if (keyid <= NTP_MAXKEY)
+ continue;
+ if (authhavekey(keyid))
+ continue;
+ break;
+ }
+
+ /*
+ * Generate up to NTP_MAXSESSION session keys. Stop if the
+ * next one would not be unique or not a session key ID or if
+ * it would expire before the next poll. The private value
+ * included in the hash is zero if broadcast mode, the peer
+ * cookie if client mode or the host cookie if symmetric modes.
+ */
+ lifetime = min(sys_automax, (unsigned long) NTP_MAXSESSION * (1 <<(peer->kpoll)));
+ if (peer->hmode == MODE_BROADCAST)
+ cookie = 0;
+ else
+ cookie = peer->pcookie;
+ for (i = 0; i < NTP_MAXSESSION; i++) {
+ peer->keylist[i] = keyid;
+ peer->keynumber = i;
+ keyid = session_key(&dstadr->sin, &peer->srcadr, keyid,
+ cookie, lifetime);
+ lifetime -= 1 << peer->kpoll;
+ if (auth_havekey(keyid) || keyid <= NTP_MAXKEY ||
+ lifetime <= (unsigned long)(1 << (peer->kpoll)))
+ break;
+ }
+
+ /*
+ * Save the last session key ID, sequence number and timestamp,
+ * then sign these values for later retrieval by the clients. Be
+ * careful not to use invalid key media. Use the public values
+ * timestamp as filestamp.
+ */
+ vp = &peer->sndval;
+ if (vp->ptr == NULL)
+ vp->ptr = emalloc(sizeof(struct autokey));
+ ap = (struct autokey *)vp->ptr;
+ ap->seq = htonl(peer->keynumber);
+ ap->key = htonl(keyid);
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = hostval.tstamp;
+ vp->vallen = htonl(sizeof(struct autokey));
+ vp->siglen = 0;
+ if (vp->tstamp != 0) {
+ if (vp->sig == NULL)
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)vp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, sizeof(struct autokey));
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ else
+ msyslog(LOG_ERR, "make_keys %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ peer->flags |= FLAG_ASSOC;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("make_keys: %d %08x %08x ts %u fs %u poll %d\n",
+ ntohl(ap->seq), ntohl(ap->key), cookie,
+ ntohl(vp->tstamp), ntohl(vp->fstamp), peer->kpoll);
+#endif
+}
+
+
+/*
+ * crypto_recv - parse extension fields
+ *
+ * This routine is called when the packet has been matched to an
+ * association and passed sanity, format and MAC checks. We believe the
+ * extension field values only if the field has proper format and
+ * length, the timestamp and filestamp are valid and the signature has
+ * valid length and is verified. There are a few cases where some values
+ * are believed even if the signature fails, but only if the proventic
+ * bit is not set.
+ */
+int
+crypto_recv(
+ struct peer *peer, /* peer structure pointer */
+ struct recvbuf *rbufp /* packet buffer pointer */
+ )
+{
+ const EVP_MD *dp; /* message digest algorithm */
+ u_int32 *pkt; /* receive packet pointer */
+ struct autokey *ap, *bp; /* autokey pointer */
+ struct exten *ep, *fp; /* extension pointers */
+ int has_mac; /* length of MAC field */
+ int authlen; /* offset of MAC field */
+ associd_t associd; /* association ID */
+ tstamp_t tstamp = 0; /* timestamp */
+ tstamp_t fstamp = 0; /* filestamp */
+ u_int len; /* extension field length */
+ u_int code; /* extension field opcode */
+ u_int vallen = 0; /* value length */
+ X509 *cert; /* X509 certificate */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ keyid_t cookie; /* crumbles */
+ int rval = XEVNT_OK;
+ u_char *ptr;
+ u_int32 temp32;
+#ifdef KERNEL_PLL
+#if NTP_API > 3
+ struct timex ntv; /* kernel interface structure */
+#endif /* NTP_API */
+#endif /* KERNEL_PLL */
+
+ /*
+ * Initialize. Note that the packet has already been checked for
+ * valid format and extension field lengths. First extract the
+ * field length, command code and association ID in host byte
+ * order. These are used with all commands and modes. Then check
+ * the version number, which must be 2, and length, which must
+ * be at least 8 for requests and VALUE_LEN (24) for responses.
+ * Packets that fail either test sink without a trace. The
+ * association ID is saved only if nonzero.
+ */
+ authlen = LEN_PKT_NOMAC;
+ while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) {
+ pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4;
+ ep = (struct exten *)pkt;
+ code = ntohl(ep->opcode) & 0xffff0000;
+ len = ntohl(ep->opcode) & 0x0000ffff;
+ associd = (associd_t) ntohl(pkt[1]);
+ rval = XEVNT_OK;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_recv: flags 0x%x ext offset %d len %u code %x assocID %d\n",
+ peer->crypto, authlen, len, code >> 16,
+ associd);
+#endif
+
+ /*
+ * Check version number and field length. If bad,
+ * quietly ignore the packet.
+ */
+ if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8 ||
+ (len < VALUE_LEN && (code & CRYPTO_RESP))) {
+ sys_unknownversion++;
+ code |= CRYPTO_ERROR;
+ }
+
+ /*
+ * Little vulnerability bandage here. If a perp tosses a
+ * fake association ID over the fence, we better toss it
+ * out. Only the first one counts.
+ */
+ if (code & CRYPTO_RESP) {
+ if (peer->assoc == 0)
+ peer->assoc = associd;
+ else if (peer->assoc != associd)
+ code |= CRYPTO_ERROR;
+ }
+ if (len >= VALUE_LEN) {
+ tstamp = ntohl(ep->tstamp);
+ fstamp = ntohl(ep->fstamp);
+ vallen = ntohl(ep->vallen);
+ }
+ switch (code) {
+
+ /*
+ * Install status word, host name, signature scheme and
+ * association ID. In OpenSSL the signature algorithm is
+ * bound to the digest algorithm, so the NID completely
+ * defines the signature scheme. Note the request and
+ * response are identical, but neither is validated by
+ * signature. The request is processed here only in
+ * symmetric modes. The server name field would be
+ * useful to implement access controls in future.
+ */
+ case CRYPTO_ASSOC:
+
+ /*
+ * Pass the extension field to the transmit
+ * side.
+ */
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ temp32 = CRYPTO_RESP;
+ fp->opcode |= htonl(temp32);
+ peer->cmmd = fp;
+ /* fall through */
+
+ case CRYPTO_ASSOC | CRYPTO_RESP:
+
+ /*
+ * Discard the message if it has already been
+ * stored or the server is not synchronized.
+ */
+ if (peer->crypto || !fstamp)
+ break;
+
+ if (len < VALUE_LEN + vallen) {
+ rval = XEVNT_LEN;
+ break;
+ }
+
+ /*
+ * Check the identity schemes are compatible. If
+ * the client has PC, the server must have PC,
+ * in which case the server public key and
+ * identity are presumed valid, so we skip the
+ * certificate and identity exchanges and move
+ * immediately to the cookie exchange which
+ * confirms the server signature. If the client
+ * has IFF or GC or both, the server must have
+ * the same one or both. Otherwise, the default
+ * TC scheme is used.
+ */
+ if (crypto_flags & CRYPTO_FLAG_PRIV) {
+ if (!(fstamp & CRYPTO_FLAG_PRIV))
+ rval = XEVNT_KEY;
+ else
+ fstamp |= CRYPTO_FLAG_VALID |
+ CRYPTO_FLAG_VRFY;
+ } else if (crypto_flags & CRYPTO_FLAG_MASK &&
+ !(crypto_flags & fstamp &
+ CRYPTO_FLAG_MASK)) {
+ rval = XEVNT_KEY;
+ }
+
+ /*
+ * Discard the message if identity error.
+ */
+ if (rval != XEVNT_OK)
+ break;
+
+ /*
+ * Discard the message if the host name length
+ * is unreasonable or the signature digest NID
+ * is not supported.
+ */
+ temp32 = (fstamp >> 16) & 0xffff;
+ dp =
+ (const EVP_MD *)EVP_get_digestbynid(temp32);
+ if (vallen == 0 || vallen > MAXHOSTNAME)
+ rval = XEVNT_LEN;
+ else if (dp == NULL)
+ rval = XEVNT_MD;
+ if (rval != XEVNT_OK)
+ break;
+
+ /*
+ * Save status word, host name and message
+ * digest/signature type. If PC identity, be
+ * sure not to sign the certificate.
+ */
+ if (crypto_flags & CRYPTO_FLAG_PRIV)
+ fstamp |= CRYPTO_FLAG_SIGN;
+ peer->crypto = fstamp;
+ peer->digest = dp;
+ peer->subject = emalloc(vallen + 1);
+ memcpy(peer->subject, ep->pkt, vallen);
+ peer->subject[vallen] = '\0';
+ peer->issuer = emalloc(vallen + 1);
+ strcpy(peer->issuer, peer->subject);
+ temp32 = (fstamp >> 16) & 0xffff;
+ sprintf(statstr,
+ "flags 0x%x host %s signature %s", fstamp,
+ peer->subject, OBJ_nid2ln(temp32));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Decode X509 certificate in ASN.1 format and extract
+ * the data containing, among other things, subject
+ * name and public key. In the default identification
+ * scheme, the certificate trail is followed to a self
+ * signed trusted certificate.
+ */
+ case CRYPTO_CERT | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * already confirmed.
+ */
+ if (peer->crypto & CRYPTO_FLAG_VRFY)
+ break;
+
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Scan the certificate list to delete old
+ * versions and link the newest version first on
+ * the list.
+ */
+ if ((rval = cert_install(ep, peer)) != XEVNT_OK)
+ break;
+
+ /*
+ * If we snatch the certificate before the
+ * server certificate has been signed by its
+ * server, it will be self signed. When it is,
+ * we chase the certificate issuer, which the
+ * server has, and keep going until a self
+ * signed trusted certificate is found. Be sure
+ * to update the issuer field, since it may
+ * change.
+ */
+ if (peer->issuer != NULL)
+ free(peer->issuer);
+ peer->issuer = emalloc(strlen(cinfo->issuer) +
+ 1);
+ strcpy(peer->issuer, cinfo->issuer);
+
+ /*
+ * We plug in the public key and group key in
+ * the first certificate received. However, note
+ * that this certificate might not be signed by
+ * the server, so we can't check the
+ * signature/digest NID.
+ */
+ if (peer->pkey == NULL) {
+ ptr = (u_char *)cinfo->cert.ptr;
+ cert = d2i_X509(NULL, &ptr,
+ ntohl(cinfo->cert.vallen));
+ peer->pkey = X509_get_pubkey(cert);
+ X509_free(cert);
+ }
+ peer->flash &= ~TEST10;
+ temp32 = cinfo->nid;
+ sprintf(statstr, "cert %s 0x%x %s (%u) fs %u",
+ cinfo->subject, cinfo->flags,
+ OBJ_nid2ln(temp32), temp32,
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Schnorr (IFF)identity scheme. This scheme is designed
+ * for use with shared secret group keys and where the
+ * certificate may be generated by a third party. The
+ * client sends a challenge to the server, which
+ * performs a calculation and returns the result. A
+ * positive result is possible only if both client and
+ * server contain the same secret group key.
+ */
+ case CRYPTO_IFF | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * already confirmed.
+ */
+ if (peer->crypto & CRYPTO_FLAG_VRFY)
+ break;
+
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the the challenge matches the response,
+ * the certificate public key, as well as the
+ * server public key, signatyre and identity are
+ * all verified at the same time. The server is
+ * declared trusted, so we skip further
+ * certificate stages and move immediately to
+ * the cookie stage.
+ */
+ if ((rval = crypto_iff(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY |
+ CRYPTO_FLAG_PROV;
+ peer->flash &= ~TEST10;
+ sprintf(statstr, "iff fs %u",
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Guillou-Quisquater (GQ) identity scheme. This scheme
+ * is designed for use with public certificates carrying
+ * the GQ public key in an extension field. The client
+ * sends a challenge to the server, which performs a
+ * calculation and returns the result. A positive result
+ * is possible only if both client and server contain
+ * the same group key and the server has the matching GQ
+ * private key.
+ */
+ case CRYPTO_GQ | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * already confirmed.
+ */
+ if (peer->crypto & CRYPTO_FLAG_VRFY)
+ break;
+
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the the challenge matches the response,
+ * the certificate public key, as well as the
+ * server public key, signatyre and identity are
+ * all verified at the same time. The server is
+ * declared trusted, so we skip further
+ * certificate stages and move immediately to
+ * the cookie stage.
+ */
+ if ((rval = crypto_gq(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY |
+ CRYPTO_FLAG_PROV;
+ peer->flash &= ~TEST10;
+ sprintf(statstr, "gq fs %u",
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * MV
+ */
+ case CRYPTO_MV | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * already confirmed.
+ */
+ if (peer->crypto & CRYPTO_FLAG_VRFY)
+ break;
+
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * If the the challenge matches the response,
+ * the certificate public key, as well as the
+ * server public key, signatyre and identity are
+ * all verified at the same time. The server is
+ * declared trusted, so we skip further
+ * certificate stages and move immediately to
+ * the cookie stage.
+ */
+ if ((rval = crypto_mv(ep, peer)) != XEVNT_OK)
+ break;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY |
+ CRYPTO_FLAG_PROV;
+ peer->flash &= ~TEST10;
+ sprintf(statstr, "mv fs %u",
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * X509 certificate sign response. Validate the
+ * certificate signed by the server and install. Later
+ * this can be provided to clients of this server in
+ * lieu of the self signed certificate in order to
+ * validate the public key.
+ */
+ case CRYPTO_SIGN | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * not confirmed.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ break;
+
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Scan the certificate list to delete old
+ * versions and link the newest version first on
+ * the list.
+ */
+ if ((rval = cert_install(ep, peer)) != XEVNT_OK) break;
+
+ peer->crypto |= CRYPTO_FLAG_SIGN;
+ peer->flash &= ~TEST10;
+ temp32 = cinfo->nid;
+ sprintf(statstr, "sign %s 0x%x %s (%u) fs %u",
+ cinfo->issuer, cinfo->flags,
+ OBJ_nid2ln(temp32), temp32,
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Cookie request in symmetric modes. Roll a random
+ * cookie and install in symmetric mode. Encrypt for the
+ * response, which is transmitted later.
+ */
+ case CRYPTO_COOK:
+
+ /*
+ * Discard the message if invalid or identity
+ * not confirmed.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ break;
+
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Pass the extension field to the transmit
+ * side. If already agreed, walk away.
+ */
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ temp32 = CRYPTO_RESP;
+ fp->opcode |= htonl(temp32);
+ peer->cmmd = fp;
+ if (peer->crypto & CRYPTO_FLAG_AGREE) {
+ peer->flash &= ~TEST10;
+ break;
+ }
+
+ /*
+ * Install cookie values and light the cookie
+ * bit. The transmit side will pick up and
+ * encrypt it for the response.
+ */
+ key_expire(peer);
+ peer->cookval.tstamp = ep->tstamp;
+ peer->cookval.fstamp = ep->fstamp;
+ RAND_bytes((u_char *)&peer->pcookie, 4);
+ peer->crypto &= ~CRYPTO_FLAG_AUTO;
+ peer->crypto |= CRYPTO_FLAG_AGREE;
+ peer->flash &= ~TEST10;
+ sprintf(statstr, "cook %x ts %u fs %u",
+ peer->pcookie, ntohl(ep->tstamp),
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Cookie response in client and symmetric modes. If the
+ * cookie bit is set, the working cookie is the EXOR of
+ * the current and new values.
+ */
+ case CRYPTO_COOK | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * not confirmed or signature not verified with
+ * respect to the cookie values.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ break;
+
+ if ((rval = crypto_verify(ep, &peer->cookval,
+ peer)) != XEVNT_OK)
+ break;
+
+ /*
+ * Decrypt the cookie, hunting all the time for
+ * errors.
+ */
+ if (vallen == (u_int) EVP_PKEY_size(host_pkey)) {
+ RSA_private_decrypt(vallen,
+ (u_char *)ep->pkt,
+ (u_char *)&temp32,
+ host_pkey->pkey.rsa,
+ RSA_PKCS1_OAEP_PADDING);
+ cookie = ntohl(temp32);
+ } else {
+ rval = XEVNT_CKY;
+ break;
+ }
+
+ /*
+ * Install cookie values and light the cookie
+ * bit. If this is not broadcast client mode, we
+ * are done here.
+ */
+ key_expire(peer);
+ peer->cookval.tstamp = ep->tstamp;
+ peer->cookval.fstamp = ep->fstamp;
+ if (peer->crypto & CRYPTO_FLAG_AGREE)
+ peer->pcookie ^= cookie;
+ else
+ peer->pcookie = cookie;
+ if (peer->hmode == MODE_CLIENT &&
+ !(peer->cast_flags & MDF_BCLNT))
+ peer->crypto |= CRYPTO_FLAG_AUTO;
+ else
+ peer->crypto &= ~CRYPTO_FLAG_AUTO;
+ peer->crypto |= CRYPTO_FLAG_AGREE;
+ peer->flash &= ~TEST10;
+ sprintf(statstr, "cook %x ts %u fs %u",
+ peer->pcookie, ntohl(ep->tstamp),
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Install autokey values in broadcast client and
+ * symmetric modes. We have to do this every time the
+ * sever/peer cookie changes or a new keylist is
+ * rolled. Ordinarily, this is automatic as this message
+ * is piggybacked on the first NTP packet sent upon
+ * either of these events. Note that a broadcast client
+ * or symmetric peer can receive this response without a
+ * matching request.
+ */
+ case CRYPTO_AUTO | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * not confirmed or signature not verified with
+ * respect to the receive autokey values.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ break;
+
+ if ((rval = crypto_verify(ep, &peer->recval,
+ peer)) != XEVNT_OK)
+ break;
+
+ /*
+ * Install autokey values and light the
+ * autokey bit. This is not hard.
+ */
+ if (peer->recval.ptr == NULL)
+ peer->recval.ptr =
+ emalloc(sizeof(struct autokey));
+ bp = (struct autokey *)peer->recval.ptr;
+ peer->recval.tstamp = ep->tstamp;
+ peer->recval.fstamp = ep->fstamp;
+ ap = (struct autokey *)ep->pkt;
+ bp->seq = ntohl(ap->seq);
+ bp->key = ntohl(ap->key);
+ peer->pkeyid = bp->key;
+ peer->crypto |= CRYPTO_FLAG_AUTO;
+ peer->flash &= ~TEST10;
+ sprintf(statstr,
+ "auto seq %d key %x ts %u fs %u", bp->seq,
+ bp->key, ntohl(ep->tstamp),
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * Install leapseconds table in symmetric modes. This
+ * table is proventicated to the NIST primary servers,
+ * either by copying the file containing the table from
+ * a NIST server to a trusted server or directly using
+ * this protocol. While the entire table is installed at
+ * the server, presently only the current TAI offset is
+ * provided via the kernel to other applications.
+ */
+ case CRYPTO_TAI:
+
+ /*
+ * Discard the message if invalid or identity
+ * not confirmed.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ break;
+
+ if ((rval = crypto_verify(ep, NULL, peer)) !=
+ XEVNT_OK)
+ break;
+
+ /*
+ * Pass the extension field to the transmit
+ * side. Continue below if a leapseconds table
+ * accompanies the message.
+ */
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ temp32 = CRYPTO_RESP;
+ fp->opcode |= htonl(temp32);
+ peer->cmmd = fp;
+ if (len <= VALUE_LEN) {
+ peer->flash &= ~TEST10;
+ break;
+ }
+ /* fall through */
+
+ case CRYPTO_TAI | CRYPTO_RESP:
+
+ /*
+ * Discard the message if invalid or identity
+ * not confirmed or signature not verified with
+ * respect to the leapsecond table values.
+ */
+ if (!(peer->crypto & CRYPTO_FLAG_VRFY))
+ break;
+
+ if ((rval = crypto_verify(ep, &peer->tai_leap,
+ peer)) != XEVNT_OK)
+ break;
+
+ /*
+ * Initialize peer variables, leapseconds
+ * structure and extension field in network byte
+ * order. Since a filestamp may have changed,
+ * recompute the signatures.
+ */
+ peer->tai_leap.tstamp = ep->tstamp;
+ peer->tai_leap.fstamp = ep->fstamp;
+ peer->tai_leap.vallen = ep->vallen;
+
+ /*
+ * Install the new table if there is no stored
+ * table or the new table is more recent than
+ * the stored table. Since a filestamp may have
+ * changed, recompute the signatures.
+ */
+ if (ntohl(peer->tai_leap.fstamp) >
+ ntohl(tai_leap.fstamp)) {
+ tai_leap.fstamp = ep->fstamp;
+ tai_leap.vallen = ep->vallen;
+ if (tai_leap.ptr != NULL)
+ free(tai_leap.ptr);
+ tai_leap.ptr = emalloc(vallen);
+ memcpy(tai_leap.ptr, ep->pkt, vallen);
+ crypto_update();
+ sys_tai = vallen / 4 + TAI_1972 - 1;
+ }
+ crypto_flags |= CRYPTO_FLAG_TAI;
+ peer->crypto |= CRYPTO_FLAG_LEAP;
+ peer->flash &= ~TEST10;
+#ifdef KERNEL_PLL
+#if NTP_API > 3
+ /*
+ * If the kernel cooperates, initialize the
+ * current TAI offset.
+ */
+ ntv.modes = MOD_TAI;
+ ntv.constant = sys_tai;
+ (void)ntp_adjtime(&ntv);
+#endif /* NTP_API */
+#endif /* KERNEL_PLL */
+ sprintf(statstr, "leap %u ts %u fs %u",
+ vallen, ntohl(ep->tstamp),
+ ntohl(ep->fstamp));
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+
+ /*
+ * We come here in symmetric modes for miscellaneous
+ * commands that have value fields but are processed on
+ * the transmit side. All we need do here is check for
+ * valid field length. Remaining checks are below and on
+ * the transmit side.
+ */
+ case CRYPTO_IFF:
+ case CRYPTO_GQ:
+ case CRYPTO_MV:
+ case CRYPTO_SIGN:
+ if (len < VALUE_LEN) {
+ rval = XEVNT_LEN;
+ break;
+ }
+
+ /* fall through */
+
+ /*
+ * We come here for miscellaneous requests and unknown
+ * requests and responses. If an unknown response or
+ * error, forget it. If a request, save the extension
+ * field for later. Unknown requests will be caught on
+ * the transmit side.
+ */
+ default:
+ if (code & (CRYPTO_RESP | CRYPTO_ERROR)) {
+ rval = XEVNT_LEN;
+ } else if ((rval = crypto_verify(ep, NULL,
+ peer)) == XEVNT_OK) {
+ fp = emalloc(len);
+ memcpy(fp, ep, len);
+ temp32 = CRYPTO_RESP;
+ fp->opcode |= htonl(temp32);
+ peer->cmmd = fp;
+ }
+ }
+
+ /*
+ * We log everything except length/format errors and
+ * duplicates, which are log clogging vulnerabilities.
+ * The first error found terminates the extension field
+ * scan and we return the laundry to the caller.
+ */
+ if (rval != XEVNT_OK) {
+ sprintf(statstr,
+ "error %x opcode %x ts %u fs %u", rval,
+ code, tstamp, fstamp);
+ if (rval > XEVNT_TSP)
+ record_crypto_stats(&peer->srcadr,
+ statstr);
+ report_event(rval, peer);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_recv: %s\n", statstr);
+#endif
+ break;
+ }
+ authlen += len;
+ }
+ return (rval);
+}
+
+
+/*
+ * crypto_xmit - construct extension fields
+ *
+ * This routine is called both when an association is configured and
+ * when one is not. The only case where this matters is to retrieve the
+ * autokey information, in which case the caller has to provide the
+ * association ID to match the association.
+ *
+ * Returns length of extension field.
+ */
+int
+crypto_xmit(
+ struct pkt *xpkt, /* transmit packet pointer */
+ struct sockaddr_storage *srcadr_sin, /* active runway */
+ int start, /* offset to extension field */
+ struct exten *ep, /* extension pointer */
+ keyid_t cookie /* session cookie */
+ )
+{
+ u_int32 *pkt; /* packet pointer */
+ struct peer *peer; /* peer structure pointer */
+ u_int opcode; /* extension field opcode */
+ struct exten *fp; /* extension pointers */
+ struct cert_info *cp; /* certificate info/value pointer */
+ char certname[MAXHOSTNAME + 1]; /* subject name buffer */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ u_int vallen;
+ u_int len;
+ struct value vtemp;
+ associd_t associd;
+ int rval;
+ keyid_t tcookie;
+
+ /*
+ * Generate the requested extension field request code, length
+ * and association ID. If this is a response and the host is not
+ * synchronized, light the error bit and go home.
+ */
+ pkt = (u_int32 *)xpkt + start / 4;
+ fp = (struct exten *)pkt;
+ opcode = ntohl(ep->opcode);
+ associd = (associd_t) ntohl(ep->associd);
+ fp->associd = htonl(associd);
+ len = 8;
+ rval = XEVNT_OK;
+ switch (opcode & 0xffff0000) {
+
+ /*
+ * Send association request and response with status word and
+ * host name. Note, this message is not signed and the filestamp
+ * contains only the status word. We check at this point whether
+ * the identity schemes are compatible to save tears later on.
+ */
+ case CRYPTO_ASSOC | CRYPTO_RESP:
+ case CRYPTO_ASSOC:
+ len += crypto_send(fp, &hostval);
+ if (crypto_time() == 0)
+ fp->fstamp = 0;
+ else
+ fp->fstamp = htonl(crypto_flags);
+ break;
+
+ /*
+ * Send certificate request. Use the values from the extension
+ * field.
+ */
+ case CRYPTO_CERT:
+ memset(&vtemp, 0, sizeof(vtemp));
+ vtemp.tstamp = ep->tstamp;
+ vtemp.fstamp = ep->fstamp;
+ vtemp.vallen = ep->vallen;
+ vtemp.ptr = (unsigned char *)ep->pkt;
+ len += crypto_send(fp, &vtemp);
+ break;
+
+ /*
+ * Send certificate response or sign request. Use the values
+ * from the certificate. If the request contains no subject
+ * name, assume the name of this host. This is for backwards
+ * compatibility. Light the error bit if no certificate with
+ * the given subject name is found. Of course, private
+ * certificates are never sent.
+ */
+ case CRYPTO_SIGN:
+ case CRYPTO_CERT | CRYPTO_RESP:
+ vallen = ntohl(ep->vallen);
+ if (vallen == 8) {
+ strcpy(certname, sys_hostname);
+ } else if (vallen == 0 || vallen > MAXHOSTNAME) {
+ opcode |= CRYPTO_ERROR;
+ break;
+
+ } else {
+ memcpy(certname, ep->pkt, vallen);
+ certname[vallen] = '\0';
+ }
+ for (cp = cinfo; cp != NULL; cp = cp->link) {
+ if (cp->flags & CERT_PRIV)
+ continue;
+ if (strcmp(certname, cp->subject) == 0) {
+ len += crypto_send(fp, &cp->cert);
+ break;
+ }
+ }
+ if (cp == NULL)
+ opcode |= CRYPTO_ERROR;
+ break;
+
+ /*
+ * Send challenge in Schnorr (IFF) identity scheme.
+ */
+ case CRYPTO_IFF:
+ if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) {
+ opcode |= CRYPTO_ERROR;
+ break;
+ }
+ if ((rval = crypto_alice(peer, &vtemp)) == XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Send response in Schnorr (IFF) identity scheme.
+ */
+ case CRYPTO_IFF | CRYPTO_RESP:
+ if ((rval = crypto_bob(ep, &vtemp)) == XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Send challenge in Guillou-Quisquater (GQ) identity scheme.
+ */
+ case CRYPTO_GQ:
+ if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) {
+ opcode |= CRYPTO_ERROR;
+ break;
+ }
+ if ((rval = crypto_alice2(peer, &vtemp)) == XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Send response in Guillou-Quisquater (GQ) identity scheme.
+ */
+ case CRYPTO_GQ | CRYPTO_RESP:
+ if ((rval = crypto_bob2(ep, &vtemp)) == XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Send challenge in MV identity scheme.
+ */
+ case CRYPTO_MV:
+ if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) {
+ opcode |= CRYPTO_ERROR;
+ break;
+ }
+ if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Send response in MV identity scheme.
+ */
+ case CRYPTO_MV | CRYPTO_RESP:
+ if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Send certificate sign response. The integrity of the request
+ * certificate has already been verified on the receive side.
+ * Sign the response using the local server key. Use the
+ * filestamp from the request and use the timestamp as the
+ * current time. Light the error bit if the certificate is
+ * invalid or contains an unverified signature.
+ */
+ case CRYPTO_SIGN | CRYPTO_RESP:
+ if ((rval = cert_sign(ep, &vtemp)) == XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Send public key and signature. Use the values from the public
+ * key.
+ */
+ case CRYPTO_COOK:
+ len += crypto_send(fp, &pubkey);
+ break;
+
+ /*
+ * Encrypt and send cookie and signature. Light the error bit if
+ * anything goes wrong.
+ */
+ case CRYPTO_COOK | CRYPTO_RESP:
+ if ((opcode & 0xffff) < VALUE_LEN) {
+ opcode |= CRYPTO_ERROR;
+ break;
+ }
+ if (PKT_MODE(xpkt->li_vn_mode) == MODE_SERVER) {
+ tcookie = cookie;
+ } else {
+ if ((peer = findpeerbyassoc(associd)) == NULL) {
+ opcode |= CRYPTO_ERROR;
+ break;
+ }
+ tcookie = peer->pcookie;
+ }
+ if ((rval = crypto_encrypt(ep, &vtemp, &tcookie)) ==
+ XEVNT_OK)
+ len += crypto_send(fp, &vtemp);
+ value_free(&vtemp);
+ break;
+
+ /*
+ * Find peer and send autokey data and signature in broadcast
+ * server and symmetric modes. Use the values in the autokey
+ * structure. If no association is found, either the server has
+ * restarted with new associations or some perp has replayed an
+ * old message, in which case light the error bit.
+ */
+ case CRYPTO_AUTO | CRYPTO_RESP:
+ if ((peer = findpeerbyassoc(associd)) == NULL) {
+ opcode |= CRYPTO_ERROR;
+ break;
+ }
+ peer->flags &= ~FLAG_ASSOC;
+ len += crypto_send(fp, &peer->sndval);
+ break;
+
+ /*
+ * Send leapseconds table and signature. Use the values from the
+ * tai structure. If no table has been loaded, just send a
+ * request.
+ */
+ case CRYPTO_TAI:
+ case CRYPTO_TAI | CRYPTO_RESP:
+ if (crypto_flags & CRYPTO_FLAG_TAI)
+ len += crypto_send(fp, &tai_leap);
+ break;
+
+ /*
+ * Default - Fall through for requests; for unknown responses,
+ * flag as error.
+ */
+ default:
+ if (opcode & CRYPTO_RESP)
+ opcode |= CRYPTO_ERROR;
+ }
+
+ /*
+ * We ignore length/format errors and duplicates. Other errors
+ * are reported to the log and deny further service. To really
+ * persistent rascals we toss back a kiss-of-death grenade.
+ */
+ if (rval > XEVNT_TSP) {
+ opcode |= CRYPTO_ERROR;
+ sprintf(statstr, "error %x opcode %x", rval, opcode);
+ record_crypto_stats(srcadr_sin, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_xmit: %s\n", statstr);
+#endif
+ }
+
+ /*
+ * Round up the field length to a multiple of 8 bytes and save
+ * the request code and length.
+ */
+ len = ((len + 7) / 8) * 8;
+ fp->opcode = htonl((opcode & 0xffff0000) | len);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_xmit: ext offset %d len %u code %x assocID %d\n",
+ start, len, opcode>> 16, associd);
+#endif
+ return (len);
+}
+
+
+/*
+ * crypto_verify - parse and verify the extension field and value
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_LEN bad field format or length
+ * XEVNT_TSP bad timestamp
+ * XEVNT_FSP bad filestamp
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_SGL bad signature length
+ * XEVNT_SIG signature not verified
+ */
+static int
+crypto_verify(
+ struct exten *ep, /* extension pointer */
+ struct value *vp, /* value pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ EVP_PKEY *pkey; /* server public key */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* timestamp */
+ tstamp_t fstamp; /* filestamp */
+ u_int vallen; /* value length */
+ u_int siglen; /* signature length */
+ u_int opcode, len;
+ int rval;
+ int i;
+
+ /*
+ * We require valid opcode and field length, timestamp,
+ * filestamp, public key, digest, signature length and
+ * signature, where relevant. Note that preliminary length
+ * checks are done in the main loop.
+ */
+ len = ntohl(ep->opcode) & 0x0000ffff;
+ opcode = ntohl(ep->opcode) & 0xffff0000;
+
+ /*
+ * Check for valid operation code and protocol. The opcode must
+ * not have the error bit set. If a response, it must have a
+ * value header. If a request and does not contain a value
+ * header, no need for further checking.
+ */
+ if (opcode & CRYPTO_ERROR)
+ return (XEVNT_LEN);
+ if (opcode & CRYPTO_RESP) {
+ if (len < VALUE_LEN)
+ return (XEVNT_LEN);
+ } else {
+ if (len < VALUE_LEN)
+ return (XEVNT_OK);
+ }
+ /*
+ * We have a value header. Check for valid field lengths. The
+ * field length must be long enough to contain the value header,
+ * value and signature. If a request and a previous request of
+ * the same type is pending, discard the previous request. If a
+ * request but no signature, there is no need for further
+ * checking.
+ */
+ vallen = ntohl(ep->vallen);
+ if (len < ((VALUE_LEN + vallen + 3) / 4) * 4)
+ return (XEVNT_LEN);
+
+ i = (vallen + 3) / 4;
+ siglen = ntohl(ep->pkt[i++]);
+ if (len < VALUE_LEN + vallen + siglen)
+ return (XEVNT_LEN);
+
+ if (!(opcode & CRYPTO_RESP)) {
+ if (peer->cmmd != NULL) {
+ if ((opcode | CRYPTO_RESP) ==
+ (ntohl(peer->cmmd->opcode) & 0xffff0000)) {
+ free(peer->cmmd);
+ peer->cmmd = NULL;
+ } else {
+ return (XEVNT_LEN);
+ }
+ }
+ if (siglen == 0)
+ return (XEVNT_OK);
+ }
+
+ /*
+ * We have a signature. Check for valid timestamp and filestamp.
+ * The timestamp must not precede the filestamp. The timestamp
+ * and filestamp must not precede the corresponding values in
+ * the value structure. Once the autokey values have been
+ * installed, the timestamp must always be later than the
+ * corresponding value in the value structure. Duplicate
+ * timestamps are illegal once the cookie has been validated.
+ */
+ rval = XEVNT_OK;
+ if (crypto_flags & peer->crypto & CRYPTO_FLAG_PRIV)
+ pkey = sign_pkey;
+ else
+ pkey = peer->pkey;
+ tstamp = ntohl(ep->tstamp);
+ fstamp = ntohl(ep->fstamp);
+ if (tstamp == 0 || tstamp < fstamp) {
+ rval = XEVNT_TSP;
+ } else if (vp != NULL && (tstamp < ntohl(vp->tstamp) ||
+ (tstamp == ntohl(vp->tstamp) && (peer->crypto &
+ CRYPTO_FLAG_AUTO)))) {
+ rval = XEVNT_TSP;
+ } else if (vp != NULL && (tstamp < ntohl(vp->fstamp) || fstamp <
+ ntohl(vp->fstamp))) {
+ rval = XEVNT_FSP;
+
+ /*
+ * If a public key and digest is present, and if valid key
+ * length, check for valid signature. Note that the first valid
+ * signature lights the proventic bit.
+ */
+ } else if (pkey == NULL || peer->digest == NULL) {
+ /* fall through */
+ } else if (siglen != (u_int) EVP_PKEY_size(pkey)) {
+ rval = XEVNT_SGL;
+ } else {
+ EVP_VerifyInit(&ctx, peer->digest);
+ EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen +
+ 12);
+ if (EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i], siglen,
+ pkey)) {
+ if (peer->crypto & CRYPTO_FLAG_VRFY)
+ peer->crypto |= CRYPTO_FLAG_PROV;
+ } else {
+ rval = XEVNT_SIG;
+ }
+ }
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "crypto_recv: verify %x vallen %u siglen %u ts %u fs %u\n",
+ rval, vallen, siglen, tstamp, fstamp);
+#endif
+ return (rval);
+}
+
+
+/*
+ * crypto_encrypt - construct encrypted cookie and signature from
+ * extension field and cookie
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_CKY bad or missing cookie
+ */
+static int
+crypto_encrypt(
+ struct exten *ep, /* extension pointer */
+ struct value *vp, /* value pointer */
+ keyid_t *cookie /* server cookie */
+ )
+{
+ EVP_PKEY *pkey; /* public key */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ u_int32 temp32;
+ u_int len;
+ u_char *ptr;
+
+ /*
+ * Extract the public key from the request.
+ */
+ len = ntohl(ep->vallen);
+ ptr = (u_char *)ep->pkt;
+ pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, len);
+ if (pkey == NULL) {
+ msyslog(LOG_ERR, "crypto_encrypt %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Encrypt the cookie, encode in ASN.1 and sign.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = hostval.tstamp;
+ len = EVP_PKEY_size(pkey);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ temp32 = htonl(*cookie);
+ if (!RSA_public_encrypt(4, (u_char *)&temp32, vp->ptr,
+ pkey->pkey.rsa, RSA_PKCS1_OAEP_PADDING)) {
+ msyslog(LOG_ERR, "crypto_encrypt %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_free(pkey);
+ return (XEVNT_CKY);
+ }
+ EVP_PKEY_free(pkey);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_ident - construct extension field for identity scheme
+ *
+ * This routine determines which identity scheme is in use and
+ * constructs an extension field for that scheme.
+ */
+u_int
+crypto_ident(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ char filename[MAXFILENAME + 1];
+
+ /*
+ * If the server identity has already been verified, no further
+ * action is necessary. Otherwise, try to load the identity file
+ * of the certificate issuer. If the issuer file is not found,
+ * try the host file. If nothing found, declare a cryptobust.
+ * Note we can't get here unless the trusted certificate has
+ * been found and the CRYPTO_FLAG_VALID bit is set, so the
+ * certificate issuer is valid.
+ */
+ if (peer->crypto & CRYPTO_FLAG_VRFY)
+ return (0);
+
+ if (peer->ident_pkey != NULL)
+ EVP_PKEY_free(peer->ident_pkey);
+ if (peer->crypto & CRYPTO_FLAG_GQ) {
+ snprintf(filename, MAXFILENAME, "ntpkey_gq_%s",
+ peer->issuer);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_GQ);
+
+ snprintf(filename, MAXFILENAME, "ntpkey_gq_%s",
+ sys_hostname);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_GQ);
+ }
+ if (peer->crypto & CRYPTO_FLAG_IFF) {
+ snprintf(filename, MAXFILENAME, "ntpkey_iff_%s",
+ peer->issuer);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_IFF);
+
+ snprintf(filename, MAXFILENAME, "ntpkey_iff_%s",
+ sys_hostname);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_IFF);
+ }
+ if (peer->crypto & CRYPTO_FLAG_MV) {
+ snprintf(filename, MAXFILENAME, "ntpkey_mv_%s",
+ peer->issuer);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_MV);
+
+ snprintf(filename, MAXFILENAME, "ntpkey_mv_%s",
+ sys_hostname);
+ peer->ident_pkey = crypto_key(filename, &peer->fstamp);
+ if (peer->ident_pkey != NULL)
+ return (CRYPTO_MV);
+ }
+
+ /*
+ * No compatible identity scheme is available. Use the default
+ * TC scheme.
+ */
+ msyslog(LOG_INFO,
+ "crypto_ident: no compatible identity scheme found");
+ return (0);
+}
+
+
+/*
+ * crypto_args - construct extension field from arguments
+ *
+ * This routine creates an extension field with current timestamps and
+ * specified opcode, association ID and optional string. Note that the
+ * extension field is created here, but freed after the crypto_xmit()
+ * call in the protocol module.
+ *
+ * Returns extension field pointer (no errors).
+ */
+struct exten *
+crypto_args(
+ struct peer *peer, /* peer structure pointer */
+ u_int opcode, /* operation code */
+ char *str /* argument string */
+ )
+{
+ tstamp_t tstamp; /* NTP timestamp */
+ struct exten *ep; /* extension field pointer */
+ u_int len; /* extension field length */
+
+ tstamp = crypto_time();
+ len = sizeof(struct exten);
+ if (str != NULL)
+ len += strlen(str);
+ ep = emalloc(len);
+ memset(ep, 0, len);
+ ep->opcode = htonl(opcode + len);
+
+ /*
+ * If a response, send our ID; if a request, send the
+ * responder's ID.
+ */
+ if (opcode & CRYPTO_RESP)
+ ep->associd = htonl(peer->associd);
+ else
+ ep->associd = htonl(peer->assoc);
+ ep->tstamp = htonl(tstamp);
+ ep->fstamp = hostval.tstamp;
+ ep->vallen = 0;
+ if (str != NULL) {
+ ep->vallen = htonl(strlen(str));
+ memcpy((char *)ep->pkt, str, strlen(str));
+ } else {
+ ep->pkt[0] = peer->associd;
+ }
+ return (ep);
+}
+
+
+/*
+ * crypto_send - construct extension field from value components
+ *
+ * Returns extension field length. Note: it is not polite to send a
+ * nonempty signature with zero timestamp or a nonzero timestamp with
+ * empty signature, but these rules are not enforced here.
+ */
+u_int
+crypto_send(
+ struct exten *ep, /* extension field pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ u_int len, temp32;
+ int i;
+
+ /*
+ * Copy data. If the data field is empty or zero length, encode
+ * an empty value with length zero.
+ */
+ ep->tstamp = vp->tstamp;
+ ep->fstamp = vp->fstamp;
+ ep->vallen = vp->vallen;
+ len = 12;
+ temp32 = ntohl(vp->vallen);
+ if (temp32 > 0 && vp->ptr != NULL)
+ memcpy(ep->pkt, vp->ptr, temp32);
+
+ /*
+ * Copy signature. If the signature field is empty or zero
+ * length, encode an empty signature with length zero.
+ */
+ i = (temp32 + 3) / 4;
+ len += i * 4 + 4;
+ ep->pkt[i++] = vp->siglen;
+ temp32 = ntohl(vp->siglen);
+ if (temp32 > 0 && vp->sig != NULL)
+ memcpy(&ep->pkt[i], vp->sig, temp32);
+ len += temp32;
+ return (len);
+}
+
+
+/*
+ * crypto_update - compute new public value and sign extension fields
+ *
+ * This routine runs periodically, like once a day, and when something
+ * changes. It updates the timestamps on three value structures and one
+ * value structure list, then signs all the structures:
+ *
+ * hostval host name (not signed)
+ * pubkey public key
+ * cinfo certificate info/value list
+ * tai_leap leapseconds file
+ *
+ * Filestamps are proventicated data, so this routine is run only when
+ * the host has been synchronized to a proventicated source. Thus, the
+ * timestamp is proventicated, too, and can be used to deflect
+ * clogging attacks and even cook breakfast.
+ *
+ * Returns void (no errors)
+ */
+void
+crypto_update(void)
+{
+ EVP_MD_CTX ctx; /* message digest context */
+ struct cert_info *cp, *cpn, **zp; /* certificate info/value */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ tstamp_t tstamp; /* NTP timestamp */
+ u_int len;
+
+ if ((tstamp = crypto_time()) == 0)
+ return;
+ hostval.tstamp = htonl(tstamp);
+
+ /*
+ * Sign public key and timestamps. The filestamp is derived from
+ * the host key file extension from wherever the file was
+ * generated.
+ */
+ if (pubkey.vallen != 0) {
+ pubkey.tstamp = hostval.tstamp;
+ pubkey.siglen = 0;
+ if (pubkey.sig == NULL)
+ pubkey.sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&pubkey, 12);
+ EVP_SignUpdate(&ctx, pubkey.ptr, ntohl(pubkey.vallen));
+ if (EVP_SignFinal(&ctx, pubkey.sig, &len, sign_pkey))
+ pubkey.siglen = htonl(len);
+ }
+
+ /*
+ * Sign certificates and timestamps. The filestamp is derived
+ * from the certificate file extension from wherever the file
+ * was generated. At the same time expired certificates are
+ * expunged.
+ */
+ zp = &cinfo;
+ for (cp = cinfo; cp != NULL; cp = cpn) {
+ cpn = cp->link;
+ if (tstamp > cp->last) {
+ *zp = cpn;
+ cert_free(cp);
+ } else {
+ cp->cert.tstamp = hostval.tstamp;
+ cp->cert.siglen = 0;
+ if (cp->cert.sig == NULL)
+ cp->cert.sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&cp->cert, 12);
+ EVP_SignUpdate(&ctx, cp->cert.ptr,
+ ntohl(cp->cert.vallen));
+ if (EVP_SignFinal(&ctx, cp->cert.sig, &len,
+ sign_pkey))
+ cp->cert.siglen = htonl(len);
+ zp = &cp->link;
+ }
+ }
+
+ /*
+ * Sign leapseconds table and timestamps. The filestamp is
+ * derived from the leapsecond file extension from wherever the
+ * file was generated.
+ */
+ if (tai_leap.vallen != 0) {
+ tai_leap.tstamp = hostval.tstamp;
+ tai_leap.siglen = 0;
+ if (tai_leap.sig == NULL)
+ tai_leap.sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&tai_leap, 12);
+ EVP_SignUpdate(&ctx, tai_leap.ptr,
+ ntohl(tai_leap.vallen));
+ if (EVP_SignFinal(&ctx, tai_leap.sig, &len, sign_pkey))
+ tai_leap.siglen = htonl(len);
+ }
+ sprintf(statstr, "update ts %u", ntohl(hostval.tstamp));
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_update: %s\n", statstr);
+#endif
+}
+
+
+/*
+ * value_free - free value structure components.
+ *
+ * Returns void (no errors)
+ */
+void
+value_free(
+ struct value *vp /* value structure */
+ )
+{
+ if (vp->ptr != NULL)
+ free(vp->ptr);
+ if (vp->sig != NULL)
+ free(vp->sig);
+ memset(vp, 0, sizeof(struct value));
+}
+
+
+/*
+ * crypto_time - returns current NTP time in seconds.
+ */
+tstamp_t
+crypto_time()
+{
+ l_fp tstamp; /* NTP time */ L_CLR(&tstamp);
+
+ L_CLR(&tstamp);
+ if (sys_leap != LEAP_NOTINSYNC)
+ get_systime(&tstamp);
+ return (tstamp.l_ui);
+}
+
+
+/*
+ * asn2ntp - convert ASN1_TIME time structure to NTP time in seconds.
+ */
+u_long
+asn2ntp (
+ ASN1_TIME *asn1time /* pointer to ASN1_TIME structure */
+ )
+{
+ char *v; /* pointer to ASN1_TIME string */
+ struct tm tm; /* used to convert to NTP time */
+
+ /*
+ * Extract time string YYMMDDHHMMSSZ from ASN1 time structure.
+ * Note that the YY, MM, DD fields start with one, the HH, MM,
+ * SS fiels start with zero and the Z character should be 'Z'
+ * for UTC. Also note that years less than 50 map to years
+ * greater than 100. Dontcha love ASN.1? Better than MIL-188.
+ */
+ if (asn1time->length > 13)
+ return ((u_long)(~0)); /* We can't use -1 here. It's invalid */
+ v = (char *)asn1time->data;
+ tm.tm_year = (v[0] - '0') * 10 + v[1] - '0';
+ if (tm.tm_year < 50)
+ tm.tm_year += 100;
+ tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1;
+ tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0';
+ tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0';
+ tm.tm_min = (v[8] - '0') * 10 + v[9] - '0';
+ tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0';
+ tm.tm_wday = 0;
+ tm.tm_yday = 0;
+ tm.tm_isdst = 0;
+ return (timegm(&tm) + JAN_1970);
+}
+
+
+/*
+ * bigdig() - compute a BIGNUM MD5 hash of a BIGNUM number.
+ */
+static int
+bighash(
+ BIGNUM *bn, /* BIGNUM * from */
+ BIGNUM *bk /* BIGNUM * to */
+ )
+{
+ EVP_MD_CTX ctx; /* message digest context */
+ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */
+ u_char *ptr; /* a BIGNUM as binary string */
+ u_int len;
+
+ len = BN_num_bytes(bn);
+ ptr = emalloc(len);
+ BN_bn2bin(bn, ptr);
+ EVP_DigestInit(&ctx, EVP_md5());
+ EVP_DigestUpdate(&ctx, ptr, len);
+ EVP_DigestFinal(&ctx, dgst, &len);
+ BN_bin2bn(dgst, len, bk);
+ return (1);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines implement the Schnorr (IFF) identity scheme *
+ * *
+ ***********************************************************************
+ *
+ * The Schnorr (IFF) identity scheme is intended for use when
+ * the ntp-genkeys program does not generate the certificates used in
+ * the protocol and the group key cannot be conveyed in the certificate
+ * itself. For this purpose, new generations of IFF values must be
+ * securely transmitted to all members of the group before use. The
+ * scheme is self contained and independent of new generations of host
+ * keys, sign keys and certificates.
+ *
+ * The IFF identity scheme is based on DSA cryptography and algorithms
+ * described in Stinson p. 285. The IFF values hide in a DSA cuckoo
+ * structure, but only the primes and generator are used. The p is a
+ * 512-bit prime, q a 160-bit prime that divides p - 1 and is a qth root
+ * of 1 mod p; that is, g^q = 1 mod p. The TA rolls primvate random
+ * group key b disguised as a DSA structure member, then computes public
+ * key g^(q - b). These values are shared only among group members and
+ * never revealed in messages. Alice challenges Bob to confirm identity
+ * using the protocol described below.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Both Alice and Bob have the public primes
+ * p, q and generator g. The TA gives private key b to Bob and public
+ * key v = g^(q - a) mod p to Alice.
+ *
+ * Alice rolls new random challenge r and sends to Bob in the IFF
+ * request message. Bob rolls new random k, then computes y = k + b r
+ * mod q and x = g^k mod p and sends (y, hash(x)) to Alice in the
+ * response message. Besides making the response shorter, the hash makes
+ * it effectivey impossible for an intruder to solve for b by observing
+ * a number of these messages.
+ *
+ * Alice receives the response and computes g^y v^r mod p. After a bit
+ * of algebra, this simplifies to g^k. If the hash of this result
+ * matches hash(x), Alice knows that Bob has the group key b. The signed
+ * response binds this knowledge to Bob's private key and the public key
+ * previously received in his certificate.
+ *
+ * crypto_alice - construct Alice's challenge in IFF scheme
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_ID bad or missing identity parameters
+ */
+static int
+crypto_alice(
+ struct peer *peer, /* peer pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* IFF parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp;
+ u_int len;
+
+ /*
+ * The identity parameters must have correct format and content.
+ */
+ if (peer->ident_pkey == NULL)
+ return (XEVNT_ID);
+ if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_alice: defective key");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Roll new random r (0 < r < q). The OpenSSL library has a bug
+ * omitting BN_rand_range, so we have to do it the hard way.
+ */
+ bctx = BN_CTX_new();
+ len = BN_num_bytes(dsa->q);
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = BN_new();
+ BN_rand(peer->iffval, len * 8, -1, 1); /* r */
+ BN_mod(peer->iffval, peer->iffval, dsa->q, bctx);
+ BN_CTX_free(bctx);
+
+ /*
+ * Sign and send to Bob. The filestamp is from the local file.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(peer->fstamp);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ BN_bn2bin(peer->iffval, vp->ptr);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ */
+static int
+crypto_bob(
+ struct exten *ep, /* extension pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* IFF parameters */
+ DSA_SIG *sdsa; /* DSA signature context fake */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ BIGNUM *bn, *bk, *r;
+ u_char *ptr;
+ u_int len;
+
+ /*
+ * If the IFF parameters are not valid, something awful
+ * happened or we are being tormented.
+ */
+ if (!(crypto_flags & CRYPTO_FLAG_IFF)) {
+ msyslog(LOG_INFO, "crypto_bob: scheme unavailable");
+ return (XEVNT_PUB);
+ }
+ dsa = iffpar_pkey->pkey.dsa;
+
+ /*
+ * Extract r from the challenge.
+ */
+ len = ntohl(ep->vallen);
+ if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+ msyslog(LOG_ERR, "crypto_bob %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Bob rolls random k (0 < k < q), computes y = k + b r mod q
+ * and x = g^k mod p, then sends (y, hash(x)) to Alice.
+ */
+ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new();
+ sdsa = DSA_SIG_new();
+ BN_rand(bk, len * 8, -1, 1); /* k */
+ BN_mod_mul(bn, dsa->priv_key, r, dsa->q, bctx); /* b r mod q */
+ BN_add(bn, bn, bk);
+ BN_mod(bn, bn, dsa->q, bctx); /* k + b r mod q */
+ sdsa->r = BN_dup(bn);
+ BN_mod_exp(bk, dsa->g, bk, dsa->p, bctx); /* g^k mod p */
+ bighash(bk, bk);
+ sdsa->s = BN_dup(bk);
+ BN_CTX_free(bctx);
+ BN_free(r); BN_free(bn); BN_free(bk);
+
+ /*
+ * Encode the values in ASN.1 and sign.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(if_fstamp);
+ len = i2d_DSA_SIG(sdsa, NULL);
+ if (len <= 0) {
+ msyslog(LOG_ERR, "crypto_bob %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ DSA_SIG_free(sdsa);
+ return (XEVNT_PUB);
+ }
+ vp->vallen = htonl(len);
+ ptr = emalloc(len);
+ vp->ptr = ptr;
+ i2d_DSA_SIG(sdsa, &ptr);
+ DSA_SIG_free(sdsa);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_iff - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_FSP bad filestamp
+ * XEVNT_ID bad or missing identity parameters
+ */
+int
+crypto_iff(
+ struct exten *ep, /* extension pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ DSA *dsa; /* IFF parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ DSA_SIG *sdsa; /* DSA parameters */
+ BIGNUM *bn, *bk;
+ u_int len;
+ const u_char *ptr;
+ int temp;
+
+ /*
+ * If the IFF parameters are not valid or no challenge was sent,
+ * something awful happened or we are being tormented.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_INFO, "crypto_iff: scheme unavailable");
+ return (XEVNT_PUB);
+ }
+ if (ntohl(ep->fstamp) != peer->fstamp) {
+ msyslog(LOG_INFO, "crypto_iff: invalid filestamp %u",
+ ntohl(ep->fstamp));
+ return (XEVNT_FSP);
+ }
+ if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_iff: defective key");
+ return (XEVNT_PUB);
+ }
+ if (peer->iffval == NULL) {
+ msyslog(LOG_INFO, "crypto_iff: missing challenge");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Extract the k + b r and g^k values from the response.
+ */
+ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new();
+ len = ntohl(ep->vallen);
+ ptr = (const u_char *)ep->pkt;
+ if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) {
+ msyslog(LOG_ERR, "crypto_iff %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Compute g^(k + b r) g^(q - b)r mod p.
+ */
+ BN_mod_exp(bn, dsa->pub_key, peer->iffval, dsa->p, bctx);
+ BN_mod_exp(bk, dsa->g, sdsa->r, dsa->p, bctx);
+ BN_mod_mul(bn, bn, bk, dsa->p, bctx);
+
+ /*
+ * Verify the hash of the result matches hash(x).
+ */
+ bighash(bn, bn);
+ temp = BN_cmp(bn, sdsa->s);
+ BN_free(bn); BN_free(bk); BN_CTX_free(bctx);
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+ DSA_SIG_free(sdsa);
+ if (temp == 0)
+ return (XEVNT_OK);
+ else
+ return (XEVNT_ID);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines implement the Guillou-Quisquater (GQ) *
+ * identity scheme *
+ * *
+ ***********************************************************************
+ *
+ * The Guillou-Quisquater (GQ) identity scheme is intended for use when
+ * the ntp-genkeys program generates the certificates used in the
+ * protocol and the group key can be conveyed in a certificate extension
+ * field. The scheme is self contained and independent of new
+ * generations of host keys, sign keys and certificates.
+ *
+ * The GQ identity scheme is based on RSA cryptography and algorithms
+ * described in Stinson p. 300 (with errors). The GQ values hide in a
+ * RSA cuckoo structure, but only the modulus is used. The 512-bit
+ * public modulus is n = p q, where p and q are secret large primes. The
+ * TA rolls random group key b disguised as a RSA structure member.
+ * Except for the public key, these values are shared only among group
+ * members and never revealed in messages.
+ *
+ * When rolling new certificates, Bob recomputes the private and
+ * public keys. The private key u is a random roll, while the public key
+ * is the inverse obscured by the group key v = (u^-1)^b. These values
+ * replace the private and public keys normally generated by the RSA
+ * scheme. Alice challenges Bob to confirm identity using the protocol
+ * described below.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Both Alice and Bob have the same modulus n
+ * and some random b as the group key. These values are computed and
+ * distributed in advance via secret means, although only the group key
+ * b is truly secret. Each has a private random private key u and public
+ * key (u^-1)^b, although not necessarily the same ones. Bob and Alice
+ * can regenerate the key pair from time to time without affecting
+ * operations. The public key is conveyed on the certificate in an
+ * extension field; the private key is never revealed.
+ *
+ * Alice rolls new random challenge r and sends to Bob in the GQ
+ * request message. Bob rolls new random k, then computes y = k u^r mod
+ * n and x = k^b mod n and sends (y, hash(x)) to Alice in the response
+ * message. Besides making the response shorter, the hash makes it
+ * effectivey impossible for an intruder to solve for b by observing
+ * a number of these messages.
+ *
+ * Alice receives the response and computes y^b v^r mod n. After a bit
+ * of algebra, this simplifies to k^b. If the hash of this result
+ * matches hash(x), Alice knows that Bob has the group key b. The signed
+ * response binds this knowledge to Bob's private key and the public key
+ * previously received in his certificate.
+ *
+ * crypto_alice2 - construct Alice's challenge in GQ scheme
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_ID bad or missing identity parameters
+ */
+static int
+crypto_alice2(
+ struct peer *peer, /* peer pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ RSA *rsa; /* GQ parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp;
+ u_int len;
+
+ /*
+ * The identity parameters must have correct format and content.
+ */
+ if (peer->ident_pkey == NULL)
+ return (XEVNT_ID);
+ if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_alice2: defective key");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Roll new random r (0 < r < n). The OpenSSL library has a bug
+ * omitting BN_rand_range, so we have to do it the hard way.
+ */
+ bctx = BN_CTX_new();
+ len = BN_num_bytes(rsa->n);
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = BN_new();
+ BN_rand(peer->iffval, len * 8, -1, 1); /* r mod n */
+ BN_mod(peer->iffval, peer->iffval, rsa->n, bctx);
+ BN_CTX_free(bctx);
+
+ /*
+ * Sign and send to Bob. The filestamp is from the local file.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(peer->fstamp);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ BN_bn2bin(peer->iffval, vp->ptr);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob2 - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ */
+static int
+crypto_bob2(
+ struct exten *ep, /* extension pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ RSA *rsa; /* GQ parameters */
+ DSA_SIG *sdsa; /* DSA parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ BIGNUM *r, *k, *g, *y;
+ u_char *ptr;
+ u_int len;
+
+ /*
+ * If the GQ parameters are not valid, something awful
+ * happened or we are being tormented.
+ */
+ if (!(crypto_flags & CRYPTO_FLAG_GQ)) {
+ msyslog(LOG_INFO, "crypto_bob2: scheme unavailable");
+ return (XEVNT_PUB);
+ }
+ rsa = gqpar_pkey->pkey.rsa;
+
+ /*
+ * Extract r from the challenge.
+ */
+ len = ntohl(ep->vallen);
+ if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+ msyslog(LOG_ERR, "crypto_bob2 %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Bob rolls random k (0 < k < n), computes y = k u^r mod n and
+ * x = k^b mod n, then sends (y, hash(x)) to Alice.
+ */
+ bctx = BN_CTX_new(); k = BN_new(); g = BN_new(); y = BN_new();
+ sdsa = DSA_SIG_new();
+ BN_rand(k, len * 8, -1, 1); /* k */
+ BN_mod(k, k, rsa->n, bctx);
+ BN_mod_exp(y, rsa->p, r, rsa->n, bctx); /* u^r mod n */
+ BN_mod_mul(y, k, y, rsa->n, bctx); /* k u^r mod n */
+ sdsa->r = BN_dup(y);
+ BN_mod_exp(g, k, rsa->e, rsa->n, bctx); /* k^b mod n */
+ bighash(g, g);
+ sdsa->s = BN_dup(g);
+ BN_CTX_free(bctx);
+ BN_free(r); BN_free(k); BN_free(g); BN_free(y);
+
+ /*
+ * Encode the values in ASN.1 and sign.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(gq_fstamp);
+ len = i2d_DSA_SIG(sdsa, NULL);
+ if (len <= 0) {
+ msyslog(LOG_ERR, "crypto_bob2 %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ DSA_SIG_free(sdsa);
+ return (XEVNT_PUB);
+ }
+ vp->vallen = htonl(len);
+ ptr = emalloc(len);
+ vp->ptr = ptr;
+ i2d_DSA_SIG(sdsa, &ptr);
+ DSA_SIG_free(sdsa);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_gq - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_FSP bad filestamp
+ * XEVNT_ID bad or missing identity parameters
+ */
+int
+crypto_gq(
+ struct exten *ep, /* extension pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ RSA *rsa; /* GQ parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ DSA_SIG *sdsa; /* RSA signature context fake */
+ BIGNUM *y, *v;
+ const u_char *ptr;
+ u_int len;
+ int temp;
+
+ /*
+ * If the GQ parameters are not valid or no challenge was sent,
+ * something awful happened or we are being tormented.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_INFO, "crypto_gq: scheme unavailable");
+ return (XEVNT_PUB);
+ }
+ if (ntohl(ep->fstamp) != peer->fstamp) {
+ msyslog(LOG_INFO, "crypto_gq: invalid filestamp %u",
+ ntohl(ep->fstamp));
+ return (XEVNT_FSP);
+ }
+ if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_gq: defective key");
+ return (XEVNT_PUB);
+ }
+ if (peer->iffval == NULL) {
+ msyslog(LOG_INFO, "crypto_gq: missing challenge");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Extract the y = k u^r and hash(x = k^b) values from the
+ * response.
+ */
+ bctx = BN_CTX_new(); y = BN_new(); v = BN_new();
+ len = ntohl(ep->vallen);
+ ptr = (const u_char *)ep->pkt;
+ if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) {
+ msyslog(LOG_ERR, "crypto_gq %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Compute v^r y^b mod n.
+ */
+ BN_mod_exp(v, peer->grpkey, peer->iffval, rsa->n, bctx);
+ /* v^r mod n */
+ BN_mod_exp(y, sdsa->r, rsa->e, rsa->n, bctx); /* y^b mod n */
+ BN_mod_mul(y, v, y, rsa->n, bctx); /* v^r y^b mod n */
+
+ /*
+ * Verify the hash of the result matches hash(x).
+ */
+ bighash(y, y);
+ temp = BN_cmp(y, sdsa->s);
+ BN_CTX_free(bctx); BN_free(y); BN_free(v);
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+ DSA_SIG_free(sdsa);
+ if (temp == 0)
+ return (XEVNT_OK);
+ else
+ return (XEVNT_ID);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines implement the Mu-Varadharajan (MV) identity *
+ * scheme *
+ * *
+ ***********************************************************************
+ */
+/*
+ * The Mu-Varadharajan (MV) cryptosystem was originally intended when
+ * servers broadcast messages to clients, but clients never send
+ * messages to servers. There is one encryption key for the server and a
+ * separate decryption key for each client. It operated something like a
+ * pay-per-view satellite broadcasting system where the session key is
+ * encrypted by the broadcaster and the decryption keys are held in a
+ * tamperproof set-top box.
+ *
+ * The MV parameters and private encryption key hide in a DSA cuckoo
+ * structure which uses the same parameters, but generated in a
+ * different way. The values are used in an encryption scheme similar to
+ * El Gamal cryptography and a polynomial formed from the expansion of
+ * product terms (x - x[j]), as described in Mu, Y., and V.
+ * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001,
+ * 223-231. The paper has significant errors and serious omissions.
+ *
+ * Let q be the product of n distinct primes s'[j] (j = 1...n), where
+ * each s'[j] has m significant bits. Let p be a prime p = 2 * q + 1, so
+ * that q and each s'[j] divide p - 1 and p has M = n * m + 1
+ * significant bits. The elements x mod q of Zq with the elements 2 and
+ * the primes removed form a field Zq* valid for polynomial arithetic.
+ * Let g be a generator of Zp; that is, gcd(g, p - 1) = 1 and g^q = 1
+ * mod p. We expect M to be in the 500-bit range and n relatively small,
+ * like 25, so the likelihood of a randomly generated element of x mod q
+ * of Zq colliding with a factor of p - 1 is very small and can be
+ * avoided. Associated with each s'[j] is an element s[j] such that s[j]
+ * s'[j] = s'[j] mod q. We find s[j] as the quotient (q + s'[j]) /
+ * s'[j]. These are the parameters of the scheme and they are expensive
+ * to compute.
+ *
+ * We set up an instance of the scheme as follows. A set of random
+ * values x[j] mod q (j = 1...n), are generated as the zeros of a
+ * polynomial of order n. The product terms (x - x[j]) are expanded to
+ * form coefficients a[i] mod q (i = 0...n) in powers of x. These are
+ * used as exponents of the generator g mod p to generate the private
+ * encryption key A. The pair (gbar, ghat) of public server keys and the
+ * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used
+ * to construct the decryption keys. The devil is in the details.
+ *
+ * The distinguishing characteristic of this scheme is the capability to
+ * revoke keys. Included in the calculation of E, gbar and ghat is the
+ * product s = prod(s'[j]) (j = 1...n) above. If the factor s'[j] is
+ * subsequently removed from the product and E, gbar and ghat
+ * recomputed, the jth client will no longer be able to compute E^-1 and
+ * thus unable to decrypt the block.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Bob has the server values (p, A, q, gbar,
+ * ghat) and Alice the client values (p, xbar, xhat).
+ *
+ * Alice rolls new random challenge r (0 < r < p) and sends to Bob in
+ * the MV request message. Bob rolls new random k (0 < k < q), encrypts
+ * y = A^k mod p (a permutation) and sends (hash(y), gbar^k, ghat^k) to
+ * Alice.
+ *
+ * Alice receives the response and computes the decryption key (the
+ * inverse permutation) from previously obtained (xbar, xhat) and
+ * (gbar^k, ghat^k) in the message. She computes the inverse, which is
+ * unique by reasons explained in the ntp-keygen.c program sources. If
+ * the hash of this result matches hash(y), Alice knows that Bob has the
+ * group key b. The signed response binds this knowledge to Bob's
+ * private key and the public key previously received in his
+ * certificate.
+ *
+ * crypto_alice3 - construct Alice's challenge in MV scheme
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_ID bad or missing identity parameters
+ */
+static int
+crypto_alice3(
+ struct peer *peer, /* peer pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* MV parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp;
+ u_int len;
+
+ /*
+ * The identity parameters must have correct format and content.
+ */
+ if (peer->ident_pkey == NULL)
+ return (XEVNT_ID);
+ if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_alice3: defective key");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Roll new random r (0 < r < q). The OpenSSL library has a bug
+ * omitting BN_rand_range, so we have to do it the hard way.
+ */
+ bctx = BN_CTX_new();
+ len = BN_num_bytes(dsa->p);
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ peer->iffval = BN_new();
+ BN_rand(peer->iffval, len * 8, -1, 1); /* r */
+ BN_mod(peer->iffval, peer->iffval, dsa->p, bctx);
+ BN_CTX_free(bctx);
+
+ /*
+ * Sign and send to Bob. The filestamp is from the local file.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(peer->fstamp);
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ BN_bn2bin(peer->iffval, vp->ptr);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob3 - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ */
+static int
+crypto_bob3(
+ struct exten *ep, /* extension pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ DSA *dsa; /* MV parameters */
+ DSA *sdsa; /* DSA signature context fake */
+ BN_CTX *bctx; /* BIGNUM context */
+ EVP_MD_CTX ctx; /* signature context */
+ tstamp_t tstamp; /* NTP timestamp */
+ BIGNUM *r, *k, *u;
+ u_char *ptr;
+ u_int len;
+
+ /*
+ * If the MV parameters are not valid, something awful
+ * happened or we are being tormented.
+ */
+ if (!(crypto_flags & CRYPTO_FLAG_MV)) {
+ msyslog(LOG_INFO, "crypto_bob3: scheme unavailable");
+ return (XEVNT_PUB);
+ }
+ dsa = mvpar_pkey->pkey.dsa;
+
+ /*
+ * Extract r from the challenge.
+ */
+ len = ntohl(ep->vallen);
+ if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+ msyslog(LOG_ERR, "crypto_bob3 %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Bob rolls random k (0 < k < q), making sure it is not a
+ * factor of q. He then computes y = A^k r and sends (hash(y),
+ * gbar^k, ghat^k) to Alice.
+ */
+ bctx = BN_CTX_new(); k = BN_new(); u = BN_new();
+ sdsa = DSA_new();
+ sdsa->p = BN_new(); sdsa->q = BN_new(); sdsa->g = BN_new();
+ while (1) {
+ BN_rand(k, BN_num_bits(dsa->q), 0, 0);
+ BN_mod(k, k, dsa->q, bctx);
+ BN_gcd(u, k, dsa->q, bctx);
+ if (BN_is_one(u))
+ break;
+ }
+ BN_mod_exp(u, dsa->g, k, dsa->p, bctx); /* A r */
+ BN_mod_mul(u, u, r, dsa->p, bctx);
+ bighash(u, sdsa->p);
+ BN_mod_exp(sdsa->q, dsa->priv_key, k, dsa->p, bctx); /* gbar */
+ BN_mod_exp(sdsa->g, dsa->pub_key, k, dsa->p, bctx); /* ghat */
+ BN_CTX_free(bctx); BN_free(k); BN_free(r); BN_free(u);
+
+ /*
+ * Encode the values in ASN.1 and sign.
+ */
+ tstamp = crypto_time();
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = htonl(mv_fstamp);
+ len = i2d_DSAparams(sdsa, NULL);
+ if (len <= 0) {
+ msyslog(LOG_ERR, "crypto_bob3 %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ DSA_free(sdsa);
+ return (XEVNT_PUB);
+ }
+ vp->vallen = htonl(len);
+ ptr = emalloc(len);
+ vp->ptr = ptr;
+ i2d_DSAparams(sdsa, &ptr);
+ DSA_free(sdsa);
+ vp->siglen = 0;
+ if (tstamp == 0)
+ return (XEVNT_OK);
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_mv - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_FSP bad filestamp
+ * XEVNT_ID bad or missing identity parameters
+ */
+int
+crypto_mv(
+ struct exten *ep, /* extension pointer */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ DSA *dsa; /* MV parameters */
+ DSA *sdsa; /* DSA parameters */
+ BN_CTX *bctx; /* BIGNUM context */
+ BIGNUM *k, *u, *v;
+ u_int len;
+ const u_char *ptr;
+ int temp;
+
+ /*
+ * If the MV parameters are not valid or no challenge was sent,
+ * something awful happened or we are being tormented.
+ */
+ if (peer->ident_pkey == NULL) {
+ msyslog(LOG_INFO, "crypto_mv: scheme unavailable");
+ return (XEVNT_PUB);
+ }
+ if (ntohl(ep->fstamp) != peer->fstamp) {
+ msyslog(LOG_INFO, "crypto_mv: invalid filestamp %u",
+ ntohl(ep->fstamp));
+ return (XEVNT_FSP);
+ }
+ if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+ msyslog(LOG_INFO, "crypto_mv: defective key");
+ return (XEVNT_PUB);
+ }
+ if (peer->iffval == NULL) {
+ msyslog(LOG_INFO, "crypto_mv: missing challenge");
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Extract the (hash(y), gbar, ghat) values from the response.
+ */
+ bctx = BN_CTX_new(); k = BN_new(); u = BN_new(); v = BN_new();
+ len = ntohl(ep->vallen);
+ ptr = (const u_char *)ep->pkt;
+ if ((sdsa = d2i_DSAparams(NULL, &ptr, len)) == NULL) {
+ msyslog(LOG_ERR, "crypto_mv %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Compute (gbar^xhat ghat^xbar)^-1 mod p.
+ */
+ BN_mod_exp(u, sdsa->q, dsa->pub_key, dsa->p, bctx);
+ BN_mod_exp(v, sdsa->g, dsa->priv_key, dsa->p, bctx);
+ BN_mod_mul(u, u, v, dsa->p, bctx);
+ BN_mod_inverse(u, u, dsa->p, bctx);
+ BN_mod_mul(v, u, peer->iffval, dsa->p, bctx);
+
+ /*
+ * The result should match the hash of r mod p.
+ */
+ bighash(v, v);
+ temp = BN_cmp(v, sdsa->p);
+ BN_CTX_free(bctx); BN_free(k); BN_free(u); BN_free(v);
+ BN_free(peer->iffval);
+ peer->iffval = NULL;
+ DSA_free(sdsa);
+ if (temp == 0)
+ return (XEVNT_OK);
+ else
+ return (XEVNT_ID);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines are used to manipulate certificates *
+ * *
+ ***********************************************************************
+ */
+/*
+ * cert_parse - parse x509 certificate and create info/value structures.
+ *
+ * The server certificate includes the version number, issuer name,
+ * subject name, public key and valid date interval. If the issuer name
+ * is the same as the subject name, the certificate is self signed and
+ * valid only if the server is configured as trustable. If the names are
+ * different, another issuer has signed the server certificate and
+ * vouched for it. In this case the server certificate is valid if
+ * verified by the issuer public key.
+ *
+ * Returns certificate info/value pointer if valid, NULL if not.
+ */
+struct cert_info * /* certificate information structure */
+cert_parse(
+ u_char *asn1cert, /* X509 certificate */
+ u_int len, /* certificate length */
+ tstamp_t fstamp /* filestamp */
+ )
+{
+ X509 *cert; /* X509 certificate */
+ X509_EXTENSION *ext; /* X509v3 extension */
+ struct cert_info *ret; /* certificate info/value */
+ BIO *bp;
+ X509V3_EXT_METHOD *method;
+ char pathbuf[MAXFILENAME];
+ u_char *uptr;
+ char *ptr;
+ int temp, cnt, i;
+
+ /*
+ * Decode ASN.1 objects and construct certificate structure.
+ */
+ uptr = asn1cert;
+ if ((cert = d2i_X509(NULL, &uptr, len)) == NULL) {
+ msyslog(LOG_ERR, "cert_parse %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (NULL);
+ }
+
+ /*
+ * Extract version, subject name and public key.
+ */
+ ret = emalloc(sizeof(struct cert_info));
+ memset(ret, 0, sizeof(struct cert_info));
+ if ((ret->pkey = X509_get_pubkey(cert)) == NULL) {
+ msyslog(LOG_ERR, "cert_parse %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ ret->version = X509_get_version(cert);
+ X509_NAME_oneline(X509_get_subject_name(cert), pathbuf,
+ MAXFILENAME - 1);
+ ptr = strstr(pathbuf, "CN=");
+ if (ptr == NULL) {
+ msyslog(LOG_INFO, "cert_parse: invalid subject %s",
+ pathbuf);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ ret->subject = emalloc(strlen(ptr) + 1);
+ strcpy(ret->subject, ptr + 3);
+
+ /*
+ * Extract remaining objects. Note that the NTP serial number is
+ * the NTP seconds at the time of signing, but this might not be
+ * the case for other authority. We don't bother to check the
+ * objects at this time, since the real crunch can happen only
+ * when the time is valid but not yet certificated.
+ */
+ ret->nid = OBJ_obj2nid(cert->cert_info->signature->algorithm);
+ ret->digest = (const EVP_MD *)EVP_get_digestbynid(ret->nid);
+ ret->serial =
+ (u_long)ASN1_INTEGER_get(X509_get_serialNumber(cert));
+ X509_NAME_oneline(X509_get_issuer_name(cert), pathbuf,
+ MAXFILENAME);
+ if ((ptr = strstr(pathbuf, "CN=")) == NULL) {
+ msyslog(LOG_INFO, "cert_parse: invalid issuer %s",
+ pathbuf);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ ret->issuer = emalloc(strlen(ptr) + 1);
+ strcpy(ret->issuer, ptr + 3);
+ ret->first = asn2ntp(X509_get_notBefore(cert));
+ ret->last = asn2ntp(X509_get_notAfter(cert));
+
+ /*
+ * Extract extension fields. These are ad hoc ripoffs of
+ * currently assigned functions and will certainly be changed
+ * before prime time.
+ */
+ cnt = X509_get_ext_count(cert);
+ for (i = 0; i < cnt; i++) {
+ ext = X509_get_ext(cert, i);
+ method = X509V3_EXT_get(ext);
+ temp = OBJ_obj2nid(ext->object);
+ switch (temp) {
+
+ /*
+ * If a key_usage field is present, we decode whether
+ * this is a trusted or private certificate. This is
+ * dorky; all we want is to compare NIDs, but OpenSSL
+ * insists on BIO text strings.
+ */
+ case NID_ext_key_usage:
+ bp = BIO_new(BIO_s_mem());
+ X509V3_EXT_print(bp, ext, 0, 0);
+ BIO_gets(bp, pathbuf, MAXFILENAME);
+ BIO_free(bp);
+#if DEBUG
+ if (debug)
+ printf("cert_parse: %s: %s\n",
+ OBJ_nid2ln(temp), pathbuf);
+#endif
+ if (strcmp(pathbuf, "Trust Root") == 0)
+ ret->flags |= CERT_TRUST;
+ else if (strcmp(pathbuf, "Private") == 0)
+ ret->flags |= CERT_PRIV;
+ break;
+
+ /*
+ * If a NID_subject_key_identifier field is present, it
+ * contains the GQ public key.
+ */
+ case NID_subject_key_identifier:
+ ret->grplen = ext->value->length - 2;
+ ret->grpkey = emalloc(ret->grplen);
+ memcpy(ret->grpkey, &ext->value->data[2],
+ ret->grplen);
+ break;
+ }
+ }
+
+ /*
+ * If certificate is self signed, verify signature.
+ */
+ if (strcmp(ret->subject, ret->issuer) == 0) {
+ if (!X509_verify(cert, ret->pkey)) {
+ msyslog(LOG_INFO,
+ "cert_parse: invalid signature not verified %s",
+ pathbuf);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+ }
+
+ /*
+ * Verify certificate valid times. Note that certificates cannot
+ * be retroactive.
+ */
+ if (ret->first > ret->last || ret->first < fstamp) {
+ msyslog(LOG_INFO,
+ "cert_parse: expired %s",
+ ret->subject);
+ cert_free(ret);
+ X509_free(cert);
+ return (NULL);
+ }
+
+ /*
+ * Build the value structure to sign and send later.
+ */
+ ret->cert.fstamp = htonl(fstamp);
+ ret->cert.vallen = htonl(len);
+ ret->cert.ptr = emalloc(len);
+ memcpy(ret->cert.ptr, asn1cert, len);
+#ifdef DEBUG
+ if (debug > 1)
+ X509_print_fp(stdout, cert);
+#endif
+ X509_free(cert);
+ return (ret);
+}
+
+
+/*
+ * cert_sign - sign x509 certificate and update value structure.
+ *
+ * The certificate request is a copy of the client certificate, which
+ * includes the version number, subject name and public key of the
+ * client. The resulting certificate includes these values plus the
+ * serial number, issuer name and validity interval of the server. The
+ * validity interval extends from the current time to the same time one
+ * year hence. For NTP purposes, it is convenient to use the NTP seconds
+ * of the current time as the serial number.
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PUB bad or missing public key
+ * XEVNT_CRT bad or missing certificate
+ * XEVNT_VFY certificate not verified
+ */
+static int
+cert_sign(
+ struct exten *ep, /* extension field pointer */
+ struct value *vp /* value pointer */
+ )
+{
+ X509 *req; /* X509 certificate request */
+ X509 *cert; /* X509 certificate */
+ X509_EXTENSION *ext; /* certificate extension */
+ ASN1_INTEGER *serial; /* serial number */
+ X509_NAME *subj; /* distinguished (common) name */
+ EVP_PKEY *pkey; /* public key */
+ EVP_MD_CTX ctx; /* message digest context */
+ tstamp_t tstamp; /* NTP timestamp */
+ u_int len;
+ u_char *ptr;
+ int i, temp;
+
+ /*
+ * Decode ASN.1 objects and construct certificate structure.
+ */
+ tstamp = crypto_time();
+ if (tstamp == 0)
+ return (XEVNT_TSP);
+
+ ptr = (u_char *)ep->pkt;
+ if ((req = d2i_X509(NULL, &ptr, ntohl(ep->vallen))) == NULL) {
+ msyslog(LOG_ERR, "cert_sign %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (XEVNT_CRT);
+ }
+ /*
+ * Extract public key and check for errors.
+ */
+ if ((pkey = X509_get_pubkey(req)) == NULL) {
+ msyslog(LOG_ERR, "cert_sign %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ X509_free(req);
+ return (XEVNT_PUB);
+ }
+
+ /*
+ * Generate X509 certificate signed by this server. For this
+ * prupose the issuer name is the server name. Also copy any
+ * extensions that might be present.
+ */
+ cert = X509_new();
+ X509_set_version(cert, X509_get_version(req));
+ serial = ASN1_INTEGER_new();
+ ASN1_INTEGER_set(serial, tstamp);
+ X509_set_serialNumber(cert, serial);
+ X509_gmtime_adj(X509_get_notBefore(cert), 0L);
+ X509_gmtime_adj(X509_get_notAfter(cert), YEAR);
+ subj = X509_get_issuer_name(cert);
+ X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC,
+ (unsigned char *) sys_hostname, strlen(sys_hostname), -1, 0);
+ subj = X509_get_subject_name(req);
+ X509_set_subject_name(cert, subj);
+ X509_set_pubkey(cert, pkey);
+ ext = X509_get_ext(req, 0);
+ temp = X509_get_ext_count(req);
+ for (i = 0; i < temp; i++) {
+ ext = X509_get_ext(req, i);
+ X509_add_ext(cert, ext, -1);
+ }
+ X509_free(req);
+
+ /*
+ * Sign and verify the certificate.
+ */
+ X509_sign(cert, sign_pkey, sign_digest);
+ if (!X509_verify(cert, sign_pkey)) {
+ printf("cert_sign\n%s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ X509_free(cert);
+ return (XEVNT_VFY);
+ }
+ len = i2d_X509(cert, NULL);
+
+ /*
+ * Build and sign the value structure. We have to sign it here,
+ * since the response has to be returned right away. This is a
+ * clogging hazard.
+ */
+ memset(vp, 0, sizeof(struct value));
+ vp->tstamp = htonl(tstamp);
+ vp->fstamp = ep->fstamp;
+ vp->vallen = htonl(len);
+ vp->ptr = emalloc(len);
+ ptr = vp->ptr;
+ i2d_X509(cert, &ptr);
+ vp->siglen = 0;
+ vp->sig = emalloc(sign_siglen);
+ EVP_SignInit(&ctx, sign_digest);
+ EVP_SignUpdate(&ctx, (u_char *)vp, 12);
+ EVP_SignUpdate(&ctx, vp->ptr, len);
+ if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+ vp->siglen = htonl(len);
+#ifdef DEBUG
+ if (debug > 1)
+ X509_print_fp(stdout, cert);
+#endif
+ X509_free(cert);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * cert_valid - verify certificate with given public key
+ *
+ * This is pretty ugly, as the certificate has to be verified in the
+ * OpenSSL X509 structure, not in the DER format in the info/value
+ * structure.
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_VFY certificate not verified
+ */
+int
+cert_valid(
+ struct cert_info *cinf, /* certificate information structure */
+ EVP_PKEY *pkey /* public key */
+ )
+{
+ X509 *cert; /* X509 certificate */
+ u_char *ptr;
+
+ if (cinf->flags & CERT_SIGN)
+ return (XEVNT_OK);
+ ptr = (u_char *)cinf->cert.ptr;
+ cert = d2i_X509(NULL, &ptr, ntohl(cinf->cert.vallen));
+ if (!X509_verify(cert, pkey))
+ return (XEVNT_VFY);
+ cinf->flags |= CERT_SIGN;
+ X509_free(cert);
+ return (XEVNT_OK);
+}
+
+
+/*
+ * cert - install certificate in certificate list
+ *
+ * This routine encodes an extension field into a certificate info/value
+ * structure. It searches the certificate list for duplicates and
+ * expunges whichever is older. It then searches the list for other
+ * certificates that might be verified by this latest one. Finally, it
+ * inserts this certificate first on the list.
+ *
+ * Returns
+ * XEVNT_OK success
+ * XEVNT_PER certificate expired
+ * XEVNT_CRT bad or missing certificate
+ */
+int
+cert_install(
+ struct exten *ep, /* cert info/value */
+ struct peer *peer /* peer structure */
+ )
+{
+ struct cert_info *cp, *xp, *yp, **zp;
+ int rval;
+ tstamp_t tstamp;
+
+ /*
+ * Parse and validate the signed certificate. If valid,
+ * construct the info/value structure; otherwise, scamper home.
+ * Note this allows a certificate not-before time to be in the
+ * future, but not a not-after time to be in the past.
+ */
+ if ((cp = cert_parse((u_char *)ep->pkt, ntohl(ep->vallen),
+ ntohl(ep->fstamp))) == NULL)
+ return (XEVNT_CRT);
+
+ tstamp = crypto_time();
+ if (tstamp > cp->last) {
+ cert_free(cp);
+ return (XEVNT_PER);
+ }
+
+ /*
+ * Scan certificate list looking for another certificate with
+ * the same subject and issuer. If another is found with the
+ * same or older filestamp, unlink it and return the goodies to
+ * the heap. If another is found with a later filetsamp, discard
+ * the new one and leave the building.
+ */
+ rval = XEVNT_OK;
+ yp = cp;
+ zp = &cinfo;
+ for (xp = cinfo; xp != NULL; xp = xp->link) {
+ if (strcmp(cp->subject, xp->subject) == 0 &&
+ strcmp(cp->issuer, xp->issuer) == 0) {
+ if (ntohl(cp->cert.fstamp) <=
+ ntohl(xp->cert.fstamp)) {
+ *zp = xp->link;;
+ cert_free(xp);
+ } else {
+ cert_free(cp);
+ return (XEVNT_TSP);
+ }
+ break;
+ }
+ zp = &xp->link;
+ }
+ yp->link = cinfo;
+ cinfo = yp;
+
+ /*
+ * Scan the certificate list to see if Y is signed by X.
+ */
+ for (yp = cinfo; yp != NULL; yp = yp->link) {
+ for (xp = cinfo; xp != NULL; xp = xp->link) {
+ if (yp->flags & CERT_ERROR)
+ continue;
+
+ /*
+ * If issuer Y matches subject X and signature Y
+ * is valid using public key X, then Y is valid.
+ */
+ if (strcmp(yp->issuer, xp->subject) != 0)
+ continue;
+
+ if (cert_valid(yp, xp->pkey) != XEVNT_OK) {
+ yp->flags |= CERT_ERROR;
+ continue;
+ }
+ xp->flags |= CERT_SIGN;
+
+ /*
+ * If X is trusted, then Y is trusted. Note that
+ * we might stumble over a self signed
+ * certificate that is not trusted, at least
+ * temporarily. This can happen when a dude
+ * first comes up, but has not synchronized the
+ * clock and had its certificate signed by its
+ * server. In case of broken certificate trail,
+ * this might result in a loop that could
+ * persist until timeout.
+ */
+ if (!(xp->flags & CERT_TRUST))
+ continue;
+
+ yp->flags |= CERT_TRUST;
+
+ /*
+ * If subject Y matches the server subject name,
+ * then Y has completed the certificate trail.
+ * Save the group key and light the valid bit.
+ */
+ if (strcmp(yp->subject, peer->subject) != 0)
+ continue;
+
+ if (yp->grpkey != NULL) {
+ if (peer->grpkey != NULL)
+ BN_free(peer->grpkey);
+ peer->grpkey = BN_bin2bn(yp->grpkey,
+ yp->grplen, NULL);
+ }
+ peer->crypto |= CRYPTO_FLAG_VALID;
+
+ /*
+ * If the server has an an identity scheme,
+ * fetch the identity credentials. If not, the
+ * identity is verified only by the trusted
+ * certificate. The next signature will set the
+ * server proventic.
+ */
+ if (peer->crypto & (CRYPTO_FLAG_GQ |
+ CRYPTO_FLAG_IFF | CRYPTO_FLAG_MV))
+ continue;
+
+ peer->crypto |= CRYPTO_FLAG_VRFY;
+ }
+ }
+
+ /*
+ * That was awesome. Now update the timestamps and signatures.
+ */
+ crypto_update();
+ return (rval);
+}
+
+
+/*
+ * cert_free - free certificate information structure
+ */
+void
+cert_free(
+ struct cert_info *cinf /* certificate info/value structure */
+ )
+{
+ if (cinf->pkey != NULL)
+ EVP_PKEY_free(cinf->pkey);
+ if (cinf->subject != NULL)
+ free(cinf->subject);
+ if (cinf->issuer != NULL)
+ free(cinf->issuer);
+ if (cinf->grpkey != NULL)
+ free(cinf->grpkey);
+ value_free(&cinf->cert);
+ free(cinf);
+}
+
+
+/*
+ ***********************************************************************
+ * *
+ * The following routines are used only at initialization time *
+ * *
+ ***********************************************************************
+ */
+/*
+ * crypto_key - load cryptographic parameters and keys from files
+ *
+ * This routine loads a PEM-encoded public/private key pair and extracts
+ * the filestamp from the file name.
+ *
+ * Returns public key pointer if valid, NULL if not. Side effect updates
+ * the filestamp if valid.
+ */
+static EVP_PKEY *
+crypto_key(
+ char *cp, /* file name */
+ tstamp_t *fstamp /* filestamp */
+ )
+{
+ FILE *str; /* file handle */
+ EVP_PKEY *pkey = NULL; /* public/private key */
+ char filename[MAXFILENAME]; /* name of key file */
+ char linkname[MAXFILENAME]; /* filestamp buffer) */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ char *ptr;
+
+ /*
+ * Open the key file. If the first character of the file name is
+ * not '/', prepend the keys directory string. If something goes
+ * wrong, abandon ship.
+ */
+ if (*cp == '/')
+ strcpy(filename, cp);
+ else
+ snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
+ str = fopen(filename, "r");
+ if (str == NULL)
+ return (NULL);
+
+ /*
+ * Read the filestamp, which is contained in the first line.
+ */
+ if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) {
+ msyslog(LOG_ERR, "crypto_key: no data %s\n",
+ filename);
+ return (NULL);
+ }
+ if ((ptr = strrchr(ptr, '.')) == NULL) {
+ msyslog(LOG_ERR, "crypto_key: no filestamp %s\n",
+ filename);
+ return (NULL);
+ }
+ if (sscanf(++ptr, "%u", fstamp) != 1) {
+ msyslog(LOG_ERR, "crypto_key: invalid timestamp %s\n",
+ filename);
+ return (NULL);
+ }
+
+ /*
+ * Read and decrypt PEM-encoded private key.
+ */
+ pkey = PEM_read_PrivateKey(str, NULL, NULL, passwd);
+ fclose(str);
+ if (pkey == NULL) {
+ msyslog(LOG_ERR, "crypto_key %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (NULL);
+ }
+
+ /*
+ * Leave tracks in the cryptostats.
+ */
+ if ((ptr = strrchr(linkname, '\n')) != NULL)
+ *ptr = '\0';
+ sprintf(statstr, "%s mod %d", &linkname[2],
+ EVP_PKEY_size(pkey) * 8);
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_key: %s\n", statstr);
+ if (debug > 1) {
+ if (EVP_MD_type(pkey) == EVP_PKEY_DSA)
+ DSA_print_fp(stdout, pkey->pkey.dsa, 0);
+ else
+ RSA_print_fp(stdout, pkey->pkey.rsa, 0);
+ }
+#endif
+ return (pkey);
+}
+
+
+/*
+ * crypto_cert - load certificate from file
+ *
+ * This routine loads a X.509 RSA or DSA certificate from a file and
+ * constructs a info/cert value structure for this machine. The
+ * structure includes a filestamp extracted from the file name. Later
+ * the certificate can be sent to another machine by request.
+ *
+ * Returns certificate info/value pointer if valid, NULL if not.
+ */
+static struct cert_info * /* certificate information */
+crypto_cert(
+ char *cp /* file name */
+ )
+{
+ struct cert_info *ret; /* certificate information */
+ FILE *str; /* file handle */
+ char filename[MAXFILENAME]; /* name of certificate file */
+ char linkname[MAXFILENAME]; /* filestamp buffer */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ tstamp_t fstamp; /* filestamp */
+ long len;
+ char *ptr;
+ char *name, *header;
+ u_char *data;
+
+ /*
+ * Open the certificate file. If the first character of the file
+ * name is not '/', prepend the keys directory string. If
+ * something goes wrong, abandon ship.
+ */
+ if (*cp == '/')
+ strcpy(filename, cp);
+ else
+ snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
+ str = fopen(filename, "r");
+ if (str == NULL)
+ return (NULL);
+
+ /*
+ * Read the filestamp, which is contained in the first line.
+ */
+ if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) {
+ msyslog(LOG_ERR, "crypto_cert: no data %s\n",
+ filename);
+ return (NULL);
+ }
+ if ((ptr = strrchr(ptr, '.')) == NULL) {
+ msyslog(LOG_ERR, "crypto_cert: no filestamp %s\n",
+ filename);
+ return (NULL);
+ }
+ if (sscanf(++ptr, "%u", &fstamp) != 1) {
+ msyslog(LOG_ERR, "crypto_cert: invalid filestamp %s\n",
+ filename);
+ return (NULL);
+ }
+
+ /*
+ * Read PEM-encoded certificate and install.
+ */
+ if (!PEM_read(str, &name, &header, &data, &len)) {
+ msyslog(LOG_ERR, "crypto_cert %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (NULL);
+ }
+ free(header);
+ if (strcmp(name, "CERTIFICATE") !=0) {
+ msyslog(LOG_INFO, "crypto_cert: wrong PEM type %s",
+ name);
+ free(name);
+ free(data);
+ return (NULL);
+ }
+ free(name);
+
+ /*
+ * Parse certificate and generate info/value structure.
+ */
+ ret = cert_parse(data, len, fstamp);
+ free(data);
+ if (ret == NULL)
+ return (NULL);
+ if ((ptr = strrchr(linkname, '\n')) != NULL)
+ *ptr = '\0';
+ sprintf(statstr, "%s 0x%x len %lu", &linkname[2], ret->flags,
+ len);
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_cert: %s\n", statstr);
+#endif
+ return (ret);
+}
+
+
+/*
+ * crypto_tai - load leapseconds table from file
+ *
+ * This routine loads the ERTS leapsecond file in NIST text format,
+ * converts to a value structure and extracts a filestamp from the file
+ * name. The data are used to establish the TAI offset from UTC, which
+ * is provided to the kernel if supported. Later the data can be sent to
+ * another machine on request.
+ */
+static void
+crypto_tai(
+ char *cp /* file name */
+ )
+{
+ FILE *str; /* file handle */
+ char buf[NTP_MAXSTRLEN]; /* file line buffer */
+ u_int leapsec[MAX_LEAP]; /* NTP time at leaps */
+ u_int offset; /* offset at leap (s) */
+ char filename[MAXFILENAME]; /* name of leapseconds file */
+ char linkname[MAXFILENAME]; /* file link (for filestamp) */
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+ tstamp_t fstamp; /* filestamp */
+ u_int len;
+ char *ptr;
+ int rval, i;
+#ifdef KERNEL_PLL
+#if NTP_API > 3
+ struct timex ntv; /* kernel interface structure */
+#endif /* NTP_API */
+#endif /* KERNEL_PLL */
+
+ /*
+ * Open the file and discard comment lines. If the first
+ * character of the file name is not '/', prepend the keys
+ * directory string. If the file is not found, not to worry; it
+ * can be retrieved over the net. But, if it is found with
+ * errors, we crash and burn.
+ */
+ if (*cp == '/')
+ strcpy(filename, cp);
+ else
+ snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
+ if ((str = fopen(filename, "r")) == NULL)
+ return;
+
+ /*
+ * Extract filestamp if present.
+ */
+ rval = readlink(filename, linkname, MAXFILENAME - 1);
+ if (rval > 0) {
+ linkname[rval] = '\0';
+ ptr = strrchr(linkname, '.');
+ } else {
+ ptr = strrchr(filename, '.');
+ }
+ if (ptr != NULL)
+ sscanf(++ptr, "%u", &fstamp);
+ else
+ fstamp = 0;
+ tai_leap.fstamp = htonl(fstamp);
+
+ /*
+ * We are rather paranoid here, since an intruder might cause a
+ * coredump by infiltrating naughty values. Empty lines and
+ * comments are ignored. Other lines must begin with two
+ * integers followed by junk or comments. The first integer is
+ * the NTP seconds of leap insertion, the second is the offset
+ * of TAI relative to UTC after that insertion. The second word
+ * must equal the initial insertion of ten seconds on 1 January
+ * 1972 plus one second for each succeeding insertion.
+ */
+ i = 0;
+ while (i < MAX_LEAP) {
+ ptr = fgets(buf, NTP_MAXSTRLEN - 1, str);
+ if (ptr == NULL)
+ break;
+ if (strlen(buf) < 1)
+ continue;
+ if (*buf == '#')
+ continue;
+ if (sscanf(buf, "%u %u", &leapsec[i], &offset) != 2)
+ continue;
+ if (i != (int)(offset - TAI_1972)) {
+ break;
+ }
+ i++;
+ }
+ fclose(str);
+ if (ptr != NULL) {
+ msyslog(LOG_INFO,
+ "crypto_tai: leapseconds file %s error %d", cp,
+ rval);
+ exit (-1);
+ }
+
+ /*
+ * The extension field table entries consists of the NTP seconds
+ * of leap insertion in reverse order, so that the most recent
+ * insertion is the first entry in the table.
+ */
+ len = i * 4;
+ tai_leap.vallen = htonl(len);
+ ptr = emalloc(len);
+ tai_leap.ptr = (unsigned char *) ptr;
+ for (; i >= 0; i--) {
+ *ptr++ = (char) htonl(leapsec[i]);
+ }
+ crypto_flags |= CRYPTO_FLAG_TAI;
+ sys_tai = len / 4 + TAI_1972 - 1;
+#ifdef KERNEL_PLL
+#if NTP_API > 3
+ ntv.modes = MOD_TAI;
+ ntv.constant = sys_tai;
+ if (ntp_adjtime(&ntv) == TIME_ERROR)
+ msyslog(LOG_INFO,
+ "crypto_tai: kernel TAI update failed");
+#endif /* NTP_API */
+#endif /* KERNEL_PLL */
+ sprintf(statstr, "%s link %d fs %u offset %u", cp, rval, fstamp,
+ ntohl(tai_leap.vallen) / 4 + TAI_1972 - 1);
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("crypto_tai: %s\n", statstr);
+#endif
+}
+
+
+/*
+ * crypto_setup - load keys, certificate and leapseconds table
+ *
+ * This routine loads the public/private host key and certificate. If
+ * available, it loads the public/private sign key, which defaults to
+ * the host key, and leapseconds table. The host key must be RSA, but
+ * the sign key can be either RSA or DSA. In either case, the public key
+ * on the certificate must agree with the sign key.
+ */
+void
+crypto_setup(void)
+{
+ EVP_PKEY *pkey; /* private/public key pair */
+ char filename[MAXFILENAME]; /* file name buffer */
+ l_fp seed; /* crypto PRNG seed as NTP timestamp */
+ tstamp_t fstamp; /* filestamp */
+ tstamp_t sstamp; /* sign filestamp */
+ u_int len, bytes;
+ u_char *ptr;
+
+ /*
+ * Initialize structures.
+ */
+ if (!crypto_flags)
+ return;
+ gethostname(filename, MAXFILENAME);
+ bytes = strlen(filename) + 1;
+ sys_hostname = emalloc(bytes);
+ memcpy(sys_hostname, filename, bytes);
+ if (passwd == NULL)
+ passwd = sys_hostname;
+ memset(&hostval, 0, sizeof(hostval));
+ memset(&pubkey, 0, sizeof(pubkey));
+ memset(&tai_leap, 0, sizeof(tai_leap));
+
+ /*
+ * Load required random seed file and seed the random number
+ * generator. Be default, it is found in the user home
+ * directory. The root home directory may be / or /root,
+ * depending on the system. Wiggle the contents a bit and write
+ * it back so the sequence does not repeat when we next restart.
+ */
+ ERR_load_crypto_strings();
+ if (rand_file == NULL) {
+ if ((RAND_file_name(filename, MAXFILENAME)) != NULL) {
+ rand_file = emalloc(strlen(filename) + 1);
+ strcpy(rand_file, filename);
+ }
+ } else if (*rand_file != '/') {
+ snprintf(filename, MAXFILENAME, "%s/%s", keysdir,
+ rand_file);
+ free(rand_file);
+ rand_file = emalloc(strlen(filename) + 1);
+ strcpy(rand_file, filename);
+ }
+ if (rand_file == NULL) {
+ msyslog(LOG_ERR,
+ "crypto_setup: random seed file not specified");
+ exit (-1);
+ }
+ if ((bytes = RAND_load_file(rand_file, -1)) == 0) {
+ msyslog(LOG_ERR,
+ "crypto_setup: random seed file %s not found\n",
+ rand_file);
+ exit (-1);
+ }
+ get_systime(&seed);
+ RAND_seed(&seed, sizeof(l_fp));
+ RAND_write_file(rand_file);
+ OpenSSL_add_all_algorithms();
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_setup: OpenSSL version %lx random seed file %s bytes read %d\n",
+ SSLeay(), rand_file, bytes);
+#endif
+
+ /*
+ * Load required host key from file "ntpkey_host_<hostname>". It
+ * also becomes the default sign key.
+ */
+ if (host_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_host_%s",
+ sys_hostname);
+ host_file = emalloc(strlen(filename) + 1);
+ strcpy(host_file, filename);
+ }
+ pkey = crypto_key(host_file, &fstamp);
+ if (pkey == NULL) {
+ msyslog(LOG_ERR,
+ "crypto_setup: host key file %s not found or corrupt",
+ host_file);
+ exit (-1);
+ }
+ host_pkey = pkey;
+ sign_pkey = pkey;
+ sstamp = fstamp;
+ hostval.fstamp = htonl(fstamp);
+ if (EVP_MD_type(host_pkey) != EVP_PKEY_RSA) {
+ msyslog(LOG_ERR,
+ "crypto_setup: host key is not RSA key type");
+ exit (-1);
+ }
+ hostval.vallen = htonl(strlen(sys_hostname));
+ hostval.ptr = (unsigned char *) sys_hostname;
+
+ /*
+ * Construct public key extension field for agreement scheme.
+ */
+ len = i2d_PublicKey(host_pkey, NULL);
+ ptr = emalloc(len);
+ pubkey.ptr = ptr;
+ i2d_PublicKey(host_pkey, &ptr);
+ pubkey.vallen = htonl(len);
+ pubkey.fstamp = hostval.fstamp;
+
+ /*
+ * Load optional sign key from file "ntpkey_sign_<hostname>". If
+ * loaded, it becomes the sign key.
+ */
+ if (sign_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_sign_%s",
+ sys_hostname);
+ sign_file = emalloc(strlen(filename) + 1);
+ strcpy(sign_file, filename);
+ }
+ pkey = crypto_key(sign_file, &fstamp);
+ if (pkey != NULL) {
+ sign_pkey = pkey;
+ sstamp = fstamp;
+ }
+ sign_siglen = EVP_PKEY_size(sign_pkey);
+
+ /*
+ * Load optional IFF parameters from file
+ * "ntpkey_iff_<hostname>".
+ */
+ if (iffpar_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_iff_%s",
+ sys_hostname);
+ iffpar_file = emalloc(strlen(filename) + 1);
+ strcpy(iffpar_file, filename);
+ }
+ iffpar_pkey = crypto_key(iffpar_file, &if_fstamp);
+ if (iffpar_pkey != NULL)
+ crypto_flags |= CRYPTO_FLAG_IFF;
+
+ /*
+ * Load optional GQ parameters from file "ntpkey_gq_<hostname>".
+ */
+ if (gqpar_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_gq_%s",
+ sys_hostname);
+ gqpar_file = emalloc(strlen(filename) + 1);
+ strcpy(gqpar_file, filename);
+ }
+ gqpar_pkey = crypto_key(gqpar_file, &gq_fstamp);
+ if (gqpar_pkey != NULL)
+ crypto_flags |= CRYPTO_FLAG_GQ;
+
+ /*
+ * Load optional MV parameters from file "ntpkey_mv_<hostname>".
+ */
+ if (mvpar_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_mv_%s",
+ sys_hostname);
+ mvpar_file = emalloc(strlen(filename) + 1);
+ strcpy(mvpar_file, filename);
+ }
+ mvpar_pkey = crypto_key(mvpar_file, &mv_fstamp);
+ if (mvpar_pkey != NULL)
+ crypto_flags |= CRYPTO_FLAG_MV;
+
+ /*
+ * Load required certificate from file "ntpkey_cert_<hostname>".
+ */
+ if (cert_file == NULL) {
+ snprintf(filename, MAXFILENAME, "ntpkey_cert_%s",
+ sys_hostname);
+ cert_file = emalloc(strlen(filename) + 1);
+ strcpy(cert_file, filename);
+ }
+ if ((cinfo = crypto_cert(cert_file)) == NULL) {
+ msyslog(LOG_ERR,
+ "certificate file %s not found or corrupt",
+ cert_file);
+ exit (-1);
+ }
+
+ /*
+ * The subject name must be the same as the host name, unless
+ * the certificate is private, in which case it may have come
+ * from another host.
+ */
+ if (!(cinfo->flags & CERT_PRIV) && strcmp(cinfo->subject,
+ sys_hostname) != 0) {
+ msyslog(LOG_ERR,
+ "crypto_setup: certificate %s not for this host",
+ cert_file);
+ cert_free(cinfo);
+ exit (-1);
+ }
+
+ /*
+ * It the certificate is trusted, the subject must be the same
+ * as the issuer, in other words it must be self signed.
+ */
+ if (cinfo->flags & CERT_PRIV && strcmp(cinfo->subject,
+ cinfo->issuer) != 0) {
+ if (cert_valid(cinfo, sign_pkey) != XEVNT_OK) {
+ msyslog(LOG_ERR,
+ "crypto_setup: certificate %s is trusted, but not self signed.",
+ cert_file);
+ cert_free(cinfo);
+ exit (-1);
+ }
+ }
+ sign_digest = cinfo->digest;
+ if (cinfo->flags & CERT_PRIV)
+ crypto_flags |= CRYPTO_FLAG_PRIV;
+ crypto_flags |= cinfo->nid << 16;
+
+ /*
+ * Load optional leapseconds table from file "ntpkey_leap". If
+ * the file is missing or defective, the values can later be
+ * retrieved from a server.
+ */
+ if (leap_file == NULL)
+ leap_file = "ntpkey_leap";
+ crypto_tai(leap_file);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "crypto_setup: flags 0x%x host %s signature %s\n",
+ crypto_flags, sys_hostname, OBJ_nid2ln(cinfo->nid));
+#endif
+}
+
+
+/*
+ * crypto_config - configure data from crypto configuration command.
+ */
+void
+crypto_config(
+ int item, /* configuration item */
+ char *cp /* file name */
+ )
+{
+ switch (item) {
+
+ /*
+ * Set random seed file name.
+ */
+ case CRYPTO_CONF_RAND:
+ rand_file = emalloc(strlen(cp) + 1);
+ strcpy(rand_file, cp);
+ break;
+
+ /*
+ * Set private key password.
+ */
+ case CRYPTO_CONF_PW:
+ passwd = emalloc(strlen(cp) + 1);
+ strcpy(passwd, cp);
+ break;
+
+ /*
+ * Set host file name.
+ */
+ case CRYPTO_CONF_PRIV:
+ host_file = emalloc(strlen(cp) + 1);
+ strcpy(host_file, cp);
+ break;
+
+ /*
+ * Set sign key file name.
+ */
+ case CRYPTO_CONF_SIGN:
+ sign_file = emalloc(strlen(cp) + 1);
+ strcpy(sign_file, cp);
+ break;
+
+ /*
+ * Set iff parameters file name.
+ */
+ case CRYPTO_CONF_IFFPAR:
+ iffpar_file = emalloc(strlen(cp) + 1);
+ strcpy(iffpar_file, cp);
+ break;
+
+ /*
+ * Set gq parameters file name.
+ */
+ case CRYPTO_CONF_GQPAR:
+ gqpar_file = emalloc(strlen(cp) + 1);
+ strcpy(gqpar_file, cp);
+ break;
+
+ /*
+ * Set mv parameters file name.
+ */
+ case CRYPTO_CONF_MVPAR:
+ mvpar_file = emalloc(strlen(cp) + 1);
+ strcpy(mvpar_file, cp);
+ break;
+
+ /*
+ * Set certificate file name.
+ */
+ case CRYPTO_CONF_CERT:
+ cert_file = emalloc(strlen(cp) + 1);
+ strcpy(cert_file, cp);
+ break;
+
+ /*
+ * Set leapseconds file name.
+ */
+ case CRYPTO_CONF_LEAP:
+ leap_file = emalloc(strlen(cp) + 1);
+ strcpy(leap_file, cp);
+ break;
+ }
+ crypto_flags |= CRYPTO_FLAG_ENAB;
+}
+# else
+int ntp_crypto_bs_pubkey;
+# endif /* OPENSSL */
diff --git a/ntpd/ntp_filegen.c b/ntpd/ntp_filegen.c
new file mode 100644
index 0000000..59a1d91
--- /dev/null
+++ b/ntpd/ntp_filegen.c
@@ -0,0 +1,547 @@
+/*
+ * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
+ *
+ * implements file generations support for NTP
+ * logfiles and statistic files
+ *
+ *
+ * Copyright (C) 1992, 1996 by Rainer Pruy
+ * Friedrich-Alexander Universität Erlangen-Nürnberg, Germany
+ *
+ * This code may be modified and used freely
+ * provided credits remain intact.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_string.h"
+#include "ntp_calendar.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+
+/*
+ * NTP is intended to run long periods of time without restart.
+ * Thus log and statistic files generated by NTP will grow large.
+ *
+ * this set of routines provides a central interface
+ * to generating files using file generations
+ *
+ * the generation of a file is changed according to file generation type
+ */
+
+
+/*
+ * redefine this if your system dislikes filename suffixes like
+ * X.19910101 or X.1992W50 or ....
+ */
+#define SUFFIX_SEP '.'
+
+/*
+ * other constants
+ */
+#define FGEN_AGE_SECS (24*60*60) /* life time of FILEGEN_AGE in seconds */
+
+static void filegen_open P((FILEGEN *, u_long));
+static int valid_fileref P((char *, char *));
+#ifdef UNUSED
+static FILEGEN *filegen_unregister P((char *));
+#endif /* UNUSED */
+
+/*
+ * open a file generation according to the current settings of gen
+ * will also provide a link to basename if requested to do so
+ */
+
+static void
+filegen_open(
+ FILEGEN *gen,
+ u_long newid
+ )
+{
+ char *filename;
+ char *basename;
+ u_int len;
+ FILE *fp;
+ struct calendar cal;
+
+ len = strlen(gen->prefix) + strlen(gen->basename) + 1;
+ basename = (char*)emalloc(len);
+ sprintf(basename, "%s%s", gen->prefix, gen->basename);
+
+ switch(gen->type) {
+ default:
+ msyslog(LOG_ERR, "unsupported file generations type %d for \"%s\" - reverting to FILEGEN_NONE",
+ gen->type, basename);
+ gen->type = FILEGEN_NONE;
+
+ /*FALLTHROUGH*/
+ case FILEGEN_NONE:
+ filename = (char*)emalloc(len);
+ sprintf(filename,"%s", basename);
+ break;
+
+ case FILEGEN_PID:
+ filename = (char*)emalloc(len + 1 + 1 + 10);
+ sprintf(filename,"%s%c#%ld", basename, SUFFIX_SEP, newid);
+ break;
+
+ case FILEGEN_DAY:
+ /* You can argue here in favor of using MJD, but
+ * I would assume it to be easier for humans to interpret dates
+ * in a format they are used to in everyday life.
+ */
+ caljulian(newid,&cal);
+ filename = (char*)emalloc(len + 1 + 4 + 2 + 2);
+ sprintf(filename, "%s%c%04d%02d%02d",
+ basename, SUFFIX_SEP, cal.year, cal.month, cal.monthday);
+ break;
+
+ case FILEGEN_WEEK:
+ /*
+ * This is still a hack
+ * - the term week is not correlated to week as it is used
+ * normally - it just refers to a period of 7 days
+ * starting at Jan 1 - 'weeks' are counted starting from zero
+ */
+ caljulian(newid,&cal);
+ filename = (char*)emalloc(len + 1 + 4 + 1 + 2);
+ sprintf(filename, "%s%c%04dw%02d",
+ basename, SUFFIX_SEP, cal.year, cal.yearday / 7);
+ break;
+
+ case FILEGEN_MONTH:
+ caljulian(newid,&cal);
+ filename = (char*)emalloc(len + 1 + 4 + 2);
+ sprintf(filename, "%s%c%04d%02d",
+ basename, SUFFIX_SEP, cal.year, cal.month);
+ break;
+
+ case FILEGEN_YEAR:
+ caljulian(newid,&cal);
+ filename = (char*)emalloc(len + 1 + 4);
+ sprintf(filename, "%s%c%04d", basename, SUFFIX_SEP, cal.year);
+ break;
+
+ case FILEGEN_AGE:
+ filename = (char*)emalloc(len + 1 + 2 + 10);
+ sprintf(filename, "%s%ca%08ld", basename, SUFFIX_SEP, newid);
+ break;
+ }
+
+ if (gen->type != FILEGEN_NONE) {
+ /*
+ * check for existence of a file with name 'basename'
+ * as we disallow such a file
+ * if FGEN_FLAG_LINK is set create a link
+ */
+ struct stat stats;
+ /*
+ * try to resolve name collisions
+ */
+ static u_long conflicts = 0;
+
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
+#endif
+ if (stat(basename, &stats) == 0) {
+ /* Hm, file exists... */
+ if (S_ISREG(stats.st_mode)) {
+ if (stats.st_nlink <= 1) {
+ /*
+ * Oh, it is not linked - try to save it
+ */
+ char *savename = (char*)emalloc(len + 1 + 1 + 10 + 10);
+ sprintf(savename, "%s%c%dC%lu",
+ basename,
+ SUFFIX_SEP,
+ (int) getpid(),
+ (u_long)conflicts++);
+ if (rename(basename, savename) != 0)
+ msyslog(LOG_ERR," couldn't save %s: %m", basename);
+ free(savename);
+ } else {
+ /*
+ * there is at least a second link tpo this file
+ * just remove the conflicting one
+ */
+ if (
+#if !defined(VMS)
+ unlink(basename) != 0
+#else
+ delete(basename) != 0
+#endif
+ )
+ msyslog(LOG_ERR, "couldn't unlink %s: %m", basename);
+ }
+ } else {
+ /*
+ * Ehh? Not a regular file ?? strange !!!!
+ */
+ msyslog(LOG_ERR, "expected regular file for %s (found mode 0%lo)",
+ basename, (unsigned long)stats.st_mode);
+ }
+ } else {
+ /*
+ * stat(..) failed, but it is absolutely correct for
+ * 'basename' not to exist
+ */
+ if (errno != ENOENT)
+ msyslog(LOG_ERR,"stat(%s) failed: %m", basename);
+ }
+ }
+
+ /*
+ * now, try to open new file generation...
+ */
+ fp = fopen(filename, "a");
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("opening filegen (type=%d/id=%lu) \"%s\"\n",
+ gen->type, (u_long)newid, filename);
+#endif
+
+ if (fp == NULL) {
+ /* open failed -- keep previous state
+ *
+ * If the file was open before keep the previous generation.
+ * This will cause output to end up in the 'wrong' file,
+ * but I think this is still better than losing output
+ *
+ * ignore errors due to missing directories
+ */
+
+ if (errno != ENOENT)
+ msyslog(LOG_ERR, "can't open %s: %m", filename);
+ } else {
+ if (gen->fp != NULL) {
+ fclose(gen->fp);
+ }
+ gen->fp = fp;
+ gen->id = newid;
+
+ if (gen->flag & FGEN_FLAG_LINK) {
+ /*
+ * need to link file to basename
+ * have to use hardlink for now as I want to allow
+ * gen->basename spanning directory levels
+ * this would make it more complex to get the correct
+ * filename for symlink
+ *
+ * Ok, it would just mean taking the part following
+ * the last '/' in the name.... Should add it later....
+ */
+
+ /* Windows NT does not support file links -Greg Schueman 1/18/97 */
+
+#if defined SYS_WINNT || defined SYS_VXWORKS
+ SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
+#elif defined(VMS)
+ errno = 0; /* On VMS, don't support FGEN_FLAG_LINK */
+#else /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
+ if (link(filename, basename) != 0)
+ if (errno != EEXIST)
+ msyslog(LOG_ERR, "can't link(%s, %s): %m", filename, basename);
+#endif /* SYS_WINNT || VXWORKS */
+ } /* flags & FGEN_FLAG_LINK */
+ } /* else fp == NULL */
+
+ free(basename);
+ free(filename);
+ return;
+}
+
+/*
+ * this function sets up gen->fp to point to the correct
+ * generation of the file for the time specified by 'now'
+ *
+ * 'now' usually is interpreted as second part of a l_fp as is in the cal...
+ * library routines
+ */
+
+void
+filegen_setup(
+ FILEGEN *gen,
+ u_long now
+ )
+{
+ u_long new_gen = ~ (u_long) 0;
+ struct calendar cal;
+
+ if (!(gen->flag & FGEN_FLAG_ENABLED)) {
+ if (gen->fp != NULL)
+ fclose(gen->fp);
+ return;
+ }
+
+ switch (gen->type) {
+ case FILEGEN_NONE:
+ if (gen->fp != NULL) return; /* file already open */
+ break;
+
+ case FILEGEN_PID:
+ new_gen = getpid();
+ break;
+
+ case FILEGEN_DAY:
+ caljulian(now, &cal);
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_WEEK:
+ /* Would be nice to have a calweekstart() routine */
+ /* so just use a hack ... */
+ /* just round time to integral 7 day period for actual year */
+ new_gen = now - (now - calyearstart(now)) % TIMES7(SECSPERDAY)
+ + 60;
+ /*
+ * just to be sure -
+ * the computation above would fail in the presence of leap seconds
+ * so at least carry the date to the next day (+60 (seconds))
+ * and go back to the start of the day via calendar computations
+ */
+ caljulian(new_gen, &cal);
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_MONTH:
+ caljulian(now, &cal);
+ cal.yearday = (u_short) (cal.yearday - cal.monthday + 1);
+ cal.monthday = 1;
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_YEAR:
+ new_gen = calyearstart(now);
+ break;
+
+ case FILEGEN_AGE:
+ new_gen = current_time - (current_time % FGEN_AGE_SECS);
+ break;
+ }
+ /*
+ * try to open file if not yet open
+ * reopen new file generation file on change of generation id
+ */
+ if (gen->fp == NULL || gen->id != new_gen) {
+ filegen_open(gen, new_gen);
+ }
+}
+
+
+/*
+ * change settings for filegen files
+ */
+void
+filegen_config(
+ FILEGEN *gen,
+ char *basename,
+ u_int type,
+ u_int flag
+ )
+{
+ /*
+ * if nothing would be changed...
+ */
+ if ((basename == gen->basename || strcmp(basename,gen->basename) == 0) &&
+ type == gen->type &&
+ flag == gen->flag)
+ return;
+
+ /*
+ * validate parameters
+ */
+ if (!valid_fileref(gen->prefix,basename))
+ return;
+
+ if (gen->fp != NULL)
+ fclose(gen->fp);
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("configuring filegen:\n\tprefix:\t%s\n\tbasename:\t%s -> %s\n\ttype:\t%d -> %d\n\tflag: %x -> %x\n",
+ gen->prefix, gen->basename, basename, gen->type, type, gen->flag, flag);
+#endif
+ if (gen->basename != basename || strcmp(gen->basename, basename) != 0) {
+ free(gen->basename);
+ gen->basename = (char*)emalloc(strlen(basename) + 1);
+ strcpy(gen->basename, basename);
+ }
+ gen->type = (u_char) type;
+ gen->flag = (u_char) flag;
+
+ /*
+ * make filegen use the new settings
+ * special action is only required when a generation file
+ * is currently open
+ * otherwise the new settings will be used anyway at the next open
+ */
+ if (gen->fp != NULL) {
+ l_fp now;
+
+ get_systime(&now);
+ filegen_setup(gen, now.l_ui);
+ }
+}
+
+
+/*
+ * check whether concatenating prefix and basename
+ * yields a legal filename
+ */
+static int
+valid_fileref(
+ char *prefix,
+ char *basename
+ )
+{
+ /*
+ * prefix cannot be changed dynamically
+ * (within the context of filegen)
+ * so just reject basenames containing '..'
+ *
+ * ASSUMPTION:
+ * file system parts 'below' prefix may be
+ * specified without infringement of security
+ *
+ * restricing prefix to legal values
+ * has to be ensured by other means
+ * (however, it would be possible to perform some checks here...)
+ */
+ register char *p = basename;
+
+ /*
+ * Just to catch, dumb errors opening up the world...
+ */
+ if (prefix == NULL || *prefix == '\0')
+ return 0;
+
+ if (basename == NULL)
+ return 0;
+
+ for (p = basename; p; p = strchr(p, '/')) {
+ if (*p == '.' && *(p+1) == '.' && (*(p+2) == '\0' || *(p+2) == '/'))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * filegen registry
+ */
+
+static struct filegen_entry {
+ char *name;
+ FILEGEN *filegen;
+ struct filegen_entry *next;
+} *filegen_registry = NULL;
+
+
+FILEGEN *
+filegen_get(
+ char *name
+ )
+{
+ struct filegen_entry *f = filegen_registry;
+
+ while(f) {
+ if (f->name == name || strcmp(name, f->name) == 0) {
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 3)
+ printf("filegen_get(\"%s\") = %x\n", name,
+ (u_int)f->filegen);
+#endif
+ return f->filegen;
+ }
+ f = f->next;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ printf("filegen_get(\"%s\") = NULL\n", name);
+#endif
+ return NULL;
+}
+
+void
+filegen_register(
+ const char *name,
+ FILEGEN *filegen
+ )
+{
+ struct filegen_entry **f = &filegen_registry;
+
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 3)
+ printf("filegen_register(\"%s\",%x)\n", name, (u_int)filegen);
+#endif
+ while (*f) {
+ if ((*f)->name == name || strcmp(name, (*f)->name) == 0) {
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 4) {
+ printf("replacing filegen %x\n", (u_int)(*f)->filegen);
+ }
+#endif
+ (*f)->filegen = filegen;
+ return;
+ }
+ f = &((*f)->next);
+ }
+
+ *f = (struct filegen_entry *) emalloc(sizeof(struct filegen_entry));
+ if (*f) {
+ (*f)->next = NULL;
+ (*f)->name = (char*)emalloc(strlen(name) + 1);
+ strcpy((*f)->name, name);
+ (*f)->filegen = filegen;
+#ifdef DEBUG
+ if (debug > 5) {
+ printf("adding new filegen\n");
+ }
+#endif
+ }
+
+ return;
+}
+
+#ifdef UNUSED
+static FILEGEN *
+filegen_unregister(
+ char *name
+ )
+{
+ struct filegen_entry **f = &filegen_registry;
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("filegen_unregister(\"%s\")\n", name);
+#endif
+
+ while (*f) {
+ if (strcmp((*f)->name,name) == 0) {
+ struct filegen_entry *ff = *f;
+ FILEGEN *fg;
+
+ *f = (*f)->next;
+ fg = ff->filegen;
+ free(ff->name);
+ free(ff);
+ return fg;
+ }
+ f = &((*f)->next);
+ }
+ return NULL;
+}
+#endif /* UNUSED */
diff --git a/ntpd/ntp_intres.c b/ntpd/ntp_intres.c
new file mode 100644
index 0000000..7f27f21
--- /dev/null
+++ b/ntpd/ntp_intres.c
@@ -0,0 +1,1065 @@
+/*
+ * ripped off from ../ntpres/ntpres.c by Greg Troxel 4/2/92
+ * routine callable from ntpd, rather than separate program
+ * also, key info passed in via a global, so no key file needed.
+ */
+
+/*
+ * ntpres - process configuration entries which require use of the resolver
+ *
+ * This is meant to be run by ntpd on the fly. It is not guaranteed
+ * to work properly if run by hand. This is actually a quick hack to
+ * stave off violence from people who hate using numbers in the
+ * configuration file (at least I hope the rest of the daemon is
+ * better than this). Also might provide some ideas about how one
+ * might go about autoconfiguring an NTP distribution network.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_request.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <signal.h>
+
+/**/
+#include <netinet/in.h>
+#include <arpa/inet.h>
+/**/
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h> /* MAXHOSTNAMELEN (often) */
+#endif
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Each item we are to resolve and configure gets one of these
+ * structures defined for it.
+ */
+struct conf_entry {
+ struct conf_entry *ce_next;
+ char *ce_name; /* name we are trying to resolve */
+ struct conf_peer ce_config; /* configuration info for peer */
+ struct sockaddr_storage peer_store; /* address info for both fams */
+};
+#define ce_peeraddr ce_config.peeraddr
+#define ce_peeraddr6 ce_config.peeraddr6
+#define ce_hmode ce_config.hmode
+#define ce_version ce_config.version
+#define ce_minpoll ce_config.minpoll
+#define ce_maxpoll ce_config.maxpoll
+#define ce_flags ce_config.flags
+#define ce_ttl ce_config.ttl
+#define ce_keyid ce_config.keyid
+#define ce_keystr ce_config.keystr
+
+/*
+ * confentries is a pointer to the list of configuration entries
+ * we have left to do.
+ */
+static struct conf_entry *confentries = NULL;
+
+/*
+ * We take an interrupt every thirty seconds, at which time we decrement
+ * config_timer and resolve_timer. The former is set to 2, so we retry
+ * unsucessful reconfigurations every minute. The latter is set to
+ * an exponentially increasing value which starts at 2 and increases to
+ * 32. When this expires we retry failed name resolutions.
+ *
+ * We sleep SLEEPTIME seconds before doing anything, to give the server
+ * time to arrange itself.
+ */
+#define MINRESOLVE 2
+#define MAXRESOLVE 32
+#define CONFIG_TIME 2
+#define ALARM_TIME 30
+#define SLEEPTIME 2
+
+static volatile int config_timer = 0;
+static volatile int resolve_timer = 0;
+
+static int resolve_value; /* next value of resolve timer */
+
+/*
+ * Big hack attack
+ */
+#define LOCALHOST 0x7f000001 /* 127.0.0.1, in hex, of course */
+#define SKEWTIME 0x08000000 /* 0.03125 seconds as a l_fp fraction */
+
+/*
+ * Select time out. Set to 2 seconds. The server is on the local machine,
+ * after all.
+ */
+#define TIMEOUT_SEC 2
+#define TIMEOUT_USEC 0
+
+
+/*
+ * Input processing. The data on each line in the configuration file
+ * is supposed to consist of entries in the following order
+ */
+#define TOK_HOSTNAME 0
+#define TOK_HMODE 1
+#define TOK_VERSION 2
+#define TOK_MINPOLL 3
+#define TOK_MAXPOLL 4
+#define TOK_FLAGS 5
+#define TOK_TTL 6
+#define TOK_KEYID 7
+#define TOK_KEYSTR 8
+#define NUMTOK 9
+
+#define MAXLINESIZE 512
+
+
+/*
+ * File descriptor for ntp request code.
+ */
+static SOCKET sockfd = INVALID_SOCKET; /* NT uses SOCKET */
+
+/* stuff to be filled in by caller */
+
+keyid_t req_keyid; /* request keyid */
+char *req_file; /* name of the file with configuration info */
+
+/* end stuff to be filled in */
+
+
+static RETSIGTYPE bong P((int));
+static void checkparent P((void));
+static void removeentry P((struct conf_entry *));
+static void addentry P((char *, int, int, int, int, u_int,
+ int, keyid_t, char *));
+static int findhostaddr P((struct conf_entry *));
+static void openntp P((void));
+static int request P((struct conf_peer *));
+static char * nexttoken P((char **));
+static void readconf P((FILE *, char *));
+static void doconfigure P((int));
+
+struct ntp_res_t_pkt { /* Tagged packet: */
+ void *tag; /* For the caller */
+ u_int32 paddr; /* IP to look up, or 0 */
+ char name[MAXHOSTNAMELEN]; /* Name to look up (if 1st byte is not 0) */
+};
+
+struct ntp_res_c_pkt { /* Control packet: */
+ char name[MAXHOSTNAMELEN];
+ u_int32 paddr;
+ int mode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ u_int flags;
+ int ttl;
+ keyid_t keyid;
+ u_char keystr[MAXFILENAME];
+};
+
+
+/*
+ * ntp_res_recv: Process an answer from the resolver
+ */
+
+void
+ntp_res_recv(void)
+{
+ /*
+ We have data ready on our descriptor.
+ It may be an EOF, meaning the resolver process went away.
+ Otherwise, it will be an "answer".
+ */
+}
+
+
+/*
+ * ntp_intres needs;
+ *
+ * req_key(???), req_keyid, req_file valid
+ * syslog still open
+ */
+
+void
+ntp_intres(void)
+{
+ FILE *in;
+#ifdef HAVE_SIGSUSPEND
+ sigset_t set;
+
+ sigemptyset(&set);
+#endif /* HAVE_SIGSUSPEND */
+
+#ifdef DEBUG
+ if (debug > 1) {
+ msyslog(LOG_INFO, "NTP_INTRES running");
+ }
+#endif
+
+ /* check out auth stuff */
+ if (sys_authenticate) {
+ if (!authistrusted(req_keyid)) {
+ msyslog(LOG_ERR, "invalid request keyid %08x",
+ req_keyid );
+ exit(1);
+ }
+ }
+
+ /*
+ * Read the configuration info
+ * {this is bogus, since we are forked, but it is easier
+ * to keep this code - gdt}
+ */
+ if ((in = fopen(req_file, "r")) == NULL) {
+ msyslog(LOG_ERR, "can't open configuration file %s: %m",
+ req_file);
+ exit(1);
+ }
+ readconf(in, req_file);
+ (void) fclose(in);
+
+ if (!debug )
+ (void) unlink(req_file);
+
+ /*
+ * Sleep a little to make sure the server is completely up
+ */
+
+ sleep(SLEEPTIME);
+
+ /*
+ * Make a first cut at resolving the bunch
+ */
+ doconfigure(1);
+ if (confentries == NULL) {
+#if defined SYS_WINNT
+ ExitThread(0); /* Don't want to kill whole NT process */
+#else
+ exit(0); /* done that quick */
+#endif
+ }
+
+ /*
+ * Here we've got some problem children. Set up the timer
+ * and wait for it.
+ */
+ resolve_value = resolve_timer = MINRESOLVE;
+ config_timer = CONFIG_TIME;
+#ifndef SYS_WINNT
+ (void) signal_no_reset(SIGALRM, bong);
+ alarm(ALARM_TIME);
+#endif /* SYS_WINNT */
+
+ for (;;) {
+ if (confentries == NULL)
+ exit(0);
+
+ checkparent();
+
+ if (resolve_timer == 0) {
+ if (resolve_value < MAXRESOLVE)
+ resolve_value <<= 1;
+ resolve_timer = resolve_value;
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "resolve_timer: 0->%d", resolve_timer);
+#endif
+ config_timer = CONFIG_TIME;
+ doconfigure(1);
+ continue;
+ } else if (config_timer == 0) {
+ config_timer = CONFIG_TIME;
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "config_timer: 0->%d", config_timer);
+#endif
+ doconfigure(0);
+ continue;
+ }
+#ifndef SYS_WINNT
+ /*
+ * There is a race in here. Is okay, though, since
+ * all it does is delay things by 30 seconds.
+ */
+# ifdef HAVE_SIGSUSPEND
+ sigsuspend(&set);
+# else
+ sigpause(0);
+# endif /* HAVE_SIGSUSPEND */
+#else
+ if (config_timer > 0)
+ config_timer--;
+ if (resolve_timer > 0)
+ resolve_timer--;
+ sleep(ALARM_TIME);
+#endif /* SYS_WINNT */
+ }
+}
+
+
+#ifndef SYS_WINNT
+/*
+ * bong - service and reschedule an alarm() interrupt
+ */
+static RETSIGTYPE
+bong(
+ int sig
+ )
+{
+ if (config_timer > 0)
+ config_timer--;
+ if (resolve_timer > 0)
+ resolve_timer--;
+ alarm(ALARM_TIME);
+}
+#endif /* SYS_WINNT */
+
+/*
+ * checkparent - see if our parent process is still running
+ *
+ * No need to worry in the Windows NT environment whether the
+ * main thread is still running, because if it goes
+ * down it takes the whole process down with it (in
+ * which case we won't be running this thread either)
+ * Turn function into NOP;
+ */
+
+static void
+checkparent(void)
+{
+#if !defined (SYS_WINNT) && !defined (SYS_VXWORKS)
+
+ /*
+ * If our parent (the server) has died we will have been
+ * inherited by init. If so, exit.
+ */
+ if (getppid() == 1) {
+ msyslog(LOG_INFO, "parent died before we finished, exiting");
+ exit(0);
+ }
+#endif /* SYS_WINNT && SYS_VXWORKS*/
+}
+
+
+
+/*
+ * removeentry - we are done with an entry, remove it from the list
+ */
+static void
+removeentry(
+ struct conf_entry *entry
+ )
+{
+ register struct conf_entry *ce;
+
+ ce = confentries;
+ if (ce == entry) {
+ confentries = ce->ce_next;
+ return;
+ }
+
+ while (ce != NULL) {
+ if (ce->ce_next == entry) {
+ ce->ce_next = entry->ce_next;
+ return;
+ }
+ ce = ce->ce_next;
+ }
+}
+
+
+/*
+ * addentry - add an entry to the configuration list
+ */
+static void
+addentry(
+ char *name,
+ int mode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ u_int flags,
+ int ttl,
+ keyid_t keyid,
+ char *keystr
+ )
+{
+ register char *cp;
+ register struct conf_entry *ce;
+ unsigned int len;
+
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "intres: <%s> %d %d %d %d %x %d %x %s\n", name,
+ mode, version, minpoll, maxpoll, flags, ttl, keyid,
+ keystr);
+#endif
+ len = strlen(name) + 1;
+ cp = (char *)emalloc(len);
+ memmove(cp, name, len);
+
+ ce = (struct conf_entry *)emalloc(sizeof(struct conf_entry));
+ ce->ce_name = cp;
+ ce->ce_peeraddr = 0;
+ ce->ce_peeraddr6 = in6addr_any;
+ ANYSOCK(&ce->peer_store);
+ ce->ce_hmode = (u_char)mode;
+ ce->ce_version = (u_char)version;
+ ce->ce_minpoll = (u_char)minpoll;
+ ce->ce_maxpoll = (u_char)maxpoll;
+ ce->ce_flags = (u_char)flags;
+ ce->ce_ttl = (u_char)ttl;
+ ce->ce_keyid = keyid;
+ strncpy((char *)ce->ce_keystr, keystr, MAXFILENAME);
+ ce->ce_next = NULL;
+
+ if (confentries == NULL) {
+ confentries = ce;
+ } else {
+ register struct conf_entry *cep;
+
+ for (cep = confentries; cep->ce_next != NULL;
+ cep = cep->ce_next)
+ /* nothing */;
+ cep->ce_next = ce;
+ }
+}
+
+
+/*
+ * findhostaddr - resolve a host name into an address (Or vice-versa)
+ *
+ * Given one of {ce_peeraddr,ce_name}, find the other one.
+ * It returns 1 for "success" and 0 for an uncorrectable failure.
+ * Note that "success" includes try again errors. You can tell that you
+ * got a "try again" since {ce_peeraddr,ce_name} will still be zero.
+ */
+static int
+findhostaddr(
+ struct conf_entry *entry
+ )
+{
+ struct addrinfo *addr;
+ int error;
+
+ checkparent(); /* make sure our guy is still running */
+
+ if (entry->ce_name != NULL && SOCKNUL(&entry->peer_store)) {
+ /* HMS: Squawk? */
+ msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are defined...");
+ return 1;
+ }
+
+ if (entry->ce_name == NULL && !SOCKNUL(&entry->peer_store)) {
+ msyslog(LOG_ERR, "findhostaddr: both ce_name and ce_peeraddr are undefined!");
+ return 0;
+ }
+
+ if (entry->ce_name) {
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "findhostaddr: Resolving <%s>",
+ entry->ce_name);
+#endif /* DEBUG */
+ error = getaddrinfo(entry->ce_name, NULL, NULL, &addr);
+ if (error == 0) {
+ entry->peer_store = *((struct sockaddr_storage*)(addr->ai_addr));
+ if (entry->peer_store.ss_family == AF_INET) {
+ entry->ce_peeraddr =
+ GET_INADDR(entry->peer_store);
+ entry->ce_config.v6_flag = 0;
+ } else {
+ entry->ce_peeraddr6 =
+ GET_INADDR6(entry->peer_store);
+ entry->ce_config.v6_flag = 1;
+ }
+ }
+ } else {
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "findhostaddr: Resolving %s>",
+ stoa(&entry->peer_store));
+#endif
+ entry->ce_name = emalloc(MAXHOSTNAMELEN);
+ error = getnameinfo((const struct sockaddr *)&entry->peer_store,
+ SOCKLEN(&entry->peer_store),
+ (char *)&entry->ce_name, MAXHOSTNAMELEN,
+ NULL, 0, 0);
+ }
+
+ if (error != 0) {
+ /*
+ * If the resolver is in use, see if the failure is
+ * temporary. If so, return success.
+ */
+ if (h_errno == TRY_AGAIN)
+ return (1);
+ return (0);
+ }
+
+ if (entry->ce_name) {
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "findhostaddr: name resolved.");
+#endif
+
+#ifdef DEBUG
+ if (debug > 2)
+ msyslog(LOG_INFO, "findhostaddr: address resolved.");
+#endif
+ }
+
+ return (1);
+}
+
+
+/*
+ * openntp - open a socket to the ntp server
+ */
+static void
+openntp(void)
+{
+ struct addrinfo hints;
+ struct addrinfo *addrResult;
+
+ if (sockfd >= 0)
+ return;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ if (getaddrinfo(NULL, "ntp", &hints, &addrResult)!=0) {
+ msyslog(LOG_ERR, "getaddrinfo failed: %m");
+ exit(1);
+ }
+ sockfd = socket(addrResult->ai_family, addrResult->ai_socktype, 0);
+
+ if (sockfd == -1) {
+ msyslog(LOG_ERR, "socket() failed: %m");
+ exit(1);
+ }
+
+ /*
+ * Make the socket non-blocking. We'll wait with select()
+ */
+#ifndef SYS_WINNT
+#if defined(O_NONBLOCK)
+ if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
+ msyslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
+ exit(1);
+ }
+#else
+#if defined(FNDELAY)
+ if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
+ msyslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
+ exit(1);
+ }
+#else
+# include "Bletch: NEED NON BLOCKING IO"
+#endif /* FNDDELAY */
+#endif /* O_NONBLOCK */
+#else /* SYS_WINNT */
+ {
+ int on = 1;
+ if (ioctlsocket(sockfd,FIONBIO,(u_long *) &on) == SOCKET_ERROR) {
+ msyslog(LOG_ERR, "ioctlsocket(FIONBIO) fails: %m");
+ exit(1); /* Windows NT - set socket in non-blocking mode */
+ }
+ }
+#endif /* SYS_WINNT */
+ if (connect(sockfd, addrResult->ai_addr, addrResult->ai_addrlen) == -1) {
+ msyslog(LOG_ERR, "openntp: connect() failed: %m");
+ exit(1);
+ }
+ freeaddrinfo(addrResult);
+}
+
+
+/*
+ * request - send a configuration request to the server, wait for a response
+ */
+static int
+request(
+ struct conf_peer *conf
+ )
+{
+ fd_set fdset;
+ struct timeval tvout;
+ struct req_pkt reqpkt;
+ l_fp ts;
+ int n;
+#ifdef SYS_WINNT
+ HANDLE hReadWriteEvent = NULL;
+ BOOL ret;
+ DWORD NumberOfBytesWritten, NumberOfBytesRead, dwWait;
+ OVERLAPPED overlap;
+#endif /* SYS_WINNT */
+
+ checkparent(); /* make sure our guy is still running */
+
+ if (sockfd < 0)
+ openntp();
+
+#ifdef SYS_WINNT
+ hReadWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif /* SYS_WINNT */
+
+ /*
+ * Try to clear out any previously received traffic so it
+ * doesn't fool us. Note the socket is nonblocking.
+ */
+ tvout.tv_sec = 0;
+ tvout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
+ 0) {
+ recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ }
+
+ /*
+ * Make up a request packet with the configuration info
+ */
+ memset((char *)&reqpkt, 0, sizeof(reqpkt));
+
+ reqpkt.rm_vn_mode = RM_VN_MODE(0, 0, 0);
+ reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */
+ reqpkt.implementation = IMPL_XNTPD; /* local implementation */
+ reqpkt.request = REQ_CONFIG; /* configure a new peer */
+ reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */
+ reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(struct conf_peer));
+ /* Make sure mbz_itemsize <= sizeof reqpkt.data */
+ if (sizeof(struct conf_peer) > sizeof (reqpkt.data)) {
+ msyslog(LOG_ERR, "Bletch: conf_peer is too big for reqpkt.data!");
+ exit(1);
+ }
+ memmove(reqpkt.data, (char *)conf, sizeof(struct conf_peer));
+ reqpkt.keyid = htonl(req_keyid);
+
+ get_systime(&ts);
+ L_ADDUF(&ts, SKEWTIME);
+ HTONL_FP(&ts, &reqpkt.tstamp);
+ n = 0;
+ if (sys_authenticate)
+ n = authencrypt(req_keyid, (u_int32 *)&reqpkt, REQ_LEN_NOMAC);
+
+ /*
+ * Done. Send it.
+ */
+#ifndef SYS_WINNT
+ n = send(sockfd, (char *)&reqpkt, (unsigned)(REQ_LEN_NOMAC + n), 0);
+ if (n < 0) {
+ msyslog(LOG_ERR, "send to NTP server failed: %m");
+ return 0; /* maybe should exit */
+ }
+#else
+ /* In the NT world, documentation seems to indicate that there
+ * exist _write and _read routines that can be used to do blocking
+ * I/O on sockets. Problem is these routines require a socket
+ * handle obtained through the _open_osf_handle C run-time API
+ * of which there is no explanation in the documentation. We need
+ * nonblocking write's and read's anyway for our purpose here.
+ * We're therefore forced to deviate a little bit from the Unix
+ * model here and use the ReadFile and WriteFile Win32 I/O API's
+ * on the socket
+ */
+ overlap.Offset = overlap.OffsetHigh = (DWORD)0;
+ overlap.hEvent = hReadWriteEvent;
+ ret = WriteFile((HANDLE)sockfd, (char *)&reqpkt, REQ_LEN_NOMAC + n,
+ (LPDWORD)&NumberOfBytesWritten, (LPOVERLAPPED)&overlap);
+ if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
+ msyslog(LOG_ERR, "send to NTP server failed: %m");
+ return 0;
+ }
+ dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
+ if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
+ if (dwWait == WAIT_FAILED)
+ msyslog(LOG_ERR, "WaitForSingleObject failed: %m");
+ return 0;
+ }
+#endif /* SYS_WINNT */
+
+
+ /*
+ * Wait for a response. A weakness of the mode 7 protocol used
+ * is that there is no way to associate a response with a
+ * particular request, i.e. the response to this configuration
+ * request is indistinguishable from that to any other. I should
+ * fix this some day. In any event, the time out is fairly
+ * pessimistic to make sure that if an answer is coming back
+ * at all, we get it.
+ */
+ for (;;) {
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ tvout.tv_sec = TIMEOUT_SEC;
+ tvout.tv_usec = TIMEOUT_USEC;
+
+ n = select(sockfd + 1, &fdset, (fd_set *)0,
+ (fd_set *)0, &tvout);
+
+ if (n < 0)
+ {
+ if (errno != EINTR)
+ msyslog(LOG_ERR, "select() fails: %m");
+ return 0;
+ }
+ else if (n == 0)
+ {
+ if (debug)
+ msyslog(LOG_INFO, "select() returned 0.");
+ return 0;
+ }
+
+#ifndef SYS_WINNT
+ n = recv(sockfd, (char *)&reqpkt, REQ_LEN_MAC, 0);
+ if (n <= 0) {
+ if (n < 0) {
+ msyslog(LOG_ERR, "recv() fails: %m");
+ return 0;
+ }
+ continue;
+ }
+#else /* Overlapped I/O used on non-blocking sockets on Windows NT */
+ ret = ReadFile((HANDLE)sockfd, (char *)&reqpkt, (DWORD)REQ_LEN_MAC,
+ (LPDWORD)&NumberOfBytesRead, (LPOVERLAPPED)&overlap);
+ if ((ret == FALSE) && (GetLastError() != ERROR_IO_PENDING)) {
+ msyslog(LOG_ERR, "ReadFile() fails: %m");
+ return 0;
+ }
+ dwWait = WaitForSingleObject(hReadWriteEvent, (DWORD) TIMEOUT_SEC * 1000);
+ if ((dwWait == WAIT_FAILED) || (dwWait == WAIT_TIMEOUT)) {
+ if (dwWait == WAIT_FAILED) {
+ msyslog(LOG_ERR, "WaitForSingleObject fails: %m");
+ return 0;
+ }
+ continue;
+ }
+ n = NumberOfBytesRead;
+#endif /* SYS_WINNT */
+
+ /*
+ * Got one. Check through to make sure it is what
+ * we expect.
+ */
+ if (n < RESP_HEADER_SIZE) {
+ msyslog(LOG_ERR, "received runt response (%d octets)",
+ n);
+ continue;
+ }
+
+ if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO, "received non-response packet");
+#endif
+ continue;
+ }
+
+ if (ISMORE(reqpkt.rm_vn_mode)) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO, "received fragmented packet");
+#endif
+ continue;
+ }
+
+ if ( ( (INFO_VERSION(reqpkt.rm_vn_mode) < 2)
+ || (INFO_VERSION(reqpkt.rm_vn_mode) > NTP_VERSION))
+ || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "version (%d/%d) or mode (%d/%d) incorrect",
+ INFO_VERSION(reqpkt.rm_vn_mode),
+ NTP_VERSION,
+ INFO_MODE(reqpkt.rm_vn_mode),
+ MODE_PRIVATE);
+#endif
+ continue;
+ }
+
+ if (INFO_SEQ(reqpkt.auth_seq) != 0) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "nonzero sequence number (%d)",
+ INFO_SEQ(reqpkt.auth_seq));
+#endif
+ continue;
+ }
+
+ if (reqpkt.implementation != IMPL_XNTPD ||
+ reqpkt.request != REQ_CONFIG) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "implementation (%d) or request (%d) incorrect",
+ reqpkt.implementation, reqpkt.request);
+#endif
+ continue;
+ }
+
+ if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
+ INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
+ INFO_ITEMSIZE(reqpkt.mbz_itemsize) != 0) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "nitems (%d) mbz (%d) or itemsize (%d) nonzero",
+ INFO_NITEMS(reqpkt.err_nitems),
+ INFO_MBZ(reqpkt.mbz_itemsize),
+ INFO_ITEMSIZE(reqpkt.mbz_itemsize));
+#endif
+ continue;
+ }
+
+ n = INFO_ERR(reqpkt.err_nitems);
+ switch (n) {
+ case INFO_OKAY:
+ /* success */
+ return 1;
+
+ case INFO_ERR_IMPL:
+ msyslog(LOG_ERR,
+ "ntpd reports implementation mismatch!");
+ return 0;
+
+ case INFO_ERR_REQ:
+ msyslog(LOG_ERR,
+ "ntpd says configuration request is unknown!");
+ return 0;
+
+ case INFO_ERR_FMT:
+ msyslog(LOG_ERR,
+ "ntpd indicates a format error occurred!");
+ return 0;
+
+ case INFO_ERR_NODATA:
+ msyslog(LOG_ERR,
+ "ntpd indicates no data available!");
+ return 0;
+
+ case INFO_ERR_AUTH:
+ msyslog(LOG_ERR,
+ "ntpd returns a permission denied error!");
+ return 0;
+
+ default:
+ msyslog(LOG_ERR,
+ "ntpd returns unknown error code %d!", n);
+ return 0;
+ }
+ }
+}
+
+
+/*
+ * nexttoken - return the next token from a line
+ */
+static char *
+nexttoken(
+ char **lptr
+ )
+{
+ register char *cp;
+ register char *tstart;
+
+ cp = *lptr;
+
+ /*
+ * Skip leading white space
+ */
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+
+ /*
+ * If this is the end of the line, return nothing.
+ */
+ if (*cp == '\n' || *cp == '\0') {
+ *lptr = cp;
+ return NULL;
+ }
+
+ /*
+ * Must be the start of a token. Record the pointer and look
+ * for the end.
+ */
+ tstart = cp++;
+ while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
+ cp++;
+
+ /*
+ * Terminate the token with a \0. If this isn't the end of the
+ * line, space to the next character.
+ */
+ if (*cp == '\n' || *cp == '\0')
+ *cp = '\0';
+ else
+ *cp++ = '\0';
+
+ *lptr = cp;
+ return tstart;
+}
+
+
+/*
+ * readconf - read the configuration information out of the file we
+ * were passed. Note that since the file is supposed to be
+ * machine generated, we bail out at the first sign of trouble.
+ */
+static void
+readconf(
+ FILE *fp,
+ char *name
+ )
+{
+ register int i;
+ char *token[NUMTOK];
+ u_long intval[NUMTOK];
+ u_int flags;
+ char buf[MAXLINESIZE];
+ char *bp;
+
+ while (fgets(buf, MAXLINESIZE, fp) != NULL) {
+
+ bp = buf;
+ for (i = 0; i < NUMTOK; i++) {
+ if ((token[i] = nexttoken(&bp)) == NULL) {
+ msyslog(LOG_ERR,
+ "tokenizing error in file `%s', quitting",
+ name);
+ exit(1);
+ }
+ }
+
+ for (i = 1; i < NUMTOK - 1; i++) {
+ if (!atouint(token[i], &intval[i])) {
+ msyslog(LOG_ERR,
+ "format error for integer token `%s', file `%s', quitting",
+ token[i], name);
+ exit(1);
+ }
+ }
+
+ if (intval[TOK_HMODE] != MODE_ACTIVE &&
+ intval[TOK_HMODE] != MODE_CLIENT &&
+ intval[TOK_HMODE] != MODE_BROADCAST) {
+ msyslog(LOG_ERR, "invalid mode (%ld) in file %s",
+ intval[TOK_HMODE], name);
+ exit(1);
+ }
+
+ if (intval[TOK_VERSION] > NTP_VERSION ||
+ intval[TOK_VERSION] < NTP_OLDVERSION) {
+ msyslog(LOG_ERR, "invalid version (%ld) in file %s",
+ intval[TOK_VERSION], name);
+ exit(1);
+ }
+ if (intval[TOK_MINPOLL] < NTP_MINPOLL ||
+ intval[TOK_MINPOLL] > NTP_MAXPOLL) {
+ msyslog(LOG_ERR, "invalid MINPOLL value (%ld) in file %s",
+ intval[TOK_MINPOLL], name);
+ exit(1);
+ }
+
+ if (intval[TOK_MAXPOLL] < NTP_MINPOLL ||
+ intval[TOK_MAXPOLL] > NTP_MAXPOLL) {
+ msyslog(LOG_ERR, "invalid MAXPOLL value (%ld) in file %s",
+ intval[TOK_MAXPOLL], name);
+ exit(1);
+ }
+
+ if ((intval[TOK_FLAGS] & ~(FLAG_AUTHENABLE | FLAG_PREFER |
+ FLAG_NOSELECT | FLAG_BURST | FLAG_IBURST | FLAG_SKEY))
+ != 0) {
+ msyslog(LOG_ERR, "invalid flags (%ld) in file %s",
+ intval[TOK_FLAGS], name);
+ exit(1);
+ }
+
+ flags = 0;
+ if (intval[TOK_FLAGS] & FLAG_AUTHENABLE)
+ flags |= CONF_FLAG_AUTHENABLE;
+ if (intval[TOK_FLAGS] & FLAG_PREFER)
+ flags |= CONF_FLAG_PREFER;
+ if (intval[TOK_FLAGS] & FLAG_NOSELECT)
+ flags |= CONF_FLAG_NOSELECT;
+ if (intval[TOK_FLAGS] & FLAG_BURST)
+ flags |= CONF_FLAG_BURST;
+ if (intval[TOK_FLAGS] & FLAG_IBURST)
+ flags |= CONF_FLAG_IBURST;
+ if (intval[TOK_FLAGS] & FLAG_SKEY)
+ flags |= CONF_FLAG_SKEY;
+
+ /*
+ * This is as good as we can check it. Add it in.
+ */
+ addentry(token[TOK_HOSTNAME], (int)intval[TOK_HMODE],
+ (int)intval[TOK_VERSION], (int)intval[TOK_MINPOLL],
+ (int)intval[TOK_MAXPOLL], flags, (int)intval[TOK_TTL],
+ intval[TOK_KEYID], token[TOK_KEYSTR]);
+ }
+}
+
+
+/*
+ * doconfigure - attempt to resolve names and configure the server
+ */
+static void
+doconfigure(
+ int dores
+ )
+{
+ register struct conf_entry *ce;
+ register struct conf_entry *ceremove;
+
+ ce = confentries;
+ while (ce != NULL) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO,
+ "doconfigure: <%s> has peeraddr %s",
+ ce->ce_name, stoa(&ce->peer_store));
+#endif
+ if (dores && !SOCKNUL(&(ce->peer_store))) {
+ if (!findhostaddr(ce)) {
+ msyslog(LOG_ERR,
+ "couldn't resolve `%s', giving up on it",
+ ce->ce_name);
+ ceremove = ce;
+ ce = ceremove->ce_next;
+ removeentry(ceremove);
+ continue;
+ }
+ }
+
+ if (!SOCKNUL(&ce->peer_store)) {
+ if (request(&ce->ce_config)) {
+ ceremove = ce;
+ ce = ceremove->ce_next;
+ removeentry(ceremove);
+ continue;
+ }
+#ifdef DEBUG
+ if (debug > 1) {
+ msyslog(LOG_INFO,
+ "doconfigure: request() FAILED, maybe next time.");
+ }
+#endif
+ }
+ ce = ce->ce_next;
+ }
+}
diff --git a/ntpd/ntp_io.c b/ntpd/ntp_io.c
new file mode 100644
index 0000000..9f2acea
--- /dev/null
+++ b/ntpd/ntp_io.c
@@ -0,0 +1,2257 @@
+/*
+ * ntp_io.c - input/output routines for ntpd. The socket-opening code
+ * was shamelessly stolen from ntpd.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "iosignal.h"
+#include "ntp_refclock.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+#include "ntp.h"
+
+/* Don't include ISC's version of IPv6 variables and structures */
+#define ISC_IPV6_H 1
+#include <isc/interfaceiter.h>
+#include <isc/list.h>
+#include <isc/result.h>
+
+#ifdef SIM
+#include "ntpsim.h"
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#else /* Some old linux systems at least have in_system.h instead. */
+# ifdef HAVE_NETINET_IN_SYSTEM_H
+# include <netinet/in_system.h>
+# endif
+#endif /* HAVE_NETINET_IN_SYSTM_H */
+#ifdef HAVE_NETINET_IP_H
+# include <netinet/ip.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H /* UXPV: SIOC* #defines (Frank Vance <fvance@waii.com>) */
+# include <sys/sockio.h>
+#endif
+#include <arpa/inet.h>
+
+extern int listen_to_virtual_ips;
+
+#if defined(SYS_WINNT)
+#include <transmitbuff.h>
+#include <isc/win32os.h>
+/*
+ * Define this macro to control the behavior of connection
+ * resets on UDP sockets. See Microsoft KnowledgeBase Article Q263823
+ * for details.
+ * NOTE: This requires that Windows 2000 systems install Service Pack 2
+ * or later.
+ */
+#ifndef SIO_UDP_CONNRESET
+#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
+#endif
+
+#endif
+
+/*
+ * We do asynchronous input using the SIGIO facility. A number of
+ * recvbuf buffers are preallocated for input. In the signal
+ * handler we poll to see which sockets are ready and read the
+ * packets from them into the recvbuf's along with a time stamp and
+ * an indication of the source host and the interface it was received
+ * through. This allows us to get as accurate receive time stamps
+ * as possible independent of other processing going on.
+ *
+ * We watch the number of recvbufs available to the signal handler
+ * and allocate more when this number drops below the low water
+ * mark. If the signal handler should run out of buffers in the
+ * interim it will drop incoming frames, the idea being that it is
+ * better to drop a packet than to be inaccurate.
+ */
+
+
+/*
+ * Other statistics of possible interest
+ */
+volatile u_long packets_dropped; /* total number of packets dropped on reception */
+volatile u_long packets_ignored; /* packets received on wild card interface */
+volatile u_long packets_received; /* total number of packets received */
+u_long packets_sent; /* total number of packets sent */
+u_long packets_notsent; /* total number of packets which couldn't be sent */
+
+volatile u_long handler_calls; /* number of calls to interrupt handler */
+volatile u_long handler_pkts; /* number of pkts received by handler */
+u_long io_timereset; /* time counters were reset */
+
+/*
+ * Interface stuff
+ */
+struct interface *any_interface; /* default ipv4 interface */
+struct interface *any6_interface; /* default ipv6 interface */
+struct interface *loopback_interface; /* loopback ipv4 interface */
+struct interface *loopback6_interface; /* loopback ipv6 interface */
+struct interface inter_list[MAXINTERFACES]; /* Interface list */
+int ninterfaces; /* Total number of interfaces */
+int nwilds; /* Total number of wildcard intefaces */
+int wildipv4 = -1; /* Index into inter_list for IPv4 wildcard */
+int wildipv6 = -1; /* Index into inter_list for IPv6 wildcard */
+
+#ifdef REFCLOCK
+/*
+ * Refclock stuff. We keep a chain of structures with data concerning
+ * the guys we are doing I/O for.
+ */
+static struct refclockio *refio;
+#endif /* REFCLOCK */
+
+
+/*
+ * Define what the possible "soft" errors can be. These are non-fatal returns
+ * of various network related functions, like recv() and so on.
+ *
+ * For some reason, BSDI (and perhaps others) will sometimes return <0
+ * from recv() but will have errno==0. This is broken, but we have to
+ * work around it here.
+ */
+#define SOFT_ERROR(e) ((e) == EAGAIN || \
+ (e) == EWOULDBLOCK || \
+ (e) == EINTR || \
+ (e) == 0)
+
+/*
+ * File descriptor masks etc. for call to select
+ * Not needed for I/O Completion Ports
+ */
+fd_set activefds;
+int maxactivefd;
+
+static int create_sockets P((u_short));
+static SOCKET open_socket P((struct sockaddr_storage *, int, int));
+static void close_socket P((SOCKET));
+#ifdef REFCLOCK
+static void close_file P((SOCKET));
+#endif
+static char * fdbits P((int, fd_set *));
+static void set_reuseaddr P((int));
+
+typedef struct vsock vsock_t;
+
+struct vsock {
+ SOCKET fd;
+ ISC_LINK(vsock_t) link;
+};
+
+ISC_LIST(vsock_t) sockets_list;
+
+typedef struct remaddr remaddr_t;
+
+struct remaddr {
+ struct sockaddr_storage addr;
+ int if_index;
+ ISC_LINK(remaddr_t) link;
+};
+
+ISC_LIST(remaddr_t) remoteaddr_list;
+
+void add_socket_to_list P((SOCKET));
+void delete_socket_from_list P((SOCKET));
+void add_addr_to_list P((struct sockaddr_storage *, int));
+void delete_addr_from_list P((struct sockaddr_storage *));
+int find_addr_in_list P((struct sockaddr_storage *));
+int create_wildcards P((u_short));
+isc_boolean_t address_okay P((isc_interface_t *));
+void convert_isc_if P((isc_interface_t *, struct interface *, u_short));
+
+#ifdef SYS_WINNT
+/*
+ * Windows 2000 systems incorrectly cause UDP sockets using WASRecvFrom
+ * to not work correctly, returning a WSACONNRESET error when a WSASendTo
+ * fails with an "ICMP port unreachable" response and preventing the
+ * socket from using the WSARecvFrom in subsequent operations.
+ * The function below fixes this, but requires that Windows 2000
+ * Service Pack 2 or later be installed on the system. NT 4.0
+ * systems are not affected by this and work correctly.
+ * See Microsoft Knowledge Base Article Q263823 for details of this.
+ */
+isc_result_t
+connection_reset_fix(SOCKET fd) {
+ DWORD dwBytesReturned = 0;
+ BOOL bNewBehavior = FALSE;
+ DWORD status;
+
+ if(isc_win32os_majorversion() < 5)
+ return (ISC_R_SUCCESS); /* NT 4.0 has no problem */
+
+ /* disable bad behavior using IOCTL: SIO_UDP_CONNRESET */
+ status = WSAIoctl(fd, SIO_UDP_CONNRESET, &bNewBehavior,
+ sizeof(bNewBehavior), NULL, 0,
+ &dwBytesReturned, NULL, NULL);
+ if (status != SOCKET_ERROR)
+ return (ISC_R_SUCCESS);
+ else
+ return (ISC_R_UNEXPECTED);
+}
+#endif
+/*
+ * init_io - initialize I/O data structures and call socket creation routine
+ */
+void
+init_io(void)
+{
+#ifdef SYS_WINNT
+ init_transmitbuff();
+#endif /* SYS_WINNT */
+
+ /*
+ * Init buffer free list and stat counters
+ */
+ init_recvbuff(RECV_INIT);
+
+ packets_dropped = packets_received = 0;
+ packets_ignored = 0;
+ packets_sent = packets_notsent = 0;
+ handler_calls = handler_pkts = 0;
+ io_timereset = 0;
+ loopback_interface = NULL;
+ loopback6_interface = NULL;
+
+#ifdef REFCLOCK
+ refio = 0;
+#endif
+
+#if defined(HAVE_SIGNALED_IO)
+ (void) set_signal();
+#endif
+
+#ifdef SYS_WINNT
+ if (!Win32InitSockets())
+ {
+ netsyslog(LOG_ERR, "No useable winsock.dll: %m");
+ exit(1);
+ }
+#endif /* SYS_WINNT */
+
+ ISC_LIST_INIT(sockets_list);
+
+ ISC_LIST_INIT(remoteaddr_list);
+
+ /*
+ * Create the sockets
+ */
+ BLOCKIO();
+ (void) create_sockets(htons(NTP_PORT));
+ UNBLOCKIO();
+
+#ifdef DEBUG
+ if (debug)
+ printf("init_io: maxactivefd %d\n", maxactivefd);
+#endif
+}
+
+int
+create_wildcards(u_short port) {
+
+ int idx = 0;
+ /*
+ * create pseudo-interface with wildcard IPv4 address
+ */
+ inter_list[idx].sin.ss_family = AF_INET;
+ ((struct sockaddr_in*)&inter_list[idx].sin)->sin_addr.s_addr = htonl(INADDR_ANY);
+ ((struct sockaddr_in*)&inter_list[idx].sin)->sin_port = port;
+ (void) strncpy(inter_list[idx].name, "wildcard", sizeof(inter_list[idx].name));
+ inter_list[idx].mask.ss_family = AF_INET;
+ ((struct sockaddr_in*)&inter_list[idx].mask)->sin_addr.s_addr = htonl(~(u_int32)0);
+ inter_list[idx].bfd = INVALID_SOCKET;
+ inter_list[idx].num_mcast = 0;
+ inter_list[idx].received = 0;
+ inter_list[idx].sent = 0;
+ inter_list[idx].notsent = 0;
+ inter_list[idx].flags = INT_BROADCAST;
+ any_interface = &inter_list[idx];
+#if defined(MCAST)
+ /*
+ * enable possible multicast reception on the broadcast socket
+ */
+ inter_list[idx].bcast.ss_family = AF_INET;
+ ((struct sockaddr_in*)&inter_list[idx].bcast)->sin_port = port;
+ ((struct sockaddr_in*)&inter_list[idx].bcast)->sin_addr.s_addr = htonl(INADDR_ANY);
+#endif /* MCAST */
+ wildipv4 = idx;
+ idx++;
+
+#ifdef HAVE_IPV6
+ /*
+ * create pseudo-interface with wildcard IPv6 address
+ */
+ if (isc_net_probeipv6() == ISC_R_SUCCESS) {
+ inter_list[idx].sin.ss_family = AF_INET6;
+ ((struct sockaddr_in6*)&inter_list[idx].sin)->sin6_addr = in6addr_any;
+ ((struct sockaddr_in6*)&inter_list[idx].sin)->sin6_port = port;
+ (void) strncpy(inter_list[idx].name, "wildcard", sizeof(inter_list[idx].name));
+ inter_list[idx].mask.ss_family = AF_INET6;
+ memset(&((struct sockaddr_in6*)&inter_list[idx].mask)->sin6_addr.s6_addr, 0xff, sizeof(struct in6_addr));
+ inter_list[idx].bfd = INVALID_SOCKET;
+ inter_list[idx].num_mcast = 0;
+ inter_list[idx].received = 0;
+ inter_list[idx].sent = 0;
+ inter_list[idx].notsent = 0;
+ inter_list[idx].flags = 0;
+ any6_interface = &inter_list[idx];
+ wildipv6 = idx;
+ idx++;
+ }
+#endif
+ return (idx);
+}
+
+isc_boolean_t
+address_okay(isc_interface_t *isc_if) {
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("address_okay: listen Virtual: %d, IF name: %s, Up Flag: %d\n",
+ listen_to_virtual_ips, isc_if->name, (isc_if->flags & INTERFACE_F_UP));
+#endif
+
+ if (listen_to_virtual_ips == 0 && (strchr(isc_if->name, (int)':') != NULL))
+ return (ISC_FALSE);
+
+ /* XXXPDM This should be fixed later, but since we may not have set
+ * the UP flag, we at least get to use the interface.
+ * The UP flag is not always set so we don't do this right now.
+ */
+/* if ((isc_if->flags & INTERFACE_F_UP) == 0)
+ return (ISC_FALSE);
+*/
+ return (ISC_TRUE);
+}
+void
+convert_isc_if(isc_interface_t *isc_if, struct interface *itf, u_short port) {
+
+ if(isc_if->af == AF_INET) {
+ itf->sin.ss_family = (u_short) isc_if->af;
+ strcpy(itf->name, isc_if->name);
+ memcpy(&(((struct sockaddr_in*)&itf->sin)->sin_addr),
+ &(isc_if->address.type.in),
+ sizeof(struct in_addr));
+ ((struct sockaddr_in*)&itf->sin)->sin_port = port;
+
+ if((isc_if->flags & INTERFACE_F_BROADCAST) != 0) {
+ itf->flags |= INT_BROADCAST;
+ itf->bcast.ss_family = itf->sin.ss_family;
+ memcpy(&(((struct sockaddr_in*)&itf->bcast)->sin_addr),
+ &(isc_if->broadcast.type.in),
+ sizeof(struct in_addr));
+ ((struct sockaddr_in*)&itf->bcast)->sin_port = port;
+ }
+
+ itf->mask.ss_family = itf->sin.ss_family;
+ memcpy(&(((struct sockaddr_in*)&itf->mask)->sin_addr),
+ &(isc_if->netmask.type.in),
+ sizeof(struct in_addr));
+ ((struct sockaddr_in*)&itf->mask)->sin_port = port;
+
+ if (((isc_if->flags & INTERFACE_F_LOOPBACK) != 0) && (loopback_interface == NULL))
+ {
+ loopback_interface = itf;
+ }
+ }
+#ifdef HAVE_IPV6
+ else if (isc_if->af == AF_INET6) {
+ itf->sin.ss_family = (u_short) isc_if->af;
+ strcpy(itf->name, isc_if->name);
+ memcpy(&(((struct sockaddr_in6 *)&itf->sin)->sin6_addr),
+ &(isc_if->address.type.in6),
+ sizeof(struct in6_addr));
+ ((struct sockaddr_in6 *)&itf->sin)->sin6_port = port;
+
+ itf->mask.ss_family = itf->sin.ss_family;
+ memcpy(&(((struct sockaddr_in6 *)&itf->mask)->sin6_addr),
+ &(isc_if->netmask.type.in6),
+ sizeof(struct in6_addr));
+ ((struct sockaddr_in6 *)&itf->mask)->sin6_port = port;
+
+ if (((isc_if->flags & INTERFACE_F_LOOPBACK) != 0) && (loopback6_interface == NULL))
+ {
+ loopback6_interface = itf;
+ }
+ }
+#endif /* HAVE_IPV6 */
+
+ /* Process the rest of the flags */
+
+ if((isc_if->flags & INTERFACE_F_UP) != 0)
+ itf->flags |= INT_UP;
+ if((isc_if->flags & INTERFACE_F_LOOPBACK) != 0)
+ itf->flags |= INT_LOOPBACK;
+ if((isc_if->flags & INTERFACE_F_POINTTOPOINT) != 0)
+ itf->flags |= INT_PPP;
+}
+/*
+ * create_sockets - create a socket for each interface plus a default
+ * socket for when we don't know where to send
+ */
+static int
+create_sockets(
+ u_short port
+ )
+{
+ struct sockaddr_storage resmask;
+ int i;
+ isc_mem_t *mctx = NULL;
+ isc_interfaceiter_t *iter = NULL;
+ isc_boolean_t scan_ipv4 = ISC_FALSE;
+ isc_boolean_t scan_ipv6 = ISC_FALSE;
+ isc_result_t result;
+ int idx = 0;
+
+#ifdef DEBUG
+ if (debug)
+ printf("create_sockets(%d)\n", ntohs( (u_short) port));
+#endif
+
+ if (isc_net_probeipv6() == ISC_R_SUCCESS)
+ scan_ipv6 = ISC_TRUE;
+#ifdef HAVE_IPV6
+ else
+ netsyslog(LOG_ERR, "no IPv6 interfaces found");
+#endif
+
+ if (isc_net_probeipv4() == ISC_R_SUCCESS)
+ scan_ipv4 = ISC_TRUE;
+ else
+ netsyslog(LOG_ERR, "no IPv4 interfaces found");
+
+ nwilds = create_wildcards(port);
+ idx = nwilds;
+
+ result = isc_interfaceiter_create(mctx, &iter);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ for (result = isc_interfaceiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = isc_interfaceiter_next(iter))
+ {
+ isc_interface_t isc_if;
+ unsigned int family;
+
+ result = isc_interfaceiter_current(iter, &isc_if);
+ if (result != ISC_R_SUCCESS)
+ break;
+
+ /* See if we have a valid family to use */
+ family = isc_if.address.family;
+ if (family != AF_INET && family != AF_INET6)
+ continue;
+ if (scan_ipv4 == ISC_FALSE && family == AF_INET)
+ continue;
+ if (scan_ipv6 == ISC_FALSE && family == AF_INET6)
+ continue;
+
+ /* Check to see if we are going to use the interface */
+ if (address_okay(&isc_if) == ISC_TRUE) {
+ convert_isc_if(&isc_if, &inter_list[idx], port);
+ inter_list[idx].fd = INVALID_SOCKET;
+ inter_list[idx].bfd = INVALID_SOCKET;
+ inter_list[idx].num_mcast = 0;
+ inter_list[idx].received = 0;
+ inter_list[idx].sent = 0;
+ inter_list[idx].notsent = 0;
+ idx++;
+ }
+ }
+ isc_interfaceiter_destroy(&iter);
+
+ ninterfaces = idx;
+ /*
+ * I/O Completion Ports don't care about the select and FD_SET
+ */
+#ifndef HAVE_IO_COMPLETION_PORT
+ maxactivefd = 0;
+ FD_ZERO(&activefds);
+#endif
+ for (i = 0; i < ninterfaces; i++) {
+ inter_list[i].fd = open_socket(&inter_list[i].sin,
+ inter_list[i].flags & INT_BROADCAST, 0);
+ if (inter_list[i].bfd != INVALID_SOCKET)
+ msyslog(LOG_INFO, "Listening on interface %s, %s#%d",
+ inter_list[i].name,
+ stoa((&inter_list[i].sin)),
+ NTP_PORT);
+ if ((inter_list[i].flags & INT_BROADCAST) &&
+ inter_list[i].bfd != INVALID_SOCKET)
+ msyslog(LOG_INFO, "Listening on broadcast address %s#%d",
+ stoa((&inter_list[i].bcast)),
+ NTP_PORT);
+#if defined (HAVE_IO_COMPLETION_PORT)
+ if (inter_list[i].fd != INVALID_SOCKET) {
+ io_completion_port_add_socket(inter_list[i].fd, &inter_list[i]);
+ }
+#endif
+ }
+
+ /*
+ * Now that we have opened all the sockets, turn off the reuse
+ * flag for security.
+ */
+ set_reuseaddr(0);
+
+ /*
+ * Blacklist all bound interface addresses
+ * Wildcard interfaces are ignored.
+ */
+
+ for (i = nwilds; i < ninterfaces; i++) {
+ SET_HOSTMASK(&resmask, inter_list[i].sin.ss_family);
+ hack_restrict(RESTRICT_FLAGS, &inter_list[i].sin, &resmask,
+ RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE);
+ }
+
+ /*
+ * Calculate the address hash for each interface address.
+ */
+ for (i = 0; i < ninterfaces; i++) {
+ inter_list[i].addr_refid = addr2refid(&inter_list[i].sin);
+ }
+
+
+#ifdef DEBUG
+ if (debug > 1) {
+ printf("create_sockets: ninterfaces=%d\n", ninterfaces);
+ for (i = 0; i < ninterfaces; i++) {
+ printf("interface %d: fd=%d, bfd=%d, name=%.8s, flags=0x%x\n",
+ i,
+ inter_list[i].fd,
+ inter_list[i].bfd,
+ inter_list[i].name,
+ inter_list[i].flags);
+ /* Leave these as three printf calls. */
+ printf(" sin=%s",
+ stoa((&inter_list[i].sin)));
+ if (inter_list[i].flags & INT_BROADCAST)
+ printf(" bcast=%s,",
+ stoa((&inter_list[i].bcast)));
+ printf(" mask=%s\n",
+ stoa((&inter_list[i].mask)));
+ }
+ }
+#endif
+ return ninterfaces;
+}
+
+/*
+ * io_setbclient - open the broadcast client sockets
+ */
+void
+io_setbclient(void)
+{
+ int i;
+
+#ifdef OPEN_BCAST_SOCKET
+ set_reuseaddr(1);
+#endif
+ for (i = nwilds; i < ninterfaces; i++) {
+ /* Only IPv4 addresses are valid for broadcast */
+ if (inter_list[i].bcast.ss_family != AF_INET)
+ continue;
+
+ /* Is this a broadcast address? */
+ if (!(inter_list[i].flags & INT_BROADCAST))
+ continue;
+
+ /* Do we already have the broadcast address open? */
+ if (inter_list[i].flags & INT_BCASTOPEN)
+ continue;
+
+#ifdef SYS_SOLARIS
+ inter_list[i].bcast.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif
+#ifdef OPEN_BCAST_SOCKET /* Was: !SYS_DOMAINOS && !SYS_LINUX */
+ inter_list[i].bfd = open_socket(&inter_list[i].bcast,
+ INT_BROADCAST, 1);
+ if (inter_list[i].bfd != INVALID_SOCKET) {
+ inter_list[i].flags |= INT_BCASTOPEN;
+#if defined (HAVE_IO_COMPLETION_PORT)
+ io_completion_port_add_socket(inter_list[i].bfd, &inter_list[i]);
+#endif
+ }
+#ifdef DEBUG
+ if (debug) {
+ if (inter_list[i].bfd != INVALID_SOCKET)
+ printf("io_setbclient: Opened broadcast client on interface %d, socket: %d\n",
+ i, inter_list[i].bfd);
+ else
+ printf("io_setbclient: Unable to Open broadcast client on interface %d\n",
+ i);
+ }
+#endif
+#endif
+ }
+#ifdef OPEN_BCAST_SOCKET
+ set_reuseaddr(0);
+#endif
+#ifdef DEBUG
+ if (debug)
+ printf("io_setbclient: Opened broadcast clients\n");
+#endif
+}
+
+/*
+ * set_reuseaddr() - set/clear REUSEADDR on all sockets
+ * NB possible hole - should we be doing this on broadcast
+ * fd's also?
+ */
+static void
+set_reuseaddr(int flag) {
+ int i;
+
+ for (i=0; i < ninterfaces; i++) {
+ /*
+ * if inter_list[ n ].fd is -1, we might have a adapter
+ * configured but not present
+ */
+ if (inter_list[i].fd != INVALID_SOCKET) {
+ if (setsockopt(inter_list[i].fd, SOL_SOCKET,
+ SO_REUSEADDR, (char *)&flag,
+ sizeof(flag))) {
+ netsyslog(LOG_ERR, "set_reuseaddr: setsockopt(SO_REUSEADDR, %s) failed: %m", flag ? "on" : "off");
+ }
+ }
+ }
+}
+
+
+/*
+ * io_multicast_add() - add multicast group address
+ */
+void
+io_multicast_add(
+ struct sockaddr_storage addr
+ )
+{
+#ifdef MCAST
+ struct ip_mreq mreq;
+ int i = ninterfaces; /* Use the next interface */
+ u_int32 haddr = ntohl(((struct sockaddr_in*)&addr)->sin_addr.s_addr);
+ struct in_addr iaddr;
+ SOCKET s;
+ struct sockaddr_in *sinp;
+
+#ifdef HAVE_IPV6
+ struct ipv6_mreq mreq6;
+ struct in6_addr iaddr6;
+ struct sockaddr_in6 *sin6p;
+#endif /* HAVE_IPV6 */
+
+ switch (addr.ss_family)
+ {
+ case AF_INET :
+ iaddr = (((struct sockaddr_in*)&addr)->sin_addr);
+ if (!IN_CLASSD(haddr)) {
+ netsyslog(LOG_ERR,
+ "multicast address %s not class D",
+ inet_ntoa(iaddr));
+ return;
+ }
+ for (i = nwilds; i < ninterfaces; i++) {
+ /* Be sure it's the correct family */
+ if (inter_list[i].sin.ss_family != AF_INET)
+ continue;
+ /* Already have this address */
+ if (SOCKCMP(&inter_list[i].sin, &addr))
+ return;
+ /* found a free slot */
+ if (SOCKNUL(&inter_list[i].sin) &&
+ inter_list[i].fd <= 0 && inter_list[i].bfd <= 0 &&
+ inter_list[i].flags == 0)
+ break;
+ }
+ sinp = (struct sockaddr_in*)&(inter_list[i].sin);
+ memset((char *)&mreq, 0, sizeof(mreq));
+ memset((char *)&inter_list[i], 0, sizeof(struct interface));
+ sinp->sin_family = AF_INET;
+ sinp->sin_addr = iaddr;
+ sinp->sin_port = htons(NTP_PORT);
+
+ /*
+ * Try opening a socket for the specified class D address. This
+ * works under SunOS 4.x, but not OSF1 .. :-(
+ */
+ set_reuseaddr(1);
+ s = open_socket((struct sockaddr_storage*)sinp, 0, 1);
+ set_reuseaddr(0);
+ if (s == INVALID_SOCKET) {
+ memset((char *)&inter_list[i], 0, sizeof(struct interface));
+ if (wildipv4 >= 0) {
+ i = wildipv4;
+ /* HACK ! -- stuff in an address */
+ inter_list[i].bcast = addr;
+ netsyslog(LOG_ERR,
+ "...multicast address %s using wildcard socket",
+ inet_ntoa(iaddr));
+ } else {
+ netsyslog(LOG_ERR,
+ "No wildcard socket available to use for address %s",
+ inet_ntoa(iaddr));
+ return;
+ }
+ } else {
+ inter_list[i].fd = s;
+ inter_list[i].bfd = INVALID_SOCKET;
+ (void) strncpy(inter_list[i].name, "multicast",
+ sizeof(inter_list[i].name));
+ ((struct sockaddr_in*)&inter_list[i].mask)->sin_addr.s_addr = htonl(~(u_int32)0);
+#if defined (HAVE_IO_COMPLETION_PORT)
+ io_completion_port_add_socket(inter_list[i].fd, &inter_list[i]);
+#endif
+ }
+
+ /*
+ * enable reception of multicast packets
+ */
+ mreq.imr_multiaddr = iaddr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) == -1)
+ netsyslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP fails: %m for %x / %x (%s)",
+ mreq.imr_multiaddr.s_addr,
+ mreq.imr_interface.s_addr, inet_ntoa(iaddr));
+ inter_list[i].flags |= INT_MULTICAST;
+ inter_list[i].num_mcast++;
+ if (i >= ninterfaces)
+ ninterfaces = i+1;
+
+ add_addr_to_list(&addr, i);
+ break;
+
+#ifdef HAVE_IPV6
+ case AF_INET6 :
+
+ iaddr6 = ((struct sockaddr_in6*)&addr)->sin6_addr;
+ if (!IN6_IS_ADDR_MULTICAST(&iaddr6)) {
+ netsyslog(LOG_ERR,
+ "address %s not IPv6 multicast address",
+ stoa(&addr));
+ return;
+ }
+ for (i = nwilds; i < ninterfaces; i++) {
+ /* Be sure it's the correct family */
+ if(inter_list[i].sin.ss_family != AF_INET6)
+ continue;
+ /* Already have this address */
+ if (SOCKCMP(&inter_list[i].sin, &addr))
+ return;
+ /* found a free slot */
+ if (SOCKNUL(&inter_list[i].sin) &&
+ inter_list[i].fd <= 0 && inter_list[i].bfd <= 0 &&
+ inter_list[i].flags == 0)
+ break;
+ }
+ sin6p = (struct sockaddr_in6*)&inter_list[i].sin;
+ memset((char *)&mreq6, 0, sizeof(mreq6));
+ memset((char *)&inter_list[i], 0, sizeof(struct interface));
+ sin6p->sin6_family = AF_INET6;
+ sin6p->sin6_addr = iaddr6;
+ sin6p->sin6_port = htons(NTP_PORT);
+
+ /*
+ * Try opening a socket for the specified class D address. This
+ * works under SunOS 4.x, but not OSF1 .. :-(
+ */
+ set_reuseaddr(1);
+ s = open_socket((struct sockaddr_storage*)sin6p, 0, 1);
+ set_reuseaddr(0);
+ if(s == INVALID_SOCKET){
+ memset((char *)&inter_list[i], 0, sizeof(struct interface));
+ if (wildipv6 >= 0) {
+ i = wildipv6;
+ /* HACK ! -- stuff in an address */
+ inter_list[i].bcast = addr;
+ netsyslog(LOG_ERR,
+ "...multicast address %s using wildcard socket",
+ stoa(&addr));
+ } else {
+ netsyslog(LOG_ERR,
+ "No wildcard socket available to use for address %s",
+ stoa(&addr));
+ return;
+ }
+ } else {
+ inter_list[i].fd = s;
+ inter_list[i].bfd = INVALID_SOCKET;
+ (void)strncpy(inter_list[i].name, "multicast",
+ sizeof(inter_list[i].name));
+ memset(&(((struct sockaddr_in6*)&inter_list[i].mask)->sin6_addr), 1, sizeof(struct in6_addr));
+#if defined (HAVE_IO_COMPLETION_PORT)
+ io_completion_port_add_socket(inter_list[i].fd, &inter_list[i]);
+#endif
+ }
+
+ /*
+ * enable reception of multicast packets
+ */
+ mreq6.ipv6mr_multiaddr = iaddr6;
+ mreq6.ipv6mr_interface = 0;
+ if(setsockopt(inter_list[i].fd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ (char *)&mreq6, sizeof(mreq6)) == -1)
+ netsyslog(LOG_ERR,
+ "setsockopt IPV6_JOIN_GROUP fails: %m on interface %d(%s)",
+ mreq6.ipv6mr_interface, stoa(&addr));
+ inter_list[i].flags |= INT_MULTICAST;
+ inter_list[i].num_mcast++;
+ if(i >= ninterfaces)
+ ninterfaces = i+1;
+
+ add_addr_to_list(&addr, i);
+ break;
+#endif /* HAVE_IPV6 */
+ }
+
+#ifdef DEBUG
+ if (debug)
+ printf("io_multicast_add %s\n", stoa(&addr));
+#endif
+#else /* MCAST */
+ netsyslog(LOG_ERR,
+ "cannot add multicast address %s as no MCAST support",
+ stoa(&addr));
+#endif /* MCAST */
+}
+
+/*
+ * io_unsetbclient - close the broadcast client sockets
+ */
+void
+io_unsetbclient(void)
+{
+ int i;
+
+ for (i = nwilds; i < ninterfaces; i++)
+ {
+ if (!(inter_list[i].flags & INT_BCASTOPEN))
+ continue;
+ close_socket(inter_list[i].bfd);
+ inter_list[i].bfd = INVALID_SOCKET;
+ inter_list[i].flags &= ~INT_BCASTOPEN;
+ }
+}
+
+
+/*
+ * io_multicast_del() - delete multicast group address
+ */
+void
+io_multicast_del(
+ struct sockaddr_storage addr
+ )
+{
+#ifdef MCAST
+ int i;
+ struct ip_mreq mreq;
+ u_int32 haddr;
+
+#ifdef HAVE_IPV6
+ struct ipv6_mreq mreq6;
+ struct in6_addr haddr6;
+#endif /* HAVE_IPV6 */
+
+ switch (addr.ss_family)
+ {
+ case AF_INET :
+
+ haddr = ntohl(((struct sockaddr_in*)&addr)->sin_addr.s_addr);
+
+ if (!IN_CLASSD(haddr))
+ {
+ netsyslog(LOG_ERR,
+ "invalid multicast address %s", stoa(&addr));
+ return;
+ }
+
+ /*
+ * Disable reception of multicast packets
+ */
+ mreq.imr_multiaddr = ((struct sockaddr_in*)&addr)->sin_addr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ for (i = 0; i < ninterfaces; i++)
+ {
+ /* Be sure it's the correct family */
+ if (inter_list[i].sin.ss_family != AF_INET)
+ continue;
+ if (!(inter_list[i].flags & INT_MULTICAST))
+ continue;
+ if (!(inter_list[i].fd < 0))
+ continue;
+ if (!SOCKCMP(&addr, &inter_list[i].sin))
+ continue;
+ if (i != wildipv4)
+ {
+ /* we have an explicit fd, so we can close it */
+ close_socket(inter_list[i].fd);
+ memset((char *)&inter_list[i], 0, sizeof(struct interface));
+ inter_list[i].fd = INVALID_SOCKET;
+ inter_list[i].bfd = INVALID_SOCKET;
+ }
+ else
+ {
+ /* We are sharing "any address" port :-( Don't close it! */
+ if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) == -1)
+ netsyslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails on address: %s %m",
+ stoa(&addr));
+ inter_list[i].num_mcast--;
+ /* If there are none left negate the Multicast flag */
+ if(inter_list[i].num_mcast == 0)
+ inter_list[i].flags &= ~INT_MULTICAST;
+ }
+ }
+ break;
+
+#ifdef HAVE_IPV6
+ case AF_INET6 :
+ haddr6 = ((struct sockaddr_in6*)&addr)->sin6_addr;
+
+ if (!IN6_IS_ADDR_MULTICAST(&haddr6))
+ {
+ netsyslog(LOG_ERR,
+ "invalid multicast address %s", stoa(&addr));
+ return;
+ }
+
+ /*
+ * Disable reception of multicast packets
+ */
+ mreq6.ipv6mr_multiaddr = ((struct sockaddr_in6*)&addr)->sin6_addr;
+ mreq6.ipv6mr_interface = 0;
+ for (i = 0; i < ninterfaces; i++)
+ {
+ /* Be sure it's the correct family */
+ if (inter_list[i].sin.ss_family != AF_INET6)
+ continue;
+ if (!(inter_list[i].flags & INT_MULTICAST))
+ continue;
+ if (!(inter_list[i].fd < 0))
+ continue;
+ if (!SOCKCMP(&addr, &inter_list[i].sin))
+ continue;
+ if (i != wildipv6)
+ {
+ /* we have an explicit fd, so we can close it */
+ close_socket(inter_list[i].fd);
+ memset((char *)&inter_list[i], 0, sizeof(struct interface));
+ inter_list[i].fd = INVALID_SOCKET;
+ inter_list[i].bfd = INVALID_SOCKET;
+ }
+ else
+ {
+ /* We are sharing "any address" port :-( Don't close it! */
+ if (setsockopt(inter_list[i].fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+ (char *)&mreq6, sizeof(mreq6)) == -1)
+ netsyslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails on address %s: %m",
+ stoa(&addr));
+ /* If there are none left negate the Multicast flag */
+ if(inter_list[i].num_mcast == 0)
+ inter_list[i].flags &= ~INT_MULTICAST;
+ }
+ }
+ break;
+#endif /* HAVE_IPV6 */
+ }/* switch */
+ delete_addr_from_list(&addr);
+
+#else /* not MCAST */
+ netsyslog(LOG_ERR, "this function requires multicast kernel");
+#endif /* not MCAST */
+}
+
+
+/*
+ * open_socket - open a socket, returning the file descriptor
+ */
+
+static SOCKET
+open_socket(
+ struct sockaddr_storage *addr,
+ int flags,
+ int turn_off_reuse
+ )
+{
+ int errval;
+ SOCKET fd;
+ int on = 1, off = 0;
+#if defined(IPTOS_LOWDELAY) && defined(IPPROTO_IP) && defined(IP_TOS)
+ int tos;
+#endif /* IPTOS_LOWDELAY && IPPROTO_IP && IP_TOS */
+
+ if ((addr->ss_family == AF_INET6) && (isc_net_probeipv6() != ISC_R_SUCCESS))
+ return (INVALID_SOCKET);
+
+ /* create a datagram (UDP) socket */
+#ifndef SYS_WINNT
+ if ( (fd = socket(addr->ss_family, SOCK_DGRAM, 0)) < 0) {
+ errval = errno;
+ if(addr->ss_family == AF_INET)
+ netsyslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed on address %s: %m",
+ stoa(addr));
+ else if(addr->ss_family == AF_INET6)
+ netsyslog(LOG_ERR, "socket(AF_INET6, SOCK_DGRAM, 0) failed on address %s: %m",
+ stoa(addr));
+ if (errval == EPROTONOSUPPORT || errval == EAFNOSUPPORT ||
+ errval == EPFNOSUPPORT)
+ return (INVALID_SOCKET);
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else
+ if ( (fd = socket(addr->ss_family, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
+ errval = WSAGetLastError();
+ if(addr->ss_family == AF_INET)
+ netsyslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed on address %s: %m",
+ stoa(addr));
+ else if(addr->ss_family == AF_INET6)
+ netsyslog(LOG_ERR, "socket(AF_INET6, SOCK_DGRAM, 0) failed on address %s: %m",
+ stoa(addr));
+ if (errval == WSAEPROTONOSUPPORT || errval == WSAEAFNOSUPPORT ||
+ errval == WSAEPFNOSUPPORT)
+ return (INVALID_SOCKET);
+ exit(1);
+ /*NOTREACHED*/
+ }
+ if (connection_reset_fix(fd) != ISC_R_SUCCESS) {
+ netsyslog(LOG_ERR, "connection_reset_fix(fd) failed on address %s: %m",
+ stoa(addr));
+ }
+
+#endif /* SYS_WINNT */
+
+ /* set SO_REUSEADDR since we will be binding the same port
+ number on each interface */
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on)))
+ {
+ netsyslog(LOG_ERR, "setsockopt SO_REUSEADDR on fails on address %s: %m",
+ stoa(addr));
+ }
+
+#if defined(IPTOS_LOWDELAY) && defined(IPPROTO_IP) && defined(IP_TOS)
+ /* set IP_TOS to minimize packet delay */
+ tos = IPTOS_LOWDELAY;
+ if (addr->ss_family == AF_INET)
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(tos)) < 0)
+ {
+ netsyslog(LOG_ERR, "setsockopt IPTOS_LOWDELAY on fails on address %s: %m",
+ stoa(addr));
+ }
+
+#if defined(IPV6_V6ONLY)
+ if (addr->ss_family == AF_INET6)
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char*)&on, sizeof(on)))
+ {
+ netsyslog(LOG_ERR, "setsockopt IPV6_V6ONLY on fails on address %s: %m",
+ stoa(addr));
+ }
+#else /* IPV6_V6ONLY */
+#if defined(IPV6_BINDV6ONLY)
+ if (addr->ss_family == AF_INET6)
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ (char*)&on, sizeof(on)))
+ {
+ netsyslog(LOG_ERR,
+ "setsockopt IPV6_BINDV6ONLY on fails on address %s: %m",
+ stoa(addr));
+ }
+#endif /* IPV6_BINDV6ONLY */
+#endif /* IPV6_V6ONLY */
+
+#endif /* IPTOS_LOWDELAY && IPPROTO_IP && IP_TOS */
+
+ /*
+ * bind the local address.
+ */
+ if (bind(fd, (struct sockaddr *)addr, SOCKLEN(addr)) < 0) {
+ char buff[160];
+
+ if(addr->ss_family == AF_INET)
+ sprintf(buff,
+ "bind() fd %d, family %d, port %d, addr %s, in_classd=%d flags=%d fails: %%m",
+ fd, addr->ss_family, (int)ntohs(((struct sockaddr_in*)addr)->sin_port),
+ stoa(addr),
+ IN_CLASSD(ntohl(((struct sockaddr_in*)addr)->sin_addr.s_addr)), flags);
+ else if(addr->ss_family == AF_INET6)
+ sprintf(buff,
+ "bind() fd %d, family %d, port %d, addr %s, in6_is_addr_multicast=%d flags=%d fails: %%m",
+ fd, addr->ss_family, (int)ntohs(((struct sockaddr_in6*)addr)->sin6_port),
+ stoa(addr),
+ IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr), flags);
+ else return INVALID_SOCKET;
+
+ netsyslog(LOG_ERR, buff);
+ closesocket(fd);
+
+ /*
+ * soft fail if opening a multicast address
+ */
+ if(addr->ss_family == AF_INET){
+ if(IN_CLASSD(ntohl(((struct sockaddr_in*)addr)->sin_addr.s_addr)))
+ return (INVALID_SOCKET);
+ }
+ else {
+ if(IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)addr)->sin6_addr))
+ return (INVALID_SOCKET);
+ }
+#if 0
+ exit(1);
+#else
+ return INVALID_SOCKET;
+#endif
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("bind() fd %d, family %d, port %d, addr %s, flags=%d\n",
+ fd,
+ addr->ss_family,
+ (int)ntohs(((struct sockaddr_in*)addr)->sin_port),
+ stoa(addr),
+ flags);
+#endif
+
+ /*
+ * I/O Completion Ports don't care about the select and FD_SET
+ */
+#ifndef HAVE_IO_COMPLETION_PORT
+ if (fd > maxactivefd)
+ maxactivefd = fd;
+ FD_SET(fd, &activefds);
+#endif
+ add_socket_to_list(fd);
+ /*
+ * set non-blocking,
+ */
+
+#ifdef USE_FIONBIO
+ /* in vxWorks we use FIONBIO, but the others are defined for old systems, so
+ * all hell breaks loose if we leave them defined
+ */
+#undef O_NONBLOCK
+#undef FNDELAY
+#undef O_NDELAY
+#endif
+
+#if defined(O_NONBLOCK) /* POSIX */
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ netsyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails on address %s: %m",
+ stoa(addr));
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(FNDELAY)
+ if (fcntl(fd, F_SETFL, FNDELAY) < 0)
+ {
+ netsyslog(LOG_ERR, "fcntl(FNDELAY) fails on address %s: %m",
+ stoa(addr));
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(O_NDELAY) /* generally the same as FNDELAY */
+ if (fcntl(fd, F_SETFL, O_NDELAY) < 0)
+ {
+ netsyslog(LOG_ERR, "fcntl(O_NDELAY) fails on address %s: %m",
+ stoa(addr));
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(FIONBIO)
+# if defined(VMS)
+ if (ioctl(fd,FIONBIO,&on) < 0)
+# elif defined(SYS_WINNT)
+ if (ioctlsocket(fd,FIONBIO,(u_long *) &on) == SOCKET_ERROR)
+# else
+ if (ioctl(fd,FIONBIO,&on) < 0)
+# endif
+ {
+ netsyslog(LOG_ERR, "ioctl(FIONBIO) fails on address %s: %m",
+ stoa(addr));
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(FIOSNBIO)
+ if (ioctl(fd,FIOSNBIO,&on) < 0)
+ {
+ netsyslog(LOG_ERR, "ioctl(FIOSNBIO) fails on address %s: %m",
+ stoa(addr));
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else
+# include "Bletch: Need non-blocking I/O!"
+#endif
+
+#ifdef HAVE_SIGNALED_IO
+ init_socket_sig(fd);
+#endif /* not HAVE_SIGNALED_IO */
+
+ /*
+ * Turn off the SO_REUSEADDR socket option. It apparently
+ * causes heartburn on systems with multicast IP installed.
+ * On normal systems it only gets looked at when the address
+ * is being bound anyway..
+ */
+ if (turn_off_reuse)
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&off, sizeof(off)))
+ {
+ netsyslog(LOG_ERR, "setsockopt SO_REUSEADDR off fails on address %s: %m",
+ stoa(addr));
+ }
+
+#ifdef SO_BROADCAST
+ /* if this interface can support broadcast, set SO_BROADCAST */
+ if (flags & INT_BROADCAST)
+ {
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
+ (char *)&on, sizeof(on)))
+ {
+ netsyslog(LOG_ERR, "setsockopt(SO_BROADCAST) on address %s: %m",
+ stoa(addr));
+ }
+ }
+#endif /* SO_BROADCAST */
+
+#if !defined(SYS_WINNT) && !defined(VMS)
+# ifdef DEBUG
+ if (debug > 1)
+ printf("flags for fd %d: 0%o\n", fd,
+ fcntl(fd, F_GETFL, 0));
+# endif
+#endif /* SYS_WINNT || VMS */
+
+ return fd;
+}
+
+
+/*
+ * close_socket - close a socket and remove from the activefd list
+ */
+static void
+close_socket(
+ SOCKET fd
+ )
+{
+ SOCKET i, newmax;
+
+ (void) closesocket(fd);
+
+ /*
+ * I/O Completion Ports don't care about select and fd_set
+ */
+#ifndef HAVE_IO_COMPLETION_PORT
+ FD_CLR( (u_int) fd, &activefds);
+
+ if (fd == maxactivefd) {
+ newmax = 0;
+ for (i = 0; i < maxactivefd; i++)
+ if (FD_ISSET(i, &activefds))
+ newmax = i;
+ maxactivefd = newmax;
+ }
+#endif
+ delete_socket_from_list(fd);
+
+}
+
+
+/*
+ * close_file - close a file and remove from the activefd list
+ * added 1/31/1997 Greg Schueman for Windows NT portability
+ */
+#ifdef REFCLOCK
+static void
+close_file(
+ SOCKET fd
+ )
+{
+ int i, newmax;
+
+ (void) close(fd);
+ /*
+ * I/O Completion Ports don't care about select and fd_set
+ */
+#ifndef HAVE_IO_COMPLETION_PORT
+ FD_CLR( (u_int) fd, &activefds);
+
+ if (fd == maxactivefd) {
+ newmax = 0;
+ for (i = 0; i < maxactivefd; i++)
+ if (FD_ISSET(i, &activefds))
+ newmax = i;
+ maxactivefd = newmax;
+ }
+#endif
+ delete_socket_from_list(fd);
+
+}
+#endif
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the specified destination. Maintain a
+ * send error cache so that only the first consecutive error for a
+ * destination is logged.
+ */
+void
+sendpkt(
+ struct sockaddr_storage *dest,
+ struct interface *inter,
+ int ttl,
+ struct pkt *pkt,
+ int len
+ )
+{
+ int cc, slot;
+#ifdef SYS_WINNT
+ DWORD err;
+#endif /* SYS_WINNT */
+
+ /*
+ * Send error caches. Empty slots have port == 0
+ * Set ERRORCACHESIZE to 0 to disable
+ */
+ struct cache {
+ u_short port;
+ struct in_addr addr;
+ };
+
+#ifdef HAVE_IPV6
+ struct cache6 {
+ u_short port;
+ struct in6_addr addr;
+ };
+#endif /* HAVE_IPV6 */
+
+#ifndef ERRORCACHESIZE
+#define ERRORCACHESIZE 8
+#endif
+#if ERRORCACHESIZE > 0
+ static struct cache badaddrs[ERRORCACHESIZE];
+#ifdef HAVE_IPV6
+ static struct cache6 badaddrs6[ERRORCACHESIZE];
+#endif /* HAVE_IPV6 */
+#else
+#define badaddrs ((struct cache *)0) /* Only used in empty loops! */
+#ifdef HAVE_IPV6
+#define badaddrs6 ((struct cache6 *)0) /* Only used in empty loops! */
+#endif /* HAVE_IPV6 */
+#endif
+#ifdef DEBUG
+ if (debug > 1)
+ printf("%ssendpkt(fd=%d dst=%s, src=%s, ttl=%d, len=%d)\n",
+ (ttl >= 0) ? "\tMCAST\t*****" : "",
+ inter->fd, stoa(dest),
+ stoa(&inter->sin), ttl, len);
+#endif
+
+#ifdef MCAST
+
+ switch (inter->sin.ss_family) {
+
+ case AF_INET :
+
+ /*
+ * for the moment we use the bcast option to set multicast ttl
+ */
+ if (ttl > 0 && ttl != inter->last_ttl) {
+
+ /*
+ * set the multicast ttl for outgoing packets
+ */
+ if (setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *) &ttl, sizeof(ttl)) != 0) {
+ netsyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails on address %s: %m",
+ stoa(&inter->sin));
+ }
+ else
+ inter->last_ttl = ttl;
+ }
+ break;
+
+#ifdef HAVE_IPV6
+ case AF_INET6 :
+
+ /*
+ * for the moment we use the bcast option to set
+ * multicast max hops
+ */
+ if (ttl > 0 && ttl != inter->last_ttl) {
+
+ /*
+ * set the multicast ttl for outgoing packets
+ */
+ if (setsockopt(inter->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &ttl, sizeof(ttl)) == -1)
+ netsyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails on address %s: %m",
+ stoa(&inter->sin));
+ else
+ inter->last_ttl = ttl;
+ }
+ break;
+#endif /* HAVE_IPV6 */
+
+ default :
+ exit(1);
+
+ }
+
+
+#endif /* MCAST */
+
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if(dest->ss_family == AF_INET) {
+ if (badaddrs[slot].port == ((struct sockaddr_in*)dest)->sin_port &&
+ badaddrs[slot].addr.s_addr == ((struct sockaddr_in*)dest)->sin_addr.s_addr)
+ break;
+ }
+#ifdef HAVE_IPV6
+ else if (dest->ss_family == AF_INET6) {
+ if (badaddrs6[slot].port == ((struct sockaddr_in6*)dest)->sin6_port &&
+ badaddrs6[slot].addr.s6_addr == ((struct sockaddr_in6*)dest)->sin6_addr.s6_addr)
+ break;
+ }
+#endif /* HAVE_IPV6 */
+ else exit(1); /* address family not supported yet */
+
+#if defined(HAVE_IO_COMPLETION_PORT)
+ err = io_completion_port_sendto(inter, pkt, len, dest);
+ if (err != ERROR_SUCCESS)
+#else
+#ifdef SIM
+ cc = srvr_rply(&ntp_node, dest, inter, pkt);
+#else /* SIM */
+ cc = sendto(inter->fd, (char *)pkt, (unsigned int)len, 0, (struct sockaddr *)dest,
+ SOCKLEN(dest));
+#endif /* SIM */
+ if (cc == -1)
+#endif
+ {
+ inter->notsent++;
+ packets_notsent++;
+#if defined(HAVE_IO_COMPLETION_PORT)
+ err = WSAGetLastError();
+ if (err != WSAEWOULDBLOCK && err != WSAENOBUFS && slot < 0)
+#else
+ if (errno != EWOULDBLOCK && errno != ENOBUFS && slot < 0)
+#endif
+ {
+ /*
+ * Remember this, if there's an empty slot
+ */
+ switch (dest->ss_family) {
+
+ case AF_INET :
+
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs[slot].port == 0)
+ {
+ badaddrs[slot].port = SRCPORT(dest);
+ badaddrs[slot].addr = ((struct sockaddr_in*)dest)->sin_addr;
+ break;
+ }
+ break;
+
+#ifdef HAVE_IPV6
+ case AF_INET6 :
+
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs6[slot].port == 0)
+ {
+ badaddrs6[slot].port = SRCPORT(dest);
+ badaddrs6[slot].addr = ((struct sockaddr_in6*)dest)->sin6_addr;
+ break;
+ }
+ break;
+#endif /* HAVE_IPV6 */
+
+ default :
+ exit(1);
+ }
+
+ netsyslog(LOG_ERR, "sendto(%s): %m", stoa(dest));
+ }
+ }
+ else
+ {
+ inter->sent++;
+ packets_sent++;
+ /*
+ * He's not bad any more
+ */
+ if (slot >= 0)
+ {
+ netsyslog(LOG_INFO, "Connection re-established to %s", stoa(dest));
+ switch (dest->ss_family) {
+ case AF_INET :
+ badaddrs[slot].port = 0;
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6 :
+ badaddrs6[slot].port = 0;
+ break;
+#endif /* HAVE_IPV6 */
+ }
+ }
+ }
+}
+
+#if !defined(HAVE_IO_COMPLETION_PORT)
+/*
+ * fdbits - generate ascii representation of fd_set (FAU debug support)
+ * HFDF format - highest fd first.
+ */
+static char *
+fdbits(
+ int count,
+ fd_set *set
+ )
+{
+ static char buffer[256];
+ char * buf = buffer;
+
+ count = (count < 256) ? count : 255;
+
+ while (count >= 0)
+ {
+ *buf++ = FD_ISSET(count, set) ? '#' : '-';
+ count--;
+ }
+ *buf = '\0';
+
+ return buffer;
+}
+
+/*
+ * input_handler - receive packets asynchronously
+ */
+void
+input_handler(
+ l_fp *cts
+ )
+{
+ register int i, n;
+ register struct recvbuf *rb;
+ register int doing;
+ register SOCKET fd;
+ struct timeval tvzero;
+ int fromlen;
+ l_fp ts; /* Timestamp at BOselect() gob */
+ l_fp ts_e; /* Timestamp at EOselect() gob */
+ fd_set fds;
+ int select_count = 0;
+ static int handler_count = 0;
+
+ ++handler_count;
+ if (handler_count != 1)
+ msyslog(LOG_ERR, "input_handler: handler_count is %d!", handler_count);
+ handler_calls++;
+ ts = *cts;
+
+ for (;;)
+ {
+ /*
+ * Do a poll to see who has data
+ */
+
+ fds = activefds;
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+
+ /*
+ * If we have something to do, freeze a timestamp.
+ * See below for the other cases (nothing (left) to do or error)
+ */
+ while (0 < (n = select(maxactivefd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero)))
+ {
+ ++select_count;
+ ++handler_pkts;
+
+#ifdef REFCLOCK
+ /*
+ * Check out the reference clocks first, if any
+ */
+ if (refio != 0)
+ {
+ register struct refclockio *rp;
+
+ for (rp = refio; rp != 0 && n > 0; rp = rp->next)
+ {
+ fd = rp->fd;
+ if (FD_ISSET(fd, &fds))
+ {
+ n--;
+ if (free_recvbuffs() == 0)
+ {
+ char buf[RX_BUFF_SIZE];
+
+ (void) read(fd, buf, sizeof buf);
+ packets_dropped++;
+ goto select_again;
+ }
+
+ rb = get_free_recv_buffer();
+
+ i = (rp->datalen == 0
+ || rp->datalen > sizeof(rb->recv_space))
+ ? sizeof(rb->recv_space) : rp->datalen;
+ rb->recv_length =
+ read(fd, (char *)&rb->recv_space, (unsigned)i);
+
+ if (rb->recv_length == -1)
+ {
+ netsyslog(LOG_ERR, "clock read fd %d: %m", fd);
+ freerecvbuf(rb);
+ goto select_again;
+ }
+
+ /*
+ * Got one. Mark how
+ * and when it got here,
+ * put it on the full
+ * list and do
+ * bookkeeping.
+ */
+ rb->recv_srcclock = rp->srcclock;
+ rb->dstadr = 0;
+ rb->fd = fd;
+ rb->recv_time = ts;
+ rb->receiver = rp->clock_recv;
+
+ if (rp->io_input)
+ {
+ /*
+ * have direct
+ * input routine
+ * for refclocks
+ */
+ if (rp->io_input(rb) == 0)
+ {
+ /*
+ * data
+ * was
+ * consumed
+ * -
+ * nothing
+ * to
+ * pass
+ * up
+ * into
+ * block
+ * input
+ * machine
+ */
+ freerecvbuf(rb);
+#if 1
+ goto select_again;
+#else
+ continue;
+#endif
+ }
+ }
+
+ add_full_recv_buffer(rb);
+
+ rp->recvcount++;
+ packets_received++;
+ }
+ }
+ }
+#endif /* REFCLOCK */
+
+ /*
+ * Loop through the interfaces looking for data
+ * to read.
+ */
+ for (i = ninterfaces - 1; (i >= 0) && (n > 0); i--)
+ {
+ for (doing = 0; (doing < 2) && (n > 0); doing++)
+ {
+ if (doing == 0)
+ {
+ fd = inter_list[i].fd;
+ }
+ else
+ {
+ if (!(inter_list[i].flags & INT_BCASTOPEN))
+ break;
+ fd = inter_list[i].bfd;
+ }
+ if (fd < 0) continue;
+ if (FD_ISSET(fd, &fds))
+ {
+ n--;
+
+ /*
+ * Get a buffer and read
+ * the frame. If we
+ * haven't got a buffer,
+ * or this is received
+ * on the wild card
+ * socket, just dump the
+ * packet.
+ */
+ if (
+#ifdef UDP_WILDCARD_DELIVERY
+ /*
+ * these guys manage to put properly addressed
+ * packets into the wildcard queue
+ */
+ (free_recvbuffs() == 0)
+#else
+ ((i == wildipv4) || (i == wildipv6)||
+ (free_recvbuffs() == 0))
+#endif
+ )
+ {
+ char buf[RX_BUFF_SIZE];
+ struct sockaddr_storage from;
+
+ fromlen = sizeof from;
+ (void) recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen);
+#ifdef DEBUG
+ if (debug)
+ printf("%s on %d(%lu) fd=%d from %s\n",
+ (i) ? "drop" : "ignore",
+ i, free_recvbuffs(), fd,
+ stoa(&from));
+#endif
+ if (i == wildipv4 || i == wildipv6)
+ packets_ignored++;
+ else
+ packets_dropped++;
+ goto select_again;
+ }
+
+ rb = get_free_recv_buffer();
+
+ fromlen = sizeof(struct sockaddr_storage);
+ rb->recv_length = recvfrom(fd,
+ (char *)&rb->recv_space,
+ sizeof(rb->recv_space), 0,
+ (struct sockaddr *)&rb->recv_srcadr,
+ &fromlen);
+ if (rb->recv_length == 0
+#ifdef EWOULDBLOCK
+ || errno==EWOULDBLOCK
+#endif
+#ifdef EAGAIN
+ || errno==EAGAIN
+#endif
+ ) {
+ freerecvbuf(rb);
+ continue;
+ }
+ else if (rb->recv_length < 0)
+ {
+ netsyslog(LOG_ERR, "recvfrom(%s) fd=%d: %m",
+ stoa(&rb->recv_srcadr), fd);
+#ifdef DEBUG
+ if (debug)
+ printf("input_handler: fd=%d dropped (bad recvfrom)\n", fd);
+#endif
+ freerecvbuf(rb);
+ continue;
+ }
+#ifdef DEBUG
+ if (debug > 2) {
+ if(rb->recv_srcadr.ss_family == AF_INET)
+ printf("input_handler: if=%d fd=%d length %d from %08lx %s\n",
+ i, fd, rb->recv_length,
+ (u_long)ntohl(((struct sockaddr_in*)&rb->recv_srcadr)->sin_addr.s_addr) &
+ 0x00000000ffffffff,
+ stoa(&rb->recv_srcadr));
+ else
+ printf("input_handler: if=%d fd=%d length %d from %s\n",
+ i, fd, rb->recv_length,
+ stoa(&rb->recv_srcadr));
+ }
+#endif
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list and do bookkeeping.
+ */
+ rb->dstadr = &inter_list[i];
+ rb->fd = fd;
+ rb->recv_time = ts;
+ rb->receiver = receive;
+
+ add_full_recv_buffer(rb);
+
+ inter_list[i].received++;
+ packets_received++;
+ goto select_again;
+ }
+ /* Check more interfaces */
+ }
+ }
+ select_again:;
+ /*
+ * Done everything from that select. Poll again.
+ */
+ }
+
+ /*
+ * If nothing more to do, try again.
+ * If nothing to do, just return.
+ * If an error occurred, complain and return.
+ */
+ if (n == 0)
+ {
+ if (select_count == 0) /* We really had nothing to do */
+ {
+ if (debug)
+ netsyslog(LOG_DEBUG, "input_handler: select() returned 0");
+ --handler_count;
+ return;
+ }
+ /* We've done our work */
+ get_systime(&ts_e);
+ /*
+ * (ts_e - ts) is the amount of time we spent
+ * processing this gob of file descriptors. Log
+ * it.
+ */
+ L_SUB(&ts_e, &ts);
+ if (debug > 3)
+ netsyslog(LOG_INFO, "input_handler: Processed a gob of fd's in %s msec", lfptoms(&ts_e, 6));
+
+ /* just bail. */
+ --handler_count;
+ return;
+ }
+ else if (n == -1)
+ {
+ int err = errno;
+
+ /*
+ * extended FAU debugging output
+ */
+ if (err != EINTR)
+ netsyslog(LOG_ERR,
+ "select(%d, %s, 0L, 0L, &0.0) error: %m",
+ maxactivefd+1,
+ fdbits(maxactivefd, &activefds));
+ if (err == EBADF) {
+ int j, b;
+
+ fds = activefds;
+ for (j = 0; j <= maxactivefd; j++)
+ if (
+ (FD_ISSET(j, &fds) && (read(j, &b, 0) == -1))
+ )
+ netsyslog(LOG_ERR, "Bad file descriptor %d", j);
+ }
+ --handler_count;
+ return;
+ }
+ }
+ msyslog(LOG_ERR, "input_handler: fell out of infinite for(;;) loop!");
+ --handler_count;
+ return;
+}
+
+#endif
+/*
+ * findinterface - find interface corresponding to address
+ */
+struct interface *
+findinterface(
+ struct sockaddr_storage *addr
+ )
+{
+ SOCKET s;
+ int rtn, i;
+ struct sockaddr_storage saddr;
+ int saddrlen = SOCKLEN(addr);
+ /*
+ * This is considerably hoke. We open a socket, connect to it
+ * and slap a getsockname() on it. If anything breaks, as it
+ * probably will in some j-random knockoff, we just return the
+ * wildcard interface.
+ */
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.ss_family = addr->ss_family;
+ if(addr->ss_family == AF_INET)
+ memcpy(&((struct sockaddr_in*)&saddr)->sin_addr, &((struct sockaddr_in*)addr)->sin_addr, sizeof(struct in_addr));
+ else if(addr->ss_family == AF_INET6)
+ memcpy(&((struct sockaddr_in6*)&saddr)->sin6_addr, &((struct sockaddr_in6*)addr)->sin6_addr, sizeof(struct in6_addr));
+ ((struct sockaddr_in*)&saddr)->sin_port = htons(2000);
+ s = socket(addr->ss_family, SOCK_DGRAM, 0);
+ if (s == INVALID_SOCKET)
+ return ANY_INTERFACE_CHOOSE(addr);
+
+ rtn = connect(s, (struct sockaddr *)&saddr, SOCKLEN(&saddr));
+#ifndef SYS_WINNT
+ if (rtn < 0)
+#else
+ if (rtn == SOCKET_ERROR)
+#endif
+ {
+ closesocket(s);
+ return ANY_INTERFACE_CHOOSE(addr);
+ }
+
+ rtn = getsockname(s, (struct sockaddr *)&saddr, &saddrlen);
+ closesocket(s);
+#ifndef SYS_WINNT
+ if (rtn < 0)
+#else
+ if (rtn == SOCKET_ERROR)
+#endif
+ return ANY_INTERFACE_CHOOSE(addr);
+
+ for (i = 0; i < ninterfaces; i++) {
+ /*
+ * First look if is the the correct family
+ */
+ if(inter_list[i].sin.ss_family != saddr.ss_family)
+ continue;
+ /*
+ * We match the unicast address only.
+ */
+ if (SOCKCMP(&inter_list[i].sin, &saddr))
+ return (&inter_list[i]);
+ }
+ return ANY_INTERFACE_CHOOSE(addr);
+}
+
+/*
+ * findbcastinter - find broadcast interface corresponding to address
+ */
+struct interface *
+findbcastinter(
+ struct sockaddr_storage *addr
+ )
+{
+#if !defined(MPE) && (defined(SIOCGIFCONF) || defined(SYS_WINNT))
+ register int i;
+
+ i = find_addr_in_list(addr);
+ if(i >= 0)
+ return (&inter_list[i]);
+
+ for (i = 0; i < ninterfaces; i++) {
+ /*
+ * First look if this is the correct family
+ */
+ if(inter_list[i].sin.ss_family != addr->ss_family)
+ continue;
+ /*
+ * We match only those interfaces marked as
+ * broadcastable and either the explicit broadcast
+ * address or the network portion of the IP address.
+ * Sloppy.
+ */
+ if (!(inter_list[i].flags & INT_BROADCAST))
+ continue;
+ if(addr->ss_family == AF_INET) {
+ if (SOCKCMP(&inter_list[i].bcast, addr))
+ return (&inter_list[i]);
+ if ((NSRCADR(&inter_list[i].sin) &
+ NSRCADR(&inter_list[i].mask)) == (NSRCADR(addr) &
+ NSRCADR(&inter_list[i].mask)))
+ return (&inter_list[i]);
+ }
+ else if(addr->ss_family == AF_INET6) {
+ if (SOCKCMP(&inter_list[i].bcast, addr))
+ return (&inter_list[i]);
+ if (SOCKCMP(netof(&inter_list[i].sin), netof(addr)))
+ return (&inter_list[i]);
+ }
+ }
+#endif /* SIOCGIFCONF */
+ return ANY_INTERFACE_CHOOSE(addr);
+}
+
+
+/*
+ * io_clr_stats - clear I/O module statistics
+ */
+void
+io_clr_stats(void)
+{
+ packets_dropped = 0;
+ packets_ignored = 0;
+ packets_received = 0;
+ packets_sent = 0;
+ packets_notsent = 0;
+
+ handler_calls = 0;
+ handler_pkts = 0;
+ io_timereset = current_time;
+}
+
+
+#ifdef REFCLOCK
+/*
+ * This is a hack so that I don't have to fool with these ioctls in the
+ * pps driver ... we are already non-blocking and turn on SIGIO thru
+ * another mechanisim
+ */
+int
+io_addclock_simple(
+ struct refclockio *rio
+ )
+{
+ BLOCKIO();
+ /*
+ * Stuff the I/O structure in the list and mark the descriptor
+ * in use. There is a harmless (I hope) race condition here.
+ */
+ rio->next = refio;
+ refio = rio;
+
+ /*
+ * I/O Completion Ports don't care about select and fd_set
+ */
+#ifndef HAVE_IO_COMPLETION_PORT
+ if (rio->fd > maxactivefd)
+ maxactivefd = rio->fd;
+ FD_SET(rio->fd, &activefds);
+#endif
+ UNBLOCKIO();
+ return 1;
+}
+
+/*
+ * io_addclock - add a reference clock to the list and arrange that we
+ * get SIGIO interrupts from it.
+ */
+int
+io_addclock(
+ struct refclockio *rio
+ )
+{
+ BLOCKIO();
+ /*
+ * Stuff the I/O structure in the list and mark the descriptor
+ * in use. There is a harmless (I hope) race condition here.
+ */
+ rio->next = refio;
+ refio = rio;
+
+# ifdef HAVE_SIGNALED_IO
+ if (init_clock_sig(rio))
+ {
+ refio = rio->next;
+ UNBLOCKIO();
+ return 0;
+ }
+# elif defined(HAVE_IO_COMPLETION_PORT)
+ if (io_completion_port_add_clock_io(rio))
+ {
+ add_socket_to_list(rio->fd);
+ refio = rio->next;
+ UNBLOCKIO();
+ return 0;
+ }
+# endif
+
+ /*
+ * I/O Completion Ports don't care about select and fd_set
+ */
+#ifndef HAVE_IO_COMPLETION_PORT
+ if (rio->fd > maxactivefd)
+ maxactivefd = rio->fd;
+ FD_SET(rio->fd, &activefds);
+#endif
+ UNBLOCKIO();
+ return 1;
+}
+
+/*
+ * io_closeclock - close the clock in the I/O structure given
+ */
+void
+io_closeclock(
+ struct refclockio *rio
+ )
+{
+ /*
+ * Remove structure from the list
+ */
+ if (refio == rio)
+ {
+ refio = rio->next;
+ }
+ else
+ {
+ register struct refclockio *rp;
+
+ for (rp = refio; rp != 0; rp = rp->next)
+ if (rp->next == rio)
+ {
+ rp->next = rio->next;
+ break;
+ }
+
+ if (rp == 0)
+ {
+ /*
+ * Internal error. Report it.
+ */
+ msyslog(LOG_ERR,
+ "internal error: refclockio structure not found");
+ return;
+ }
+ }
+
+ /*
+ * Close the descriptor.
+ */
+ close_file(rio->fd);
+}
+#endif /* REFCLOCK */
+
+ /*
+ * I/O Completion Ports don't care about select and fd_set
+ */
+#ifndef HAVE_IO_COMPLETION_PORT
+void
+kill_asyncio(
+ int startfd
+ )
+{
+ SOCKET i;
+
+ BLOCKIO();
+ for (i = startfd; i <= maxactivefd; i++)
+ (void)close_socket(i);
+}
+#else
+/*
+ * On NT a SOCKET is an unsigned int so we cannot possibly keep it in
+ * an array. So we use one of the ISC_LIST functions to hold the
+ * socket value and use that when we want to enumerate it.
+ */
+void
+kill_asyncio(int startfd)
+{
+ vsock_t *lsock;
+ vsock_t *next;
+
+ BLOCKIO();
+
+ lsock = ISC_LIST_HEAD(sockets_list);
+ while (lsock != NULL) {
+ next = ISC_LIST_NEXT(lsock, link);
+ close_socket(lsock->fd);
+ lsock = next;
+ }
+
+}
+#endif
+/*
+ * Add and delete functions for the list of open sockets
+ */
+void
+add_socket_to_list(SOCKET fd){
+ vsock_t *lsock = malloc(sizeof(vsock_t));
+ lsock->fd = fd;
+
+ ISC_LIST_APPEND(sockets_list, lsock, link);
+}
+void
+delete_socket_from_list(SOCKET fd) {
+
+ vsock_t *next;
+ vsock_t *lsock = ISC_LIST_HEAD(sockets_list);
+
+ while(lsock != NULL) {
+ next = ISC_LIST_NEXT(lsock, link);
+ if(lsock->fd == fd) {
+ ISC_LIST_DEQUEUE(sockets_list, lsock, link);
+ free(lsock);
+ break;
+ }
+ else
+ lsock = next;
+ }
+}
+void
+add_addr_to_list(struct sockaddr_storage *addr, int if_index){
+ remaddr_t *laddr = malloc(sizeof(remaddr_t));
+ memcpy(&laddr->addr, addr, sizeof(addr));
+ laddr->if_index = if_index;
+
+ ISC_LIST_APPEND(remoteaddr_list, laddr, link);
+#ifdef DEBUG
+ if (debug)
+ printf("Added addr %s to list of addresses\n",
+ stoa(addr));
+#endif
+
+
+}
+void
+delete_addr_from_list(struct sockaddr_storage *addr) {
+
+ remaddr_t *next;
+ remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list);
+
+ while(laddr != NULL) {
+ next = ISC_LIST_NEXT(laddr, link);
+ if(SOCKCMP(&laddr->addr, addr)) {
+ ISC_LIST_DEQUEUE(remoteaddr_list, laddr, link);
+ free(laddr);
+ break;
+ }
+ else
+ laddr = next;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("Deleted addr %s from list of addresses\n",
+ stoa(addr));
+#endif
+}
+int
+find_addr_in_list(struct sockaddr_storage *addr) {
+
+ remaddr_t *next;
+ remaddr_t *laddr = ISC_LIST_HEAD(remoteaddr_list);
+#ifdef DEBUG
+ if (debug)
+ printf("Finding addr %s in list of addresses\n",
+ stoa(addr));
+#endif
+
+ while(laddr != NULL) {
+ next = ISC_LIST_NEXT(laddr, link);
+ if(SOCKCMP(&laddr->addr, addr)) {
+ return (laddr->if_index);
+ break;
+ }
+ else
+ laddr = next;
+ }
+ return (-1); /* Not found */
+}
diff --git a/ntpd/ntp_loopfilter.c b/ntpd/ntp_loopfilter.c
new file mode 100644
index 0000000..99d1cc4
--- /dev/null
+++ b/ntpd/ntp_loopfilter.c
@@ -0,0 +1,1000 @@
+/*
+ * ntp_loopfilter.c - implements the NTP loop filter algorithm
+ *
+ * ATTENTION: Get approval from Dave Mills on all changes to this file!
+ *
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <signal.h>
+#include <setjmp.h>
+
+#if defined(VMS) && defined(VMS_LOCALUNIT) /*wjm*/
+#include "ntp_refclock.h"
+#endif /* VMS */
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * This is an implementation of the clock discipline algorithm described
+ * in UDel TR 97-4-3, as amended. It operates as an adaptive parameter,
+ * hybrid phase/frequency-lock loop. A number of sanity checks are
+ * included to protect against timewarps, timespikes and general mayhem.
+ * All units are in s and s/s, unless noted otherwise.
+ */
+#define CLOCK_MAX .128 /* default step threshold (s) */
+#define CLOCK_MINSTEP 900. /* default stepout threshold (s) */
+#define CLOCK_PANIC 1000. /* default panic threshold (s) */
+#define CLOCK_PHI 15e-6 /* max frequency error (s/s) */
+#define CLOCK_PLL 16. /* PLL loop gain */
+#define CLOCK_FLL 8. /* FLL loop gain */
+#define CLOCK_AVG 4. /* parameter averaging constant */
+#define CLOCK_ALLAN 1500. /* compromise Allan intercept (s) */
+#define CLOCK_DAY 86400. /* one day in seconds (s) */
+#define CLOCK_LIMIT 30 /* poll-adjust threshold */
+#define CLOCK_PGATE 4. /* poll-adjust gate */
+#define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */
+
+/*
+ * Clock discipline state machine. This is used to control the
+ * synchronization behavior during initialization and following a
+ * timewarp.
+ *
+ * State < max > max Comments
+ * ====================================================
+ * NSET FREQ FREQ no ntp.drift
+ *
+ * FSET TSET if (allow) TSET, ntp.drift
+ * else FREQ
+ *
+ * TSET SYNC FREQ time set
+ *
+ * FREQ SYNC if (mu < 900) FREQ calculate frequency
+ * else if (allow) TSET
+ * else FREQ
+ *
+ * SYNC SYNC if (mu < 900) SYNC normal state
+ * else SPIK
+ *
+ * SPIK SYNC if (allow) TSET spike detector
+ * else FREQ
+ */
+#define S_NSET 0 /* clock never set */
+#define S_FSET 1 /* frequency set from the drift file */
+#define S_TSET 2 /* time set */
+#define S_FREQ 3 /* frequency mode */
+#define S_SYNC 4 /* clock synchronized */
+#define S_SPIK 5 /* spike detected */
+
+/*
+ * Kernel PLL/PPS state machine. This is used with the kernel PLL
+ * modifications described in the README.kernel file.
+ *
+ * If kernel support for the ntp_adjtime() system call is available, the
+ * ntp_control flag is set. The ntp_enable and kern_enable flags can be
+ * set at configuration time or run time using ntpdc. If ntp_enable is
+ * false, the discipline loop is unlocked and no correctios of any kind
+ * are made. If both ntp_control and kern_enable are set, the kernel
+ * support is used as described above; if false, the kernel is bypassed
+ * entirely and the daemon PLL used instead.
+ *
+ * Each update to a prefer peer sets pps_stratum if it survives the
+ * intersection algorithm and its time is within range. The PPS time
+ * discipline is enabled (STA_PPSTIME bit set in the status word) when
+ * pps_stratum is true and the PPS frequency discipline is enabled. If
+ * the PPS time discipline is enabled and the kernel reports a PPS
+ * signal is present, the pps_control variable is set to the current
+ * time. If the current time is later than pps_control by PPS_MAXAGE
+ * (120 s), this variable is set to zero.
+ *
+ * If an external clock is present, the clock driver sets STA_CLK in the
+ * status word. When the local clock driver sees this bit, it updates
+ * via this routine, which then calls ntp_adjtime() with the STA_PLL bit
+ * set to zero, in which case the system clock is not adjusted. This is
+ * also a signal for the external clock driver to discipline the system
+ * clock.
+ */
+/*
+ * Program variables that can be tinkered.
+ */
+double clock_max = CLOCK_MAX; /* step threshold (s) */
+double clock_minstep = CLOCK_MINSTEP; /* stepout threshold (s) */
+double clock_panic = CLOCK_PANIC; /* panic threshold (s) */
+double clock_phi = CLOCK_PHI; /* dispersion rate (s/s) */
+double allan_xpt = CLOCK_ALLAN; /* Allan intercept (s) */
+
+/*
+ * Program variables
+ */
+static double clock_offset; /* clock offset adjustment (s) */
+double drift_comp; /* clock frequency (s/s) */
+double clock_stability; /* clock stability (s/s) */
+u_long pps_control; /* last pps sample time */
+static void rstclock P((int, u_long, double)); /* transition function */
+
+#ifdef KERNEL_PLL
+struct timex ntv; /* kernel API parameters */
+int pll_status; /* status bits for kernel pll */
+int pll_nano; /* nanosecond kernel switch */
+#endif /* KERNEL_PLL */
+
+/*
+ * Clock state machine control flags
+ */
+int ntp_enable; /* clock discipline enabled */
+int pll_control; /* kernel support available */
+int kern_enable; /* kernel support enabled */
+int pps_enable; /* kernel PPS discipline enabled */
+int ext_enable; /* external clock enabled */
+int pps_stratum; /* pps stratum */
+int allow_panic = FALSE; /* allow panic correction */
+int mode_ntpdate = FALSE; /* exit on first clock set */
+
+/*
+ * Clock state machine variables
+ */
+u_char sys_poll = NTP_MINDPOLL; /* system poll interval (log2 s) */
+int state; /* clock discipline state */
+int tc_counter; /* hysteresis counter */
+u_long last_time; /* time of last clock update (s) */
+double last_offset; /* last clock offset (s) */
+double sys_jitter; /* system RMS jitter (s) */
+
+/*
+ * Huff-n'-puff filter variables
+ */
+static double *sys_huffpuff; /* huff-n'-puff filter */
+static int sys_hufflen; /* huff-n'-puff filter stages */
+static int sys_huffptr; /* huff-n'-puff filter pointer */
+static double sys_mindly; /* huff-n'-puff filter min delay */
+
+#if defined(KERNEL_PLL)
+/* Emacs cc-mode goes nuts if we split the next line... */
+#define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \
+ MOD_STATUS | MOD_TIMECONST)
+#ifdef SIGSYS
+static void pll_trap P((int)); /* configuration trap */
+static struct sigaction sigsys; /* current sigaction status */
+static struct sigaction newsigsys; /* new sigaction status */
+static sigjmp_buf env; /* environment var. for pll_trap() */
+#endif /* SIGSYS */
+#endif /* KERNEL_PLL */
+
+/*
+ * init_loopfilter - initialize loop filter data
+ */
+void
+init_loopfilter(void)
+{
+ /*
+ * Initialize state variables. Initially, we expect no drift
+ * file, so set the state to S_NSET.
+ */
+ rstclock(S_NSET, current_time, 0);
+}
+
+/*
+ * local_clock - the NTP logical clock loop filter. Returns 1 if the
+ * clock was stepped, 0 if it was slewed and -1 if it is hopeless.
+ *
+ * LOCKCLOCK: The only thing this routine does is set the
+ * sys_rootdispersion variable equal to the peer dispersion.
+ */
+int
+local_clock(
+ struct peer *peer, /* synch source peer structure */
+ double fp_offset, /* clock offset (s) */
+ double epsil /* jittter (square s*s) */
+ )
+{
+ u_long mu; /* interval since last update (s) */
+ double oerror; /* previous error estimate */
+ double flladj; /* FLL frequency adjustment (ppm) */
+ double plladj; /* PLL frequency adjustment (ppm) */
+ double clock_frequency; /* clock frequency adjustment (ppm) */
+ double dtemp, etemp; /* double temps */
+ int retval; /* return value */
+
+ /*
+ * If the loop is opened, monitor and record the offsets
+ * anyway in order to determine the open-loop response.
+ */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: assocID %d offset %.9f jitter %.9f state %d\n",
+ peer->associd, fp_offset, SQRT(epsil), state);
+#endif
+#ifdef LOCKCLOCK
+ sys_rootdispersion = peer->rootdispersion;
+ return (0);
+
+#else /* LOCKCLOCK */
+ if (!ntp_enable) {
+ record_loop_stats(fp_offset, drift_comp, SQRT(epsil),
+ clock_stability, sys_poll);
+ return (0);
+ }
+
+ /*
+ * If the clock is way off, panic is declared. The clock_panic
+ * defaults to 1000 s; if set to zero, the panic will never
+ * occur. The allow_panic defaults to FALSE, so the first panic
+ * will exit. It can be set TRUE by a command line option, in
+ * which case the clock will be set anyway and time marches on.
+ * But, allow_panic will be set it FALSE when the update is
+ * within the step range; so, subsequent panics will exit.
+ */
+ if (fabs(fp_offset) > clock_panic && clock_panic > 0 &&
+ !allow_panic) {
+ msyslog(LOG_ERR,
+ "time correction of %.0f seconds exceeds sanity limit (%.0f); set clock manually to the correct UTC time.",
+ fp_offset, clock_panic);
+ return (-1);
+ }
+
+ /*
+ * If simulating ntpdate, set the clock directly, rather than
+ * using the discipline. The clock_max defines the step
+ * threshold, above which the clock will be stepped instead of
+ * slewed. The value defaults to 128 ms, but can be set to even
+ * unreasonable values. If set to zero, the clock will never be
+ * stepped.
+ *
+ * Note that if ntpdate is active, the terminal does not detach,
+ * so the termination comments print directly to the console.
+ */
+ if (mode_ntpdate) {
+ if (fabs(fp_offset) > clock_max && clock_max > 0) {
+ step_systime(fp_offset);
+ msyslog(LOG_NOTICE, "time reset %+.6f s",
+ fp_offset);
+ printf("ntpd: time set %+.6fs\n", fp_offset);
+ } else {
+ adj_systime(fp_offset);
+ msyslog(LOG_NOTICE, "time slew %+.6f s",
+ fp_offset);
+ printf("ntpd: time slew %+.6fs\n", fp_offset);
+ }
+ record_loop_stats(fp_offset, drift_comp, SQRT(epsil),
+ clock_stability, sys_poll);
+ exit (0);
+ }
+
+ /*
+ * If the clock has never been set, set it and initialize the
+ * discipline parameters. We then switch to frequency mode to
+ * speed the inital convergence process. If lucky, after an hour
+ * the ntp.drift file is created and initialized and we don't
+ * get here again.
+ */
+ if (state == S_NSET) {
+ if (fabs(fp_offset) > clock_max && clock_max > 0) {
+ step_systime(fp_offset);
+ msyslog(LOG_NOTICE, "time reset %+.6f s",
+ fp_offset);
+ reinit_timer();
+ }
+ rstclock(S_FREQ, peer->epoch, 0);
+ return (1);
+ }
+
+ /*
+ * Update the jitter estimate.
+ */
+ oerror = sys_jitter;
+ dtemp = SQUARE(sys_jitter);
+ sys_jitter = SQRT(dtemp + (epsil - dtemp) / CLOCK_AVG);
+
+ /*
+ * The huff-n'-puff filter finds the lowest delay in the recent
+ * interval. This is used to correct the offset by one-half the
+ * difference between the sample delay and minimum delay. This
+ * is most effective if the delays are highly assymetric and
+ * clockhopping is avoided and the clock frequency wander is
+ * relatively small.
+ */
+ if (sys_huffpuff != NULL) {
+ if (peer->delay < sys_huffpuff[sys_huffptr])
+ sys_huffpuff[sys_huffptr] = peer->delay;
+ if (peer->delay < sys_mindly)
+ sys_mindly = peer->delay;
+ if (fp_offset > 0)
+ dtemp = -(peer->delay - sys_mindly) / 2;
+ else
+ dtemp = (peer->delay - sys_mindly) / 2;
+ fp_offset += dtemp;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: size %d mindly %.6f huffpuff %.6f\n",
+ sys_hufflen, sys_mindly, dtemp);
+#endif
+ }
+
+ /*
+ * Clock state machine transition function. This is where the
+ * action is and defines how the system reacts to large phase
+ * and frequency errors. There are two main regimes: when the
+ * offset exceeds the step threshold and when it does not.
+ * However, if the step threshold is set to zero, a step will
+ * never occur. See the instruction manual for the details how
+ * these actions interact with the command line options.
+ */
+ retval = 0;
+ if (sys_poll > peer->maxpoll)
+ sys_poll = peer->maxpoll;
+ else if (sys_poll < peer->minpoll)
+ sys_poll = peer->minpoll;
+ clock_frequency = flladj = plladj = 0;
+ mu = peer->epoch - last_time;
+ if (fabs(fp_offset) > clock_max && clock_max > 0) {
+ switch (state) {
+
+ /*
+ * In S_TSET state the time has been set at the last
+ * valid update and the offset at that time set to zero.
+ * If following that we cruise outside the capture
+ * range, assume a really bad frequency error and switch
+ * to S_FREQ state.
+ */
+ case S_TSET:
+ state = S_FREQ;
+ break;
+
+ /*
+ * In S_SYNC state we ignore outlyers. At the first
+ * outlyer after the stepout threshold, switch to S_SPIK
+ * state.
+ */
+ case S_SYNC:
+ if (mu < clock_minstep)
+ return (0);
+ state = S_SPIK;
+ return (0);
+
+ /*
+ * In S_FREQ state we ignore outlyers. At the first
+ * outlyer after 900 s, compute the apparent phase and
+ * frequency correction.
+ */
+ case S_FREQ:
+ if (mu < clock_minstep)
+ return (0);
+ /* fall through to S_SPIK */
+
+ /*
+ * In S_SPIK state a large correction is necessary.
+ * Since the outlyer may be due to a large frequency
+ * error, compute the apparent frequency correction.
+ */
+ case S_SPIK:
+ clock_frequency = (fp_offset - clock_offset) /
+ mu;
+ /* fall through to default */
+
+ /*
+ * We get here directly in S_FSET state and indirectly
+ * from S_FREQ and S_SPIK states. The clock is either
+ * reset or shaken, but never stirred.
+ */
+ default:
+ step_systime(fp_offset);
+ msyslog(LOG_NOTICE, "time reset %+.6f s",
+ fp_offset);
+ reinit_timer();
+ rstclock(S_TSET, peer->epoch, 0);
+ retval = 1;
+ break;
+ }
+ } else {
+ switch (state) {
+
+ /*
+ * In S_FSET state this is the first update. Adjust the
+ * phase, but don't adjust the frequency until the next
+ * update.
+ */
+ case S_FSET:
+ rstclock(S_TSET, peer->epoch, fp_offset);
+ break;
+
+ /*
+ * In S_FREQ state ignore updates until the stepout
+ * threshold. After that, correct the phase and
+ * frequency and switch to S_SYNC state.
+ */
+ case S_FREQ:
+ if (mu < clock_minstep)
+ return (0);
+ clock_frequency = (fp_offset - clock_offset) /
+ mu;
+ rstclock(S_SYNC, peer->epoch, fp_offset);
+ break;
+
+ /*
+ * Either the clock has just been set or the previous
+ * update was a spike and ignored. Since this update is
+ * not an outlyer, fold the tent and resume life.
+ */
+ case S_TSET:
+ case S_SPIK:
+ state = S_SYNC;
+ /* fall through to default */
+
+ /*
+ * We come here in the normal case for linear phase and
+ * frequency adjustments. If the difference between the
+ * last offset and the current one exceeds the jitter by
+ * CLOCK_SGATE and the interval since the last update is
+ * less than twice the system poll interval, consider
+ * the update a popcorn spike and ignore it..
+ */
+ default:
+ allow_panic = FALSE;
+ dtemp = fabs(fp_offset - last_offset);
+/*
+ if (dtemp > CLOCK_SGATE * oerror && mu <
+ (u_long) ULOGTOD(sys_poll + 1)) {
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: popcorn %.6f %.6f\n",
+ dtemp, oerror);
+#endif
+ last_offset = fp_offset;
+ return (0);
+ }
+*/
+
+ /*
+ * The FLL and PLL frequency gain constants
+ * depend on the poll interval and Allan
+ * intercept. The PLL constant is calculated
+ * throughout the poll interval range, but the
+ * update interval is clamped so as not to
+ * exceed the poll interval. The FLL gain is
+ * zero below one-half the Allan intercept and
+ * unity at MAXPOLL. It decreases as 1 /
+ * (MAXPOLL + 1 - poll interval) in a feeble
+ * effort to match the loop stiffness to the
+ * Allan wobble. Particularly for the PLL, these
+ * measures allow oversampling, but not
+ * undersampling and insure stability even when
+ * the rules of fair engagement are broken.
+ */
+ if (ULOGTOD(sys_poll) > allan_xpt / 2) {
+ dtemp = NTP_MAXPOLL + 1 - sys_poll;
+ flladj = (fp_offset - clock_offset) /
+ (max(mu, allan_xpt) * dtemp);
+ }
+ etemp = min(mu, (u_long)ULOGTOD(sys_poll));
+ dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll);
+ plladj = fp_offset * etemp / (dtemp * dtemp);
+ last_time = peer->epoch;
+ last_offset = clock_offset = fp_offset;
+ break;
+ }
+ }
+
+#ifdef KERNEL_PLL
+ /*
+ * This code segment works when clock adjustments are made using
+ * precision time kernel support and the ntp_adjtime() system
+ * call. This support is available in Solaris 2.6 and later,
+ * Digital Unix 4.0 and later, FreeBSD, Linux and specially
+ * modified kernels for HP-UX 9 and Ultrix 4. In the case of the
+ * DECstation 5000/240 and Alpha AXP, additional kernel
+ * modifications provide a true microsecond clock and nanosecond
+ * clock, respectively.
+ */
+ if (pll_control && kern_enable) {
+
+ /*
+ * We initialize the structure for the ntp_adjtime()
+ * system call. We have to convert everything to
+ * microseconds or nanoseconds first. Do not update the
+ * system variables if the ext_enable flag is set. In
+ * this case, the external clock driver will update the
+ * variables, which will be read later by the local
+ * clock driver. Afterwards, remember the time and
+ * frequency offsets for jitter and stability values and
+ * to update the drift file.
+ */
+ memset(&ntv, 0, sizeof(ntv));
+ if (ext_enable) {
+ ntv.modes = MOD_STATUS;
+ } else {
+ ntv.modes = MOD_BITS;
+ if (clock_offset < 0)
+ dtemp = -.5;
+ else
+ dtemp = .5;
+ if (pll_nano) {
+ ntv.offset = (int32)(clock_offset *
+ 1e9 + dtemp);
+ ntv.constant = sys_poll;
+ } else {
+ ntv.offset = (int32)(clock_offset *
+ 1e6 + dtemp);
+ ntv.constant = sys_poll - 4;
+ }
+ if (clock_frequency != 0) {
+ ntv.modes |= MOD_FREQUENCY;
+ ntv.freq = (int32)((clock_frequency +
+ drift_comp) * 65536e6);
+ }
+ ntv.esterror = (u_int32)(sys_jitter * 1e6);
+ ntv.maxerror = (u_int32)((sys_rootdelay / 2 +
+ sys_rootdispersion) * 1e6);
+ ntv.status = STA_PLL;
+
+ /*
+ * Set the leap bits in the status word.
+ */
+ if (sys_leap == LEAP_NOTINSYNC) {
+ ntv.status |= STA_UNSYNC;
+ } else if (calleapwhen(sys_reftime.l_ui) <
+ CLOCK_DAY) {
+ if (sys_leap & LEAP_ADDSECOND)
+ ntv.status |= STA_INS;
+ else if (sys_leap & LEAP_DELSECOND)
+ ntv.status |= STA_DEL;
+ }
+
+ /*
+ * Switch to FLL mode if the poll interval is
+ * greater than MAXDPOLL, so that the kernel
+ * loop behaves as the daemon loop; viz.,
+ * selects the FLL when necessary, etc. For
+ * legacy only.
+ */
+ if (sys_poll > NTP_MAXDPOLL)
+ ntv.status |= STA_FLL;
+
+ /*
+ * If the PPS signal is up and enabled, light
+ * the frequency bit. If the PPS driver is
+ * working, light the phase bit as well. If not,
+ * douse the lights, since somebody else may
+ * have left the switch on.
+ */
+ if (pps_enable && pll_status & STA_PPSSIGNAL) {
+ ntv.status |= STA_PPSFREQ;
+ if (pps_stratum < STRATUM_UNSPEC)
+ ntv.status |= STA_PPSTIME;
+ } else {
+ ntv.status &= ~(STA_PPSFREQ |
+ STA_PPSTIME);
+ }
+ }
+
+ /*
+ * Pass the stuff to the kernel. If it squeals, turn off
+ * the pigs. In any case, fetch the kernel offset and
+ * frequency and pretend we did it here.
+ */
+ if (ntp_adjtime(&ntv) == TIME_ERROR) {
+ if (ntv.status != pll_status)
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "kernel time sync disabled %04x",
+ ntv.status);
+ ntv.status &= ~(STA_PPSFREQ | STA_PPSTIME);
+ } else {
+ if (ntv.status != pll_status)
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "kernel time sync enabled %04x",
+ ntv.status);
+ }
+ pll_status = ntv.status;
+ if (pll_nano)
+ clock_offset = ntv.offset / 1e9;
+ else
+ clock_offset = ntv.offset / 1e6;
+ clock_frequency = ntv.freq / 65536e6 - drift_comp;
+ flladj = plladj = 0;
+
+ /*
+ * If the kernel PPS is lit, monitor its performance.
+ */
+ if (ntv.status & STA_PPSTIME) {
+ pps_control = current_time;
+ if (pll_nano)
+ sys_jitter = ntv.jitter / 1e9;
+ else
+ sys_jitter = ntv.jitter / 1e6;
+ }
+ }
+#endif /* KERNEL_PLL */
+
+ /*
+ * Adjust the clock frequency and calculate the stability. If
+ * kernel support is available, we use the results of the kernel
+ * discipline instead of the PLL/FLL discipline. In this case,
+ * drift_comp is a sham and used only for updating the drift
+ * file and for billboard eye candy.
+ */
+ dtemp = clock_frequency + flladj + plladj;
+ etemp = drift_comp + dtemp;
+ if (etemp > NTP_MAXFREQ)
+ drift_comp = NTP_MAXFREQ;
+ else if (etemp <= -NTP_MAXFREQ)
+ drift_comp = -NTP_MAXFREQ;
+ else
+ drift_comp = etemp;
+ if (fabs(etemp) > NTP_MAXFREQ)
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "frequency error %.0f PPM exceeds tolerance %.0f PPM",
+ etemp * 1e6, NTP_MAXFREQ * 1e6);
+
+ etemp = SQUARE(clock_stability);
+ dtemp = SQUARE(dtemp);
+ clock_stability = SQRT(etemp + (dtemp - etemp) / CLOCK_AVG);
+
+ /*
+ * In SYNC state, adjust the poll interval. The trick here is to
+ * compare the apparent frequency change induced by the system
+ * jitter over the poll interval, or fritter, to the frequency
+ * stability. If the fritter is greater than the stability,
+ * phase noise predominates and the averaging interval is
+ * increased; otherwise, it is decreased. A bit of hysteresis
+ * helps calm the dance. Works best using burst mode.
+ */
+ if (state == S_SYNC) {
+ if (sys_jitter > ULOGTOD(sys_poll) * clock_stability &&
+ fabs(clock_offset) < CLOCK_PGATE * sys_jitter) {
+ tc_counter += sys_poll;
+ if (tc_counter > CLOCK_LIMIT) {
+ tc_counter = CLOCK_LIMIT;
+ if (sys_poll < peer->maxpoll) {
+ tc_counter = 0;
+ sys_poll++;
+ }
+ }
+ } else {
+ tc_counter -= sys_poll << 1;
+ if (tc_counter < -CLOCK_LIMIT) {
+ tc_counter = -CLOCK_LIMIT;
+ if (sys_poll > peer->minpoll) {
+ tc_counter = 0;
+ sys_poll--;
+ }
+ }
+ }
+ }
+
+ /*
+ * Update the system time variables.
+ */
+ dtemp = peer->disp + (current_time - peer->epoch) * clock_phi +
+ sys_jitter + fabs(last_offset);
+ if (!(peer->flags & FLAG_REFCLOCK) && dtemp < MINDISPERSE)
+ dtemp = MINDISPERSE;
+ sys_rootdispersion = peer->rootdispersion + dtemp;
+ record_loop_stats(last_offset, drift_comp, sys_jitter,
+ clock_stability, sys_poll);
+
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: mu %lu rootjit %.6f stab %.3f poll %d count %d\n",
+ mu, dtemp, clock_stability * 1e6, sys_poll,
+ tc_counter);
+#endif /* DEBUG */
+ return (retval);
+#endif /* LOCKCLOCK */
+}
+
+
+/*
+ * adj_host_clock - Called once every second to update the local clock.
+ *
+ * LOCKCLOCK: The only thing this routine does is increment the
+ * sys_rootdispersion variable.
+ */
+void
+adj_host_clock(
+ void
+ )
+{
+ double adjustment;
+
+ /*
+ * Update the dispersion since the last update. In contrast to
+ * NTPv3, NTPv4 does not declare unsynchronized after one day,
+ * since the dispersion check serves this function. Also,
+ * since the poll interval can exceed one day, the old test
+ * would be counterproductive. Note we do this even with
+ * external clocks, since the clock driver will recompute the
+ * maximum error and the local clock driver will pick it up and
+ * pass to the common refclock routines. Very elegant.
+ */
+ sys_rootdispersion += clock_phi;
+
+#ifndef LOCKCLOCK
+ /*
+ * Declare PPS kernel unsync if the pps signal has not been
+ * heard for a few minutes.
+ */
+ if (pps_control && current_time - pps_control > PPS_MAXAGE) {
+ if (pps_control)
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE, "pps sync disabled");
+ pps_control = 0;
+ }
+
+ /*
+ * If NTP is disabled or ntpdate mode enabled or the kernel
+ * discipline enabled, we have no business going further.
+ */
+ if (!ntp_enable || mode_ntpdate || (pll_control && kern_enable))
+ return;
+
+ /*
+ * Intricate wrinkle for legacy only. If the local clock driver
+ * is in use and selected for synchronization, somebody else may
+ * tinker the adjtime() syscall. If this is the case, the driver
+ * is marked prefer and we have to avoid calling adjtime(),
+ * since that may truncate the other guy's requests.
+ */
+ if (sys_peer != 0) {
+ if (sys_peer->refclktype == REFCLK_LOCALCLOCK &&
+ sys_peer->flags & FLAG_PREFER)
+ return;
+ }
+
+ /*
+ * Implement the phase and frequency adjustments. Note the
+ * black art formerly practiced here has been whitewashed.
+ */
+ adjustment = clock_offset / (CLOCK_PLL * ULOGTOD(sys_poll));
+ clock_offset -= adjustment;
+ adj_systime(adjustment + drift_comp);
+#endif /* LOCKCLOCK */
+}
+
+
+/*
+ * Clock state machine. Enter new state and set state variables.
+ */
+static void
+rstclock(
+ int trans, /* new state */
+ u_long epoch, /* last time */
+ double offset /* last offset */
+ )
+{
+ tc_counter = 0;
+ sys_poll = NTP_MINPOLL;
+ state = trans;
+ last_time = epoch;
+ last_offset = clock_offset = offset;
+#ifdef DEBUG
+ if (debug)
+ printf("local_clock: at %lu state %d\n", last_time,
+ trans);
+#endif
+}
+
+
+/*
+ * huff-n'-puff filter
+ */
+void
+huffpuff()
+{
+ int i;
+
+ if (sys_huffpuff == NULL)
+ return;
+ sys_huffptr = (sys_huffptr + 1) % sys_hufflen;
+ sys_huffpuff[sys_huffptr] = 1e9;
+ sys_mindly = 1e9;
+ for (i = 0; i < sys_hufflen; i++) {
+ if (sys_huffpuff[i] < sys_mindly)
+ sys_mindly = sys_huffpuff[i];
+ }
+}
+
+
+/*
+ * loop_config - configure the loop filter
+ *
+ * LOCKCLOCK: The LOOP_DRIFTINIT and LOOP_DRIFTCOMP cases are no-ops.
+ */
+void
+loop_config(
+ int item,
+ double freq
+ )
+{
+ int i;
+
+ switch (item) {
+
+ case LOOP_DRIFTINIT:
+
+#ifndef LOCKCLOCK
+#ifdef KERNEL_PLL
+ /*
+ * Assume the kernel supports the ntp_adjtime() syscall.
+ * If that syscall works, initialize the kernel
+ * variables. Otherwise, continue leaving no harm
+ * behind. While at it, ask to set nanosecond mode. If
+ * the kernel agrees, rejoice; othewise, it does only
+ * microseconds.
+ *
+ * Call out the safety patrol. If ntpdate mode or if the
+ * step threshold has been changed by the -x option or
+ * tinker command, kernel discipline is unsafe, so don't
+ * do any of this stuff.
+ */
+ if (mode_ntpdate || clock_max != CLOCK_MAX)
+ break;
+
+ pll_control = 1;
+ memset(&ntv, 0, sizeof(ntv));
+#ifdef STA_NANO
+ ntv.modes = MOD_BITS | MOD_NANO;
+#else
+ ntv.modes = MOD_BITS;
+#endif /* STA_NANO */
+ ntv.maxerror = MAXDISPERSE;
+ ntv.esterror = MAXDISPERSE;
+ ntv.status = STA_UNSYNC;
+#ifdef SIGSYS
+ /*
+ * Use sigsetjmp() to save state and then call
+ * ntp_adjtime(); if it fails, then siglongjmp() is used
+ * to return control
+ */
+ newsigsys.sa_handler = pll_trap;
+ newsigsys.sa_flags = 0;
+ if (sigaction(SIGSYS, &newsigsys, &sigsys)) {
+ msyslog(LOG_ERR,
+ "sigaction() fails to save SIGSYS trap: %m");
+ pll_control = 0;
+ }
+ if (sigsetjmp(env, 1) == 0)
+ ntp_adjtime(&ntv);
+ if ((sigaction(SIGSYS, &sigsys,
+ (struct sigaction *)NULL))) {
+ msyslog(LOG_ERR,
+ "sigaction() fails to restore SIGSYS trap: %m");
+ pll_control = 0;
+ }
+#else /* SIGSYS */
+ ntp_adjtime(&ntv);
+#endif /* SIGSYS */
+ pll_status = ntv.status;
+ if (pll_control) {
+#ifdef STA_NANO
+ if (pll_status & STA_NANO)
+ pll_nano = 1;
+ if (pll_status & STA_CLK)
+ ext_enable = 1;
+#endif /* STA_NANO */
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_INFO,
+ "kernel time sync status %04x",
+ pll_status);
+ }
+#endif /* KERNEL_PLL */
+#endif /* LOCKCLOCK */
+ break;
+
+ case LOOP_DRIFTCOMP:
+
+#ifndef LOCKCLOCK
+ /*
+ * If the frequency value is reasonable, set the initial
+ * frequency to the given value and the state to S_FSET.
+ * Otherwise, the drift file may be missing or broken,
+ * so set the frequency to zero. This erases past
+ * history should somebody break something.
+ */
+ if (freq <= NTP_MAXFREQ && freq >= -NTP_MAXFREQ) {
+ drift_comp = freq;
+ rstclock(S_FSET, current_time, 0);
+ } else {
+ drift_comp = 0;
+ }
+
+#ifdef KERNEL_PLL
+ /*
+ * Sanity check. If the kernel is enabled, load the
+ * frequency and light up the loop. If not, set the
+ * kernel frequency to zero and leave the loop dark. In
+ * either case set the time to zero to cancel any
+ * previous nonsense.
+ */
+ if (pll_control) {
+ memset((char *)&ntv, 0, sizeof(ntv));
+ ntv.modes = MOD_OFFSET | MOD_FREQUENCY;
+ if (kern_enable) {
+ ntv.modes |= MOD_STATUS;
+ ntv.status = STA_PLL;
+ ntv.freq = (int32)(drift_comp *
+ 65536e6);
+ }
+ (void)ntp_adjtime(&ntv);
+ }
+#endif /* KERNEL_PLL */
+#endif /* LOCKCLOCK */
+ break;
+
+ /*
+ * Special tinker variables for Ulrich Windl. Very dangerous.
+ */
+ case LOOP_MAX: /* step threshold */
+ clock_max = freq;
+ break;
+
+ case LOOP_PANIC: /* panic threshold */
+ clock_panic = freq;
+ break;
+
+ case LOOP_PHI: /* dispersion rate */
+ clock_phi = freq;
+ break;
+
+ case LOOP_MINSTEP: /* watchdog bark */
+ clock_minstep = freq;
+ break;
+
+ case LOOP_ALLAN: /* Allan intercept */
+ allan_xpt = freq;
+ break;
+
+ case LOOP_HUFFPUFF: /* huff-n'-puff filter length */
+ if (freq < HUFFPUFF)
+ freq = HUFFPUFF;
+ sys_hufflen = (int)(freq / HUFFPUFF);
+ sys_huffpuff = (double *)emalloc(sizeof(double) *
+ sys_hufflen);
+ for (i = 0; i < sys_hufflen; i++)
+ sys_huffpuff[i] = 1e9;
+ sys_mindly = 1e9;
+ break;
+
+ case LOOP_FREQ: /* initial frequency */
+ drift_comp = freq / 1e6;
+ rstclock(S_FSET, current_time, 0);
+ break;
+ }
+}
+
+
+#if defined(KERNEL_PLL) && defined(SIGSYS)
+/*
+ * _trap - trap processor for undefined syscalls
+ *
+ * This nugget is called by the kernel when the SYS_ntp_adjtime()
+ * syscall bombs because the silly thing has not been implemented in
+ * the kernel. In this case the phase-lock loop is emulated by
+ * the stock adjtime() syscall and a lot of indelicate abuse.
+ */
+static RETSIGTYPE
+pll_trap(
+ int arg
+ )
+{
+ pll_control = 0;
+ siglongjmp(env, 1);
+}
+#endif /* KERNEL_PLL && SIGSYS */
diff --git a/ntpd/ntp_monitor.c b/ntpd/ntp_monitor.c
new file mode 100644
index 0000000..6b288fc
--- /dev/null
+++ b/ntpd/ntp_monitor.c
@@ -0,0 +1,335 @@
+/*
+ * ntp_monitor - monitor ntpd statistics
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+/*
+ * I'm still not sure I like what I've done here. It certainly consumes
+ * memory like it is going out of style, and also may not be as low
+ * overhead as I'd imagined.
+ *
+ * Anyway, we record statistics based on source address, mode and
+ * version (for now, anyway. Check the code). The receive procedure
+ * calls us with the incoming rbufp before it does anything else.
+ *
+ * Each entry is doubly linked into two lists, a hash table and a
+ * most-recently-used list. When a packet arrives it is looked up in
+ * the hash table. If found, the statistics are updated and the entry
+ * relinked at the head of the MRU list. If not found, a new entry is
+ * allocated, initialized and linked into both the hash table and at the
+ * head of the MRU list.
+ *
+ * Memory is usually allocated by grabbing a big chunk of new memory and
+ * cutting it up into littler pieces. The exception to this when we hit
+ * the memory limit. Then we free memory by grabbing entries off the
+ * tail for the MRU list, unlinking from the hash table, and
+ * reinitializing.
+ *
+ * trimmed back memory consumption ... jdg 8/94
+ */
+/*
+ * Limits on the number of structures allocated. This limit is picked
+ * with the illicit knowlege that we can only return somewhat less
+ * than 8K bytes in a mode 7 response packet, and that each structure
+ * will require about 20 bytes of space in the response.
+ *
+ * ... I don't believe the above is true anymore ... jdg
+ */
+#ifndef MAXMONMEM
+#define MAXMONMEM 600 /* we allocate up to 600 structures */
+#endif
+#ifndef MONMEMINC
+#define MONMEMINC 40 /* allocate them 40 at a time */
+#endif
+
+/*
+ * Hashing stuff
+ */
+#define MON_HASH_SIZE 128
+#define MON_HASH_MASK (MON_HASH_SIZE-1)
+#define MON_HASH(addr) sock_hash(addr)
+
+/*
+ * Pointers to the hash table, the MRU list and the count table. Memory
+ * for the hash and count tables is only allocated if monitoring is
+ * turned on.
+ */
+static struct mon_data *mon_hash[MON_HASH_SIZE]; /* list ptrs */
+struct mon_data mon_mru_list;
+
+/*
+ * List of free structures structures, and counters of free and total
+ * structures. The free structures are linked with the hash_next field.
+ */
+static struct mon_data *mon_free; /* free list or null if none */
+static int mon_total_mem; /* total structures allocated */
+static int mon_mem_increments; /* times called malloc() */
+
+/*
+ * Initialization state. We may be monitoring, we may not. If
+ * we aren't, we may not even have allocated any memory yet.
+ */
+int mon_enabled; /* enable switch */
+u_long mon_age = 3000; /* preemption limit */
+static int mon_have_memory;
+static void mon_getmoremem P((void));
+static void remove_from_hash P((struct mon_data *));
+
+/*
+ * init_mon - initialize monitoring global data
+ */
+void
+init_mon(void)
+{
+ /*
+ * Don't do much of anything here. We don't allocate memory
+ * until someone explicitly starts us.
+ */
+ mon_enabled = MON_OFF;
+ mon_have_memory = 0;
+
+ mon_total_mem = 0;
+ mon_mem_increments = 0;
+ mon_free = NULL;
+ memset(&mon_hash[0], 0, sizeof mon_hash);
+ memset(&mon_mru_list, 0, sizeof mon_mru_list);
+}
+
+
+/*
+ * mon_start - start up the monitoring software
+ */
+void
+mon_start(
+ int mode
+ )
+{
+
+ if (mon_enabled != MON_OFF) {
+ mon_enabled |= mode;
+ return;
+ }
+ if (mode == MON_OFF)
+ return;
+
+ if (!mon_have_memory) {
+ mon_total_mem = 0;
+ mon_mem_increments = 0;
+ mon_free = NULL;
+ mon_getmoremem();
+ mon_have_memory = 1;
+ }
+
+ mon_mru_list.mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = &mon_mru_list;
+ mon_enabled = mode;
+}
+
+
+/*
+ * mon_stop - stop the monitoring software
+ */
+void
+mon_stop(
+ int mode
+ )
+{
+ register struct mon_data *md, *md_next;
+ register int i;
+
+ if (mon_enabled == MON_OFF)
+ return;
+ if ((mon_enabled & mode) == 0 || mode == MON_OFF)
+ return;
+
+ mon_enabled &= ~mode;
+ if (mon_enabled != MON_OFF)
+ return;
+
+ /*
+ * Put everything back on the free list
+ */
+ for (i = 0; i < MON_HASH_SIZE; i++) {
+ md = mon_hash[i]; /* get next list */
+ mon_hash[i] = NULL; /* zero the list head */
+ while (md != NULL) {
+ md_next = md->hash_next;
+ md->hash_next = mon_free;
+ mon_free = md;
+ md = md_next;
+ }
+ }
+
+ mon_mru_list.mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = &mon_mru_list;
+}
+
+
+/*
+ * ntp_monitor - record stats about this packet
+ */
+void
+ntp_monitor(
+ struct recvbuf *rbufp
+ )
+{
+ register struct pkt *pkt;
+ register struct mon_data *md;
+ struct sockaddr_storage addr;
+ register int hash;
+ register int mode;
+
+ if (mon_enabled == MON_OFF)
+ return;
+
+ pkt = &rbufp->recv_pkt;
+ memset(&addr, 0, sizeof(addr));
+ memcpy(&addr, &(rbufp->recv_srcadr), sizeof(addr));
+ hash = MON_HASH(&addr);
+ mode = PKT_MODE(pkt->li_vn_mode);
+ md = mon_hash[hash];
+ while (md != NULL) {
+
+ /*
+ * Match address only to conserve MRU size.
+ */
+ if (SOCKCMP(&md->rmtadr, &addr)) {
+ md->drop_count = current_time - md->lasttime;
+ md->lasttime = current_time;
+ md->count++;
+ md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
+ md->mode = (u_char) mode;
+ md->version = PKT_VERSION(pkt->li_vn_mode);
+
+ /*
+ * Shuffle to the head of the MRU list.
+ */
+ md->mru_next->mru_prev = md->mru_prev;
+ md->mru_prev->mru_next = md->mru_next;
+ md->mru_next = mon_mru_list.mru_next;
+ md->mru_prev = &mon_mru_list;
+ mon_mru_list.mru_next->mru_prev = md;
+ mon_mru_list.mru_next = md;
+ return;
+ }
+ md = md->hash_next;
+ }
+
+ /*
+ * If we got here, this is the first we've heard of this
+ * guy. Get him some memory, either from the free list
+ * or from the tail of the MRU list.
+ */
+ if (mon_free == NULL && mon_total_mem >= MAXMONMEM) {
+
+ /*
+ * Preempt from the MRU list if old enough.
+ */
+ md = mon_mru_list.mru_prev;
+ if (((u_long)RANDOM & 0xffffffff) / FRAC >
+ (double)(current_time - md->lasttime) / mon_age)
+ return;
+
+ md->mru_prev->mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = md->mru_prev;
+ remove_from_hash(md);
+ } else {
+ if (mon_free == NULL)
+ mon_getmoremem();
+ md = mon_free;
+ mon_free = md->hash_next;
+ }
+
+ /*
+ * Got one, initialize it
+ */
+ md->avg_interval = 0;
+ md->lasttime = current_time;
+ md->count = 1;
+ md->drop_count = 0;
+ memset(&md->rmtadr, 0, sizeof(md->rmtadr));
+ memcpy(&md->rmtadr, &addr, sizeof(addr));
+ md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
+ md->mode = (u_char) mode;
+ md->version = PKT_VERSION(pkt->li_vn_mode);
+ md->interface = rbufp->dstadr;
+ md->cast_flags = (u_char)(((rbufp->dstadr->flags & INT_MULTICAST) &&
+ rbufp->fd == md->interface->fd) ? MDF_MCAST: rbufp->fd ==
+ md->interface->bfd ? MDF_BCAST : MDF_UCAST);
+
+ /*
+ * Drop him into front of the hash table. Also put him on top of
+ * the MRU list.
+ */
+ md->hash_next = mon_hash[hash];
+ mon_hash[hash] = md;
+ md->mru_next = mon_mru_list.mru_next;
+ md->mru_prev = &mon_mru_list;
+ mon_mru_list.mru_next->mru_prev = md;
+ mon_mru_list.mru_next = md;
+}
+
+
+/*
+ * mon_getmoremem - get more memory and put it on the free list
+ */
+static void
+mon_getmoremem(void)
+{
+ register struct mon_data *md;
+ register int i;
+ struct mon_data *freedata; /* 'old' free list (null) */
+
+ md = (struct mon_data *)emalloc(MONMEMINC *
+ sizeof(struct mon_data));
+ freedata = mon_free;
+ mon_free = md;
+ for (i = 0; i < (MONMEMINC-1); i++) {
+ md->hash_next = (md + 1);
+ md++;
+ }
+
+ /*
+ * md now points at the last. Link in the rest of the chain.
+ */
+ md->hash_next = freedata;
+ mon_total_mem += MONMEMINC;
+ mon_mem_increments++;
+}
+
+static void
+remove_from_hash(
+ struct mon_data *md
+ )
+{
+ register int hash;
+ register struct mon_data *md_prev;
+
+ hash = MON_HASH(&md->rmtadr);
+ if (mon_hash[hash] == md) {
+ mon_hash[hash] = md->hash_next;
+ } else {
+ md_prev = mon_hash[hash];
+ while (md_prev->hash_next != md) {
+ md_prev = md_prev->hash_next;
+ if (md_prev == NULL) {
+ /* logic error */
+ return;
+ }
+ }
+ md_prev->hash_next = md->hash_next;
+ }
+}
diff --git a/ntpd/ntp_peer.c b/ntpd/ntp_peer.c
new file mode 100644
index 0000000..cf8a600
--- /dev/null
+++ b/ntpd/ntp_peer.c
@@ -0,0 +1,880 @@
+/*
+ * ntp_peer.c - management of data maintained for peer associations
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#ifdef OPENSSL
+#include "openssl/rand.h"
+#endif /* OPENSSL */
+
+/*
+ * Table of valid association combinations
+ * ---------------------------------------
+ *
+ * packet->mode
+ * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST
+ * ---------- | ---------------------------------------------
+ * NO_PEER | e 1 e 1 1 1
+ * ACTIVE | e 1 1 0 0 0
+ * PASSIVE | e 1 e 0 0 0
+ * CLIENT | e 0 0 0 1 1
+ * SERVER | e 0 0 0 0 0
+ * BCAST | e 0 0 0 0 0
+ * CONTROL | e 0 0 0 0 0
+ * PRIVATE | e 0 0 0 0 0
+ * BCLIENT | e 0 0 0 e 1
+ *
+ * One point to note here: a packet in BCAST mode can potentially match
+ * a peer in CLIENT mode, but we that is a special case and we check for
+ * that early in the decision process. This avoids having to keep track
+ * of what kind of associations are possible etc... We actually
+ * circumvent that problem by requiring that the first b(m)roadcast
+ * received after the change back to BCLIENT mode sets the clock.
+ */
+
+int AM[AM_MODES][AM_MODES] = {
+/* { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */
+
+/*NONE*/{ AM_ERR, AM_NEWPASS, AM_ERR, AM_FXMIT, AM_MANYCAST, AM_NEWBCL},
+
+/*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*P*/ { AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*C*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_POSSBCL},
+
+/*S*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*CNTL*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*PRIV*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_ERR, AM_PROCPKT},
+};
+
+#define MATCH_ASSOC(x,y) AM[(x)][(y)]
+
+/*
+ * These routines manage the allocation of memory to peer structures
+ * and the maintenance of the peer hash table. The two main entry
+ * points are findpeer(), which looks for matching peer sturctures in
+ * the peer list, newpeer(), which allocates a new peer structure and
+ * adds it to the list, and unpeer(), which demobilizes the association
+ * and deallocates the structure.
+ */
+/*
+ * Peer hash tables
+ */
+struct peer *peer_hash[HASH_SIZE]; /* peer hash table */
+int peer_hash_count[HASH_SIZE]; /* peers in each bucket */
+struct peer *assoc_hash[HASH_SIZE]; /* association ID hash table */
+int assoc_hash_count[HASH_SIZE]; /* peers in each bucket */
+static struct peer *peer_free; /* peer structures free list */
+int peer_free_count; /* count of free structures */
+
+/*
+ * Association ID. We initialize this value randomly, then assign a new
+ * value every time the peer structure is incremented.
+ */
+static associd_t current_association_ID; /* association ID */
+
+/*
+ * Memory allocation watermarks.
+ */
+#define INIT_PEER_ALLOC 15 /* initialize for 15 peers */
+#define INC_PEER_ALLOC 5 /* when run out, add 5 more */
+
+/*
+ * Miscellaneous statistic counters which may be queried.
+ */
+u_long peer_timereset; /* time stat counters zeroed */
+u_long findpeer_calls; /* calls to findpeer */
+u_long assocpeer_calls; /* calls to findpeerbyassoc */
+u_long peer_allocations; /* allocations from free list */
+u_long peer_demobilizations; /* structs freed to free list */
+int total_peer_structs; /* peer structs */
+int peer_associations; /* active associations */
+static struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */
+
+static void getmorepeermem P((void));
+
+/*
+ * init_peer - initialize peer data structures and counters
+ *
+ * N.B. We use the random number routine in here. It had better be
+ * initialized prior to getting here.
+ */
+void
+init_peer(void)
+{
+ register int i;
+
+ /*
+ * Clear hash table and counters.
+ */
+ for (i = 0; i < HASH_SIZE; i++) {
+ peer_hash[i] = 0;
+ peer_hash_count[i] = 0;
+ assoc_hash[i] = 0;
+ assoc_hash_count[i] = 0;
+ }
+
+ /*
+ * Clear stat counters
+ */
+ findpeer_calls = peer_allocations = 0;
+ assocpeer_calls = peer_demobilizations = 0;
+
+ /*
+ * Initialize peer memory.
+ */
+ peer_free = 0;
+ for (i = 0; i < INIT_PEER_ALLOC; i++) {
+ init_peer_alloc[i].next = peer_free;
+ peer_free = &init_peer_alloc[i];
+ }
+ total_peer_structs = INIT_PEER_ALLOC;
+ peer_free_count = INIT_PEER_ALLOC;
+
+ /*
+ * Initialize our first association ID
+ */
+ current_association_ID = (associd_t)ranp2(16);
+ if (current_association_ID == 0)
+ current_association_ID = 1;
+}
+
+
+/*
+ * getmorepeermem - add more peer structures to the free list
+ */
+static void
+getmorepeermem(void)
+{
+ register int i;
+ register struct peer *peer;
+
+ peer = (struct peer *)emalloc(INC_PEER_ALLOC *
+ sizeof(struct peer));
+ for (i = 0; i < INC_PEER_ALLOC; i++) {
+ peer->next = peer_free;
+ peer_free = peer;
+ peer++;
+ }
+
+ total_peer_structs += INC_PEER_ALLOC;
+ peer_free_count += INC_PEER_ALLOC;
+}
+
+
+/*
+ * findexistingpeer - return a pointer to a peer in the hash table
+ */
+struct peer *
+findexistingpeer(
+ struct sockaddr_storage *addr,
+ struct peer *start_peer,
+ int mode
+ )
+{
+ register struct peer *peer;
+
+ /*
+ * start_peer is included so we can locate instances of the
+ * same peer through different interfaces in the hash table.
+ */
+ if (start_peer == 0)
+ peer = peer_hash[HASH_ADDR(addr)];
+ else
+ peer = start_peer->next;
+
+ while (peer != 0) {
+ if (SOCKCMP(addr, &peer->srcadr)
+ && NSRCPORT(addr) == NSRCPORT(&peer->srcadr)) {
+ if (mode == -1)
+ return (peer);
+ else if (peer->hmode == mode)
+ break;
+ }
+ peer = peer->next;
+ }
+ return (peer);
+}
+
+
+/*
+ * findpeer - find and return a peer in the hash table.
+ */
+struct peer *
+findpeer(
+ struct sockaddr_storage *srcadr,
+ struct interface *dstadr,
+ int fd,
+ int pkt_mode,
+ int *action
+ )
+{
+ register struct peer *peer;
+ int hash;
+
+ findpeer_calls++;
+ hash = HASH_ADDR(srcadr);
+ for (peer = peer_hash[hash]; peer != NULL; peer = peer->next) {
+ if (SOCKCMP(srcadr, &peer->srcadr)
+ && NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) {
+
+ /*
+ * if the association matching rules determine
+ * that this is not a valid combination, then
+ * look for the next valid peer association.
+ */
+ *action = MATCH_ASSOC(peer->hmode, pkt_mode);
+
+ /*
+ * Sigh! Check if BCLIENT peer in client
+ * server mode, else return error.
+ */
+ if ((*action == AM_POSSBCL) && !(peer->flags &
+ FLAG_MCAST))
+ *action = AM_ERR;
+
+ /*
+ * if an error was returned, exit back right
+ * here.
+ */
+ if (*action == AM_ERR)
+ return ((struct peer *)0);
+
+ /*
+ * if a match is found, we stop our search.
+ */
+ if (*action != AM_NOMATCH)
+ break;
+ }
+ }
+
+ /*
+ * If no matching association is found
+ */
+ if (peer == 0) {
+ *action = MATCH_ASSOC(NO_PEER, pkt_mode);
+ return ((struct peer *)0);
+ }
+ peer->dstadr = dstadr;
+ return (peer);
+}
+
+/*
+ * findpeerbyassocid - find and return a peer using his association ID
+ */
+struct peer *
+findpeerbyassoc(
+ u_int assoc
+ )
+{
+ register struct peer *peer;
+ int hash;
+
+ assocpeer_calls++;
+
+ hash = assoc & HASH_MASK;
+ for (peer = assoc_hash[hash]; peer != 0; peer =
+ peer->ass_next) {
+ if (assoc == peer->associd)
+ return (peer);
+ }
+ return (NULL);
+}
+
+
+/*
+ * clear_all - flush all time values for all associations
+ */
+void
+clear_all(void)
+{
+ struct peer *peer, *next_peer;
+ int n;
+
+ /*
+ * This routine is called when the clock is stepped, and so all
+ * previously saved time values are untrusted.
+ */
+ for (n = 0; n < HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+ next_peer = peer->next;
+ if (peer->flags & FLAG_CONFIG) {
+ if (!(peer->cast_flags & (MDF_ACAST |
+ MDF_MCAST | MDF_BCAST)))
+ peer_clear(peer, "STEP");
+ } else {
+ unpeer(peer);
+ }
+ }
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("clear_all: at %lu\n", current_time);
+#endif
+}
+
+
+/*
+ * unpeer - remove peer structure from hash table and free structure
+ */
+void
+unpeer(
+ struct peer *peer_to_remove
+ )
+{
+ int hash;
+#ifdef OPENSSL
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+
+ if (peer_to_remove->flags & FLAG_SKEY) {
+ sprintf(statstr, "unpeer %d flash %x reach %03o flags %04x",
+ peer_to_remove->associd, peer_to_remove->flash,
+ peer_to_remove->reach, peer_to_remove->flags);
+ record_crypto_stats(&peer_to_remove->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("peer: %s\n", statstr);
+#endif
+ }
+#endif /* OPENSSL */
+#ifdef DEBUG
+ if (debug)
+ printf("demobilize %u %d\n", peer_to_remove->associd,
+ peer_associations);
+#endif
+ peer_clear(peer_to_remove, "NULL");
+ hash = HASH_ADDR(&peer_to_remove->srcadr);
+ peer_hash_count[hash]--;
+ peer_demobilizations++;
+#ifdef REFCLOCK
+ /*
+ * If this peer is actually a clock, shut it down first
+ */
+ if (peer_to_remove->flags & FLAG_REFCLOCK)
+ refclock_unpeer(peer_to_remove);
+#endif
+ peer_to_remove->action = 0; /* disable timeout actions */
+ if (peer_hash[hash] == peer_to_remove)
+ peer_hash[hash] = peer_to_remove->next;
+ else {
+ register struct peer *peer;
+
+ peer = peer_hash[hash];
+ while (peer != 0 && peer->next != peer_to_remove)
+ peer = peer->next;
+
+ if (peer == 0) {
+ peer_hash_count[hash]++;
+ msyslog(LOG_ERR, "peer struct for %s not in table!",
+ stoa(&peer->srcadr));
+ } else {
+ peer->next = peer_to_remove->next;
+ }
+ }
+
+ /*
+ * Remove him from the association hash as well.
+ */
+ hash = peer_to_remove->associd & HASH_MASK;
+ assoc_hash_count[hash]--;
+ if (assoc_hash[hash] == peer_to_remove)
+ assoc_hash[hash] = peer_to_remove->ass_next;
+ else {
+ register struct peer *peer;
+
+ peer = assoc_hash[hash];
+ while (peer != 0 && peer->ass_next != peer_to_remove)
+ peer = peer->ass_next;
+
+ if (peer == 0) {
+ assoc_hash_count[hash]++;
+ msyslog(LOG_ERR,
+ "peer struct for %s not in association table!",
+ stoa(&peer->srcadr));
+ } else {
+ peer->ass_next = peer_to_remove->ass_next;
+ }
+ }
+ peer_to_remove->next = peer_free;
+ peer_free = peer_to_remove;
+ peer_free_count++;
+ peer_associations--;
+}
+
+
+/*
+ * peer_config - configure a new association
+ */
+struct peer *
+peer_config(
+ struct sockaddr_storage *srcadr,
+ struct interface *dstadr,
+ int hmode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ u_int flags,
+ int ttl,
+ keyid_t key,
+ u_char *keystr
+ )
+{
+ register struct peer *peer;
+ u_char cast_flags;
+
+ /*
+ * First search from the beginning for an association with given
+ * remote address and mode. If an interface is given, search
+ * from there to find the association which matches that
+ * destination.
+ */
+ peer = findexistingpeer(srcadr, (struct peer *)0, hmode);
+ if (dstadr != 0) {
+ while (peer != 0) {
+ if (peer->dstadr == dstadr)
+ break;
+ peer = findexistingpeer(srcadr, peer, hmode);
+ }
+ }
+
+ /*
+ * We do a dirty little jig to figure the cast flags. This is
+ * probably not the best place to do this, at least until the
+ * configure code is rebuilt. Note only one flag can be set.
+ */
+ switch (hmode) {
+
+ case MODE_BROADCAST:
+ if(srcadr->ss_family == AF_INET) {
+ if (IN_CLASSD(ntohl(((struct sockaddr_in*)srcadr)->sin_addr.s_addr)))
+ cast_flags = MDF_MCAST;
+ else
+ cast_flags = MDF_BCAST;
+ break;
+ }
+ else {
+ if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)srcadr)->sin6_addr))
+ cast_flags = MDF_MCAST;
+ else
+ cast_flags = MDF_BCAST;
+ break;
+ }
+
+ case MODE_CLIENT:
+ if(srcadr->ss_family == AF_INET) {
+ if (IN_CLASSD(ntohl(((struct sockaddr_in*)srcadr)->sin_addr.s_addr)))
+ cast_flags = MDF_ACAST;
+ else
+ cast_flags = MDF_UCAST;
+ break;
+ }
+ else {
+ if (IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)srcadr)->sin6_addr))
+ cast_flags = MDF_ACAST;
+ else
+ cast_flags = MDF_UCAST;
+ break;
+ }
+
+ default:
+ cast_flags = MDF_UCAST;
+ }
+
+ /*
+ * If the peer is already configured, some dope has a duplicate
+ * configureation entry or another dope is wiggling from afar.
+ */
+ if (peer != 0) {
+ peer->hmode = (u_char)hmode;
+ peer->version = (u_char) version;
+ peer->minpoll = (u_char) minpoll;
+ peer->maxpoll = (u_char) maxpoll;
+ peer->flags = flags | FLAG_CONFIG |
+ (peer->flags & FLAG_REFCLOCK);
+ peer->cast_flags = cast_flags;
+ peer->ttl = (u_char) ttl;
+ peer->keyid = key;
+ peer->precision = sys_precision;
+ peer_clear(peer, "RMOT");
+ return (peer);
+ }
+
+ /*
+ * Here no match has been found, so presumably this is a new
+ * persistent association. Mobilize the thing and initialize its
+ * variables. If emulating ntpdate, force iburst.
+ */
+ if (mode_ntpdate)
+ flags |= FLAG_IBURST;
+ peer = newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll,
+ flags | FLAG_CONFIG, cast_flags, ttl, key);
+ return (peer);
+}
+
+
+/*
+ * newpeer - initialize a new peer association
+ */
+struct peer *
+newpeer(
+ struct sockaddr_storage *srcadr,
+ struct interface *dstadr,
+ int hmode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ u_int flags,
+ u_char cast_flags,
+ int ttl,
+ keyid_t key
+ )
+{
+ register struct peer *peer;
+ register int i;
+#ifdef OPENSSL
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+#endif /* OPENSSL */
+
+ /*
+ * Allocate a new peer structure. Some dirt here, since some of
+ * the initialization requires knowlege of our system state.
+ */
+ if (peer_free_count == 0)
+ getmorepeermem();
+ peer = peer_free;
+ peer_free = peer->next;
+ peer_free_count--;
+ peer_associations++;
+ memset((char *)peer, 0, sizeof(struct peer));
+
+ /*
+ * Assign an association ID and increment the system variable.
+ */
+ peer->associd = current_association_ID;
+ if (++current_association_ID == 0)
+ ++current_association_ID;
+
+ /*
+ * Initialize the peer structure and dance the interface jig.
+ * Reference clocks step the loopback waltz, the others
+ * squaredance around the interface list looking for a buddy. If
+ * the dance peters out, there is always the wildcard interface.
+ * This might happen in some systems and would preclude proper
+ * operation with public key cryptography.
+ */
+ if (ISREFCLOCKADR(srcadr))
+ peer->dstadr = loopback_interface;
+ else if (cast_flags & (MDF_BCLNT | MDF_ACAST | MDF_MCAST | MDF_BCAST)) {
+ peer->dstadr = findbcastinter(srcadr);
+ /*
+ * If it was a multicast packet, findbcastinter() may not
+ * find it, so try a little harder.
+ */
+ if (peer->dstadr == ANY_INTERFACE_CHOOSE(srcadr))
+ peer->dstadr = findinterface(srcadr);
+ } else if (dstadr != NULL && dstadr != ANY_INTERFACE_CHOOSE(srcadr))
+ peer->dstadr = dstadr;
+ else
+ peer->dstadr = findinterface(srcadr);
+ peer->srcadr = *srcadr;
+ peer->hmode = (u_char)hmode;
+ peer->version = (u_char)version;
+ peer->minpoll = (u_char)max(NTP_MINPOLL, minpoll);
+ peer->maxpoll = (u_char)min(NTP_MAXPOLL, maxpoll);
+ peer->flags = flags;
+ if (key != 0)
+ peer->flags |= FLAG_AUTHENABLE;
+ if (key > NTP_MAXKEY)
+ peer->flags |= FLAG_SKEY;
+ peer->cast_flags = cast_flags;
+ peer->ttl = (u_char)ttl;
+ peer->keyid = key;
+ peer->precision = sys_precision;
+ if (cast_flags & MDF_ACAST)
+ peer_clear(peer, "ACST");
+ else if (cast_flags & MDF_MCAST)
+ peer_clear(peer, "MCST");
+ else if (cast_flags & MDF_BCAST)
+ peer_clear(peer, "BCST");
+ else
+ peer_clear(peer, "INIT");
+ if (mode_ntpdate)
+ peer_ntpdate++;
+
+ /*
+ * Note time on statistics timers.
+ */
+ peer->timereset = current_time;
+ peer->timereachable = current_time;
+ peer->timereceived = current_time;
+#ifdef REFCLOCK
+ if (ISREFCLOCKADR(&peer->srcadr)) {
+ /*
+ * We let the reference clock support do clock
+ * dependent initialization. This includes setting
+ * the peer timer, since the clock may have requirements
+ * for this.
+ */
+ if (!refclock_newpeer(peer)) {
+ /*
+ * Dump it, something screwed up
+ */
+ peer->next = peer_free;
+ peer_free = peer;
+ peer_free_count++;
+ return (NULL);
+ }
+ }
+#endif
+
+ /*
+ * Put the new peer in the hash tables.
+ */
+ i = HASH_ADDR(&peer->srcadr);
+ peer->next = peer_hash[i];
+ peer_hash[i] = peer;
+ peer_hash_count[i]++;
+ i = peer->associd & HASH_MASK;
+ peer->ass_next = assoc_hash[i];
+ assoc_hash[i] = peer;
+ assoc_hash_count[i]++;
+#ifdef OPENSSL
+ if (peer->flags & FLAG_SKEY) {
+ sprintf(statstr, "newpeer %d", peer->associd);
+ record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("peer: %s\n", statstr);
+#endif
+ }
+#endif /* OPENSSL */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "newpeer: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %d key %08x\n",
+ peer->dstadr == NULL ? "null" : stoa(&peer->dstadr->sin),
+ stoa(&peer->srcadr),
+ peer->hmode, peer->version, peer->minpoll,
+ peer->maxpoll, peer->flags, peer->cast_flags,
+ peer->ttl, peer->keyid);
+#endif
+ return (peer);
+}
+
+
+/*
+ * peer_unconfig - remove the configuration bit from a peer
+ */
+int
+peer_unconfig(
+ struct sockaddr_storage *srcadr,
+ struct interface *dstadr,
+ int mode
+ )
+{
+ register struct peer *peer;
+ int num_found;
+
+ num_found = 0;
+ peer = findexistingpeer(srcadr, (struct peer *)0, mode);
+ while (peer != 0) {
+ if (peer->flags & FLAG_CONFIG
+ && (dstadr == 0 || peer->dstadr == dstadr)) {
+ num_found++;
+
+ /*
+ * Tricky stuff here. If the peer is polling us
+ * in active mode, turn off the configuration
+ * bit and make the mode passive. This allows us
+ * to avoid dumping a lot of history for peers
+ * we might choose to keep track of in passive
+ * mode. The protocol will eventually terminate
+ * undesirables on its own.
+ */
+ if (peer->hmode == MODE_ACTIVE
+ && peer->pmode == MODE_ACTIVE) {
+ peer->hmode = MODE_PASSIVE;
+ peer->flags &= ~FLAG_CONFIG;
+ } else {
+ unpeer(peer);
+ peer = 0;
+ }
+ }
+ peer = findexistingpeer(srcadr, peer, mode);
+ }
+ return (num_found);
+}
+
+/*
+ * peer_clr_stats - clear peer module stat counters
+ */
+void
+peer_clr_stats(void)
+{
+ findpeer_calls = 0;
+ assocpeer_calls = 0;
+ peer_allocations = 0;
+ peer_demobilizations = 0;
+ peer_timereset = current_time;
+}
+
+/*
+ * peer_reset - reset stat counters in a peer structure
+ */
+void
+peer_reset(
+ struct peer *peer
+ )
+{
+ if (peer == 0)
+ return;
+ peer->sent = 0;
+ peer->received = 0;
+ peer->processed = 0;
+ peer->badauth = 0;
+ peer->bogusorg = 0;
+ peer->oldpkt = 0;
+ peer->seldisptoolarge = 0;
+ peer->selbroken = 0;
+ peer->rank = 0;
+ peer->timereset = current_time;
+}
+
+
+/*
+ * peer_all_reset - reset all peer stat counters
+ */
+void
+peer_all_reset(void)
+{
+ struct peer *peer;
+ int hash;
+
+ for (hash = 0; hash < HASH_SIZE; hash++)
+ for (peer = peer_hash[hash]; peer != 0; peer = peer->next)
+ peer_reset(peer);
+}
+
+
+#ifdef OPENSSL
+/*
+ * expire_all - flush all crypto data and update timestamps.
+ */
+void
+expire_all(void)
+{
+ struct peer *peer, *next_peer;
+ int n;
+
+ /*
+ * This routine is called about once per day from the timer
+ * routine and when the client is first synchronized. Search the
+ * peer list for all associations and flush only the key list
+ * and cookie. If a manycast client association, flush
+ * everything. Then, recompute and sign the agreement public
+ * value, if present.
+ */
+ if (!crypto_flags)
+ return;
+ for (n = 0; n < HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+ next_peer = peer->next;
+ if (!(peer->flags & FLAG_SKEY)) {
+ continue;
+ } else if (peer->cast_flags & MDF_ACAST) {
+ peer_clear(peer, "ACST");
+ } else if (peer->hmode == MODE_ACTIVE ||
+ peer->hmode == MODE_PASSIVE) {
+ key_expire(peer);
+ peer->crypto &= ~(CRYPTO_FLAG_AUTO |
+ CRYPTO_FLAG_AGREE);
+ }
+
+ }
+ }
+ RAND_bytes((u_char *)&sys_private, 4);
+ crypto_update();
+ resetmanycast();
+}
+#endif /* OPENSSL */
+
+
+/*
+ * findmanycastpeer - find and return a manycast peer
+ */
+struct peer *
+findmanycastpeer(
+ struct recvbuf *rbufp
+ )
+{
+ register struct peer *peer;
+ struct pkt *pkt;
+ l_fp p_org;
+ int i;
+
+ /*
+ * This routine is called upon arrival of a client-mode message
+ * from a manycast server. Search the peer list for a manycast
+ * client association where the last transmit timestamp matches
+ * the originate timestamp. This assumes the transmit timestamps
+ * for possibly more than one manycast association are unique.
+ */
+ pkt = &rbufp->recv_pkt;
+ for (i = 0; i < HASH_SIZE; i++) {
+ if (peer_hash_count[i] == 0)
+ continue;
+
+ for (peer = peer_hash[i]; peer != 0; peer =
+ peer->next) {
+ if (peer->cast_flags & MDF_ACAST) {
+ NTOHL_FP(&pkt->org, &p_org);
+ if (L_ISEQU(&peer->xmt, &p_org))
+ return (peer);
+ }
+ }
+ }
+ return (NULL);
+}
+
+
+/*
+ * resetmanycast - reset all manycast clients
+ */
+void
+resetmanycast(void)
+{
+ register struct peer *peer;
+ int i;
+
+ /*
+ * This routine is called when the number of client associations
+ * falls below the minimum. Search the peer list for manycast
+ * client associations and reset the ttl and poll interval.
+ */
+ for (i = 0; i < HASH_SIZE; i++) {
+ if (peer_hash_count[i] == 0)
+ continue;
+
+ for (peer = peer_hash[i]; peer != 0; peer =
+ peer->next) {
+ if (peer->cast_flags & MDF_ACAST) {
+ peer->ttl = 0;
+ poll_update(peer, 0);
+ }
+ }
+ }
+}
diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c
new file mode 100644
index 0000000..451bc9a
--- /dev/null
+++ b/ntpd/ntp_proto.c
@@ -0,0 +1,3208 @@
+/*
+ * ntp_proto.c - NTP version 4 protocol machinery
+ *
+ * ATTENTION: Get approval from Dave Mills on all changes to this file!
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+#include "ntp_control.h"
+#include "ntp_string.h"
+
+#include <stdio.h>
+
+#if defined(VMS) && defined(VMS_LOCALUNIT) /*wjm*/
+#include "ntp_refclock.h"
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <sys/sysctl.h>
+#endif
+
+/*
+ * System variables are declared here. See Section 3.2 of the
+ * specification.
+ */
+u_char sys_leap; /* system leap indicator */
+u_char sys_stratum; /* stratum of system */
+s_char sys_precision; /* local clock precision */
+double sys_rootdelay; /* roundtrip delay to primary source */
+double sys_rootdispersion; /* dispersion to primary source */
+u_int32 sys_refid; /* reference source for local clock */
+u_int32 sys_peer_refid; /* hashed refid of our current peer */
+static double sys_offset; /* current local clock offset */
+l_fp sys_reftime; /* time we were last updated */
+struct peer *sys_peer; /* our current peer */
+struct peer *sys_prefer; /* our cherished peer */
+int sys_kod; /* kod credit */
+int sys_kod_rate = 2; /* max kod packets per second */
+#ifdef OPENSSL
+u_long sys_automax; /* maximum session key lifetime */
+#endif /* OPENSSL */
+
+/*
+ * Nonspecified system state variables.
+ */
+int sys_bclient; /* broadcast client enable */
+double sys_bdelay; /* broadcast client default delay */
+int sys_calldelay; /* modem callup delay (s) */
+int sys_authenticate; /* requre authentication for config */
+l_fp sys_authdelay; /* authentication delay */
+static u_long sys_authdly[2]; /* authentication delay shift reg */
+static u_char leap_consensus; /* consensus of survivor leap bits */
+static double sys_selerr; /* select error (squares) */
+static double sys_syserr; /* system error (squares) */
+keyid_t sys_private; /* private value for session seed */
+int sys_manycastserver; /* respond to manycast client pkts */
+int peer_ntpdate; /* active peers in ntpdate mode */
+int sys_survivors; /* truest of the truechimers */
+#ifdef OPENSSL
+char *sys_hostname; /* gethostname() name */
+#endif /* OPENSSL */
+
+/*
+ * TOS and multicast mapping stuff
+ */
+int sys_floor = 1; /* cluster stratum floor */
+int sys_ceiling = STRATUM_UNSPEC; /* cluster stratum ceiling*/
+int sys_minsane = 1; /* minimum candidates */
+int sys_minclock = NTP_MINCLOCK; /* minimum survivors */
+int sys_cohort = 0; /* cohort switch */
+int sys_ttlmax; /* max ttl mapping vector index */
+u_char sys_ttl[MAX_TTL]; /* ttl mapping vector */
+
+/*
+ * Statistics counters
+ */
+u_long sys_stattime; /* time since reset */
+u_long sys_received; /* packets received */
+u_long sys_processed; /* packets processed */
+u_long sys_newversionpkt; /* current version */
+u_long sys_oldversionpkt; /* recent version */
+u_long sys_unknownversion; /* invalid version */
+u_long sys_restricted; /* access denied */
+u_long sys_badlength; /* bad length or format */
+u_long sys_badauth; /* bad authentication */
+u_long sys_limitrejected; /* rate exceeded */
+
+static double root_distance P((struct peer *));
+static double clock_combine P((struct peer **, int));
+static void peer_xmit P((struct peer *));
+static void fast_xmit P((struct recvbuf *, int, keyid_t, int));
+static void clock_update P((void));
+int default_get_precision P((void));
+static int peer_unfit P((struct peer *));
+
+/*
+ * transmit - Transmit Procedure. See Section 3.4.2 of the
+ * specification.
+ */
+void
+transmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int hpoll;
+
+
+ /*
+ * The polling state machine. There are two kinds of machines,
+ * those that never expect a reply (broadcast and manycast
+ * server modes) and those that do (all other modes). The dance
+ * is intricate...
+ */
+ hpoll = peer->hpoll;
+ if (peer->cast_flags & (MDF_BCAST | MDF_MCAST)) {
+
+ /*
+ * In broadcast mode the poll interval is fixed
+ * at minpoll.
+ */
+ hpoll = peer->minpoll;
+ } else if (peer->cast_flags & MDF_ACAST) {
+
+ /*
+ * In manycast mode we start with the minpoll interval
+ * and ttl. However, the actual poll interval is eight
+ * times the nominal poll interval shown here. If fewer
+ * than sys_minclock servers are found, the ttl is
+ * increased by one and we try again. If this continues
+ * to the max ttl, the poll interval is bumped by one
+ * and we try again. If at least sys_minclock servers
+ * are found, the poll interval increases with the
+ * system poll interval to the max and we continue
+ * indefinately. However, about once per day when the
+ * agreement parameters are refreshed, the manycast
+ * clients are reset and we start from the beginning.
+ * This is to catch and clamp the ttl to the lowest
+ * practical value and avoid knocking on spurious doors.
+ */
+ if (sys_survivors < sys_minclock && peer->ttl <
+ sys_ttlmax)
+ peer->ttl++;
+ hpoll = sys_poll;
+ } else {
+
+ /*
+ * For associations expecting a reply, the watchdog
+ * counter is bumped by one if the peer has not been
+ * heard since the previous poll. If the counter reaches
+ * the max, the poll interval is doubled and the peer is
+ * demobilized if not configured.
+ */
+ peer->unreach++;
+ if (peer->unreach >= NTP_UNREACH) {
+ hpoll++;
+ if (peer->flags & FLAG_CONFIG) {
+
+ /*
+ * If nothing is likely to change in
+ * future, flash the access denied bit
+ * so we won't bother the dude again.
+ */
+ if (memcmp((char *)&peer->refid,
+ "DENY", 4) == 0 ||
+ memcmp((char *)&peer->refid,
+ "CRYP", 4) == 0)
+ peer->flash |= TEST4;
+ } else {
+ unpeer(peer);
+ return;
+ }
+ }
+ if (peer->burst == 0) {
+ u_char oreach;
+
+ oreach = peer->reach;
+ peer->reach <<= 1;
+ peer->hyst *= HYST_TC;
+ if (peer->reach == 0) {
+
+ /*
+ * If this association has become
+ * unreachable, clear it and raise a
+ * trap.
+ */
+ if (oreach != 0) {
+ report_event(EVNT_UNREACH,
+ peer);
+ peer->timereachable =
+ current_time;
+ if (peer->flags & FLAG_CONFIG) {
+ peer_clear(peer,
+ "INIT");
+ } else {
+ unpeer(peer);
+ return;
+ }
+ }
+ if (peer->flags & FLAG_IBURST)
+ peer->burst = NTP_BURST;
+ } else {
+ /*
+ * Here the peer is reachable. If it has
+ * not been heard for three consecutive
+ * polls, stuff the clock filter. Next,
+ * determine the poll interval. If the
+ * peer is unfit for synchronization,
+ * increase it by one; otherwise, use
+ * the system poll interval.
+ */
+ if (!(peer->reach & 0x07)) {
+ clock_filter(peer, 0., 0.,
+ MAXDISPERSE);
+ clock_select();
+ }
+ if (peer_unfit(peer))
+ hpoll++;
+ else
+ hpoll = sys_poll;
+ if (peer->flags & FLAG_BURST)
+ peer->burst = NTP_BURST;
+ }
+ } else {
+
+ /*
+ * Source rate control. If we are restrained,
+ * each burst consists of only one packet.
+ */
+ if (memcmp((char *)&peer->refid, "RSTR", 4) ==
+ 0)
+ peer->burst = 0;
+ else
+ peer->burst--;
+ if (peer->burst == 0) {
+ /*
+ * If a broadcast client at this point,
+ * the burst has concluded, so we switch
+ * to client mode and purge the keylist,
+ * since no further transmissions will
+ * be made.
+ */
+ if (peer->cast_flags & MDF_BCLNT) {
+ peer->hmode = MODE_BCLIENT;
+#ifdef OPENSSL
+ key_expire(peer);
+#endif /* OPENSSL */
+ }
+ poll_update(peer, hpoll);
+ clock_select();
+
+ /*
+ * If ntpdate mode and the clock has not
+ * been set and all peers have completed
+ * the burst, we declare a successful
+ * failure.
+ */
+ if (mode_ntpdate) {
+ peer_ntpdate--;
+ if (peer_ntpdate > 0) {
+ poll_update(
+ peer, hpoll);
+ return;
+ }
+ msyslog(LOG_NOTICE,
+ "no reply; clock not set");
+ exit (0);
+ }
+ poll_update(peer, hpoll);
+ return;
+ }
+ }
+ }
+ peer->outdate = current_time;
+
+ /*
+ * Do not transmit if in broadcast cclient mode or access has
+ * been denied.
+ */
+ if (peer->hmode == MODE_BCLIENT || peer->flash & TEST4) {
+ poll_update(peer, hpoll);
+ return;
+
+ /*
+ * Do not transmit in broadcast mode unless we are synchronized.
+ */
+ } else if (peer->hmode == MODE_BROADCAST && sys_peer == NULL) {
+ poll_update(peer, hpoll);
+ return;
+ }
+ peer_xmit(peer);
+ poll_update(peer, hpoll);
+}
+
+/*
+ * receive - Receive Procedure. See section 3.4.3 in the specification.
+ */
+void
+receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct peer *peer; /* peer structure pointer */
+ register struct pkt *pkt; /* receive packet pointer */
+ int hismode; /* packet mode */
+ int restrict_mask; /* restrict bits */
+ int has_mac; /* length of MAC field */
+ int authlen; /* offset of MAC field */
+ int is_authentic; /* cryptosum ok */
+ keyid_t skeyid = 0; /* key ID */
+ struct sockaddr_storage *dstadr_sin; /* active runway */
+ struct peer *peer2; /* aux peer structure pointer */
+ l_fp p_org; /* originate timestamp */
+ l_fp p_xmt; /* transmit timestamp */
+#ifdef OPENSSL
+ keyid_t tkeyid = 0; /* temporary key ID */
+ keyid_t pkeyid = 0; /* previous key ID */
+ struct autokey *ap; /* autokey structure pointer */
+ int rval; /* cookie snatcher */
+#endif /* OPENSSL */
+ int retcode = AM_NOMATCH;
+
+ /*
+ * Monitor the packet and get restrictions. Note that the packet
+ * length for control and private mode packets must be checked
+ * by the service routines. Note that no statistics counters are
+ * recorded for restrict violations, since these counters are in
+ * the restriction routine. Note the careful distinctions here
+ * between a packet with a format error and a packet that is
+ * simply discarded without prejudice. Some restrictions have to
+ * be handled later in order to generate a kiss-of-death packet.
+ */
+ /*
+ * Bogus port check is before anything, since it probably
+ * reveals a clogging attack.
+ */
+ sys_received++;
+ if (SRCPORT(&rbufp->recv_srcadr) == 0) {
+ sys_badlength++;
+ return; /* bogus port */
+ }
+ ntp_monitor(rbufp);
+ restrict_mask = restrictions(&rbufp->recv_srcadr);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("receive: at %ld %s<-%s restrict %03x\n",
+ current_time, stoa(&rbufp->dstadr->sin),
+ stoa(&rbufp->recv_srcadr), restrict_mask);
+#endif
+ if (restrict_mask & RES_IGNORE) {
+ sys_restricted++;
+ return; /* no anything */
+ }
+ pkt = &rbufp->recv_pkt;
+ hismode = (int)PKT_MODE(pkt->li_vn_mode);
+ if (hismode == MODE_PRIVATE) {
+ if (restrict_mask & RES_NOQUERY) {
+ sys_restricted++;
+ return; /* no query private */
+ }
+ process_private(rbufp, ((restrict_mask &
+ RES_NOMODIFY) == 0));
+ return;
+ }
+ if (hismode == MODE_CONTROL) {
+ if (restrict_mask & RES_NOQUERY) {
+ sys_restricted++;
+ return; /* no query control */
+ }
+ process_control(rbufp, restrict_mask);
+ return;
+ }
+ if (restrict_mask & RES_DONTSERVE) {
+ sys_restricted++;
+ return; /* no time */
+ }
+ if (rbufp->recv_length < LEN_PKT_NOMAC) {
+ sys_badlength++;
+ return; /* runt packet */
+ }
+
+ /*
+ * Version check must be after the query packets, since they
+ * intentionally use early version.
+ */
+ if (PKT_VERSION(pkt->li_vn_mode) == NTP_VERSION) {
+ sys_newversionpkt++; /* new version */
+ } else if (!(restrict_mask & RES_VERSION) &&
+ PKT_VERSION(pkt->li_vn_mode) >= NTP_OLDVERSION) {
+ sys_oldversionpkt++; /* previous version */
+ } else {
+ sys_unknownversion++;
+ return; /* old version */
+ }
+
+ /*
+ * Figure out his mode and validate the packet. This has some
+ * legacy raunch that probably should be removed. In very early
+ * NTP versions mode 0 was equivalent to what later versions
+ * would interpret as client mode.
+ */
+ if (hismode == MODE_UNSPEC) {
+ if (PKT_VERSION(pkt->li_vn_mode) == NTP_OLDVERSION) {
+ hismode = MODE_CLIENT;
+ } else {
+ sys_badlength++;
+ return; /* invalid mode */
+ }
+ }
+
+ /*
+ * Discard broadcast if not enabled as broadcast client. If
+ * Autokey, the wildcard interface cannot be used, so dump
+ * packets gettiing off the bus at that stop as well. This means
+ * that some systems with broken interface code, specifically
+ * Linux, will not work with Autokey.
+ */
+ if (hismode == MODE_BROADCAST) {
+ if (!sys_bclient || restrict_mask & RES_NOPEER) {
+ sys_restricted++;
+ return; /* no client */
+ }
+#ifdef OPENSSL
+ if (crypto_flags && rbufp->dstadr == any_interface) {
+ sys_restricted++;
+ return; /* no client */
+ }
+#endif /* OPENSSL */
+ }
+
+ /*
+ * Parse the extension field if present. We figure out whether
+ * an extension field is present by measuring the MAC size. If
+ * the number of words following the packet header is 0 or 1, no
+ * MAC is present and the packet is not authenticated. If 1, the
+ * packet is a reply to a previous request that failed to
+ * authenticate. If 3, the packet is authenticated with DES; if
+ * 5, the packet is authenticated with MD5. If greater than 5,
+ * an extension field is present. If 2 or 4, the packet is a
+ * runt and goes poof! with a brilliant flash.
+ */
+ authlen = LEN_PKT_NOMAC;
+ has_mac = rbufp->recv_length - authlen;
+ while (has_mac > 0) {
+ int temp;
+
+ if (has_mac % 4 != 0 || has_mac < 0) {
+ sys_badlength++;
+ return; /* bad MAC length */
+ }
+ if (has_mac == 1 * 4 || has_mac == 3 * 4 || has_mac ==
+ MAX_MAC_LEN) {
+ skeyid = ntohl(((u_int32 *)pkt)[authlen / 4]);
+ break;
+
+ } else if (has_mac > MAX_MAC_LEN) {
+ temp = ntohl(((u_int32 *)pkt)[authlen / 4]) &
+ 0xffff;
+ if (temp < 4 || temp > NTP_MAXEXTEN || temp % 4
+ != 0) {
+ sys_badlength++;
+ return; /* bad MAC length */
+ }
+ authlen += temp;
+ has_mac -= temp;
+ } else {
+ sys_badlength++;
+ return; /* bad MAC length */
+ }
+ }
+#ifdef OPENSSL
+ pkeyid = tkeyid = 0;
+#endif /* OPENSSL */
+
+ /*
+ * We have tossed out as many buggy packets as possible early in
+ * the game to reduce the exposure to a clogging attack. Now we
+ * have to burn some cycles to find the association and
+ * authenticate the packet if required. Note that we burn only
+ * MD5 cycles, again to reduce exposure. There may be no
+ * matching association and that's okay.
+ *
+ * More on the autokey mambo. Normally the local interface is
+ * found when the association was mobilized with respect to a
+ * designated remote address. We assume packets arriving from
+ * the remote address arrive via this interface and the local
+ * address used to construct the autokey is the unicast address
+ * of the interface. However, if the sender is a broadcaster,
+ * the interface broadcast address is used instead.
+ * Notwithstanding this technobabble, if the sender is a
+ * multicaster, the broadcast address is null, so we use the
+ * unicast address anyway. Don't ask.
+ */
+ peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, rbufp->fd,
+ hismode, &retcode);
+ is_authentic = 0;
+ dstadr_sin = &rbufp->dstadr->sin;
+ if (has_mac == 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("receive: at %ld %s<-%s mode %d code %d\n",
+ current_time, stoa(&rbufp->dstadr->sin),
+ stoa(&rbufp->recv_srcadr), hismode,
+ retcode);
+#endif
+ } else {
+#ifdef OPENSSL
+ /*
+ * For autokey modes, generate the session key
+ * and install in the key cache. Use the socket
+ * broadcast or unicast address as appropriate.
+ */
+ if (skeyid > NTP_MAXKEY) {
+
+ /*
+ * More on the autokey dance (AKD). A cookie is
+ * constructed from public and private values.
+ * For broadcast packets, the cookie is public
+ * (zero). For packets that match no
+ * association, the cookie is hashed from the
+ * addresses and private value. For server
+ * packets, the cookie was previously obtained
+ * from the server. For symmetric modes, the
+ * cookie was previously constructed using an
+ * agreement protocol; however, should PKI be
+ * unavailable, we construct a fake agreement as
+ * the EXOR of the peer and host cookies.
+ *
+ * hismode ephemeral persistent
+ * =======================================
+ * active 0 cookie#
+ * passive 0% cookie#
+ * client sys cookie 0%
+ * server 0% sys cookie
+ * broadcast 0 0
+ *
+ * # if unsync, 0
+ * % can't happen
+ */
+ if (hismode == MODE_BROADCAST) {
+
+ /*
+ * For broadcaster, use the interface
+ * broadcast address when available;
+ * otherwise, use the unicast address
+ * found when the association was
+ * mobilized.
+ */
+ pkeyid = 0;
+ if (!SOCKNUL(&rbufp->dstadr->bcast))
+ dstadr_sin =
+ &rbufp->dstadr->bcast;
+ } else if (peer == NULL) {
+ pkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin, 0,
+ sys_private, 0);
+ } else {
+ pkeyid = peer->pcookie;
+ }
+
+ /*
+ * The session key includes both the public
+ * values and cookie. In case of an extension
+ * field, the cookie used for authentication
+ * purposes is zero. Note the hash is saved for
+ * use later in the autokey mambo.
+ */
+ if (authlen > LEN_PKT_NOMAC && pkeyid != 0) {
+ session_key(&rbufp->recv_srcadr,
+ dstadr_sin, skeyid, 0, 2);
+ tkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin,
+ skeyid, pkeyid, 0);
+ } else {
+ tkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin,
+ skeyid, pkeyid, 2);
+ }
+
+ }
+#endif /* OPENSSL */
+
+ /*
+ * Compute the cryptosum. Note a clogging attack may
+ * succeed in bloating the key cache. If an autokey,
+ * purge it immediately, since we won't be needing it
+ * again. If the packet is authentic, it may mobilize an
+ * association.
+ */
+ if (authdecrypt(skeyid, (u_int32 *)pkt, authlen,
+ has_mac)) {
+ is_authentic = 1;
+ restrict_mask &= ~RES_DONTTRUST;
+ } else {
+ sys_badauth++;
+ }
+#ifdef OPENSSL
+ if (skeyid > NTP_MAXKEY)
+ authtrust(skeyid, 0);
+#endif /* OPENSSL */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "receive: at %ld %s<-%s mode %d code %d keyid %08x len %d mac %d auth %d\n",
+ current_time, stoa(dstadr_sin),
+ stoa(&rbufp->recv_srcadr), hismode, retcode,
+ skeyid, authlen, has_mac,
+ is_authentic);
+#endif
+ }
+
+ /*
+ * The association matching rules are implemented by a set of
+ * routines and a table in ntp_peer.c. A packet matching an
+ * association is processed by that association. If not and
+ * certain conditions prevail, then an ephemeral association is
+ * mobilized: a broadcast packet mobilizes a broadcast client
+ * aassociation; a manycast server packet mobilizes a manycast
+ * client association; a symmetric active packet mobilizes a
+ * symmetric passive association. And, the adventure
+ * continues...
+ */
+ switch (retcode) {
+ case AM_FXMIT:
+
+ /*
+ * This is a client mode packet not matching a known
+ * association. If from a manycast client we run a few
+ * sanity checks before deciding to send a unicast
+ * server response. Otherwise, it must be a client
+ * request, so send a server response and go home.
+ */
+ if (sys_manycastserver && (rbufp->dstadr->flags &
+ INT_MULTICAST)) {
+
+ /*
+ * There is no reason to respond to a request if
+ * our time is worse than the manycaster or it
+ * has already synchronized to us.
+ */
+ if (sys_peer == NULL ||
+ PKT_TO_STRATUM(pkt->stratum) <
+ sys_stratum || (sys_cohort &&
+ PKT_TO_STRATUM(pkt->stratum) ==
+ sys_stratum) ||
+ rbufp->dstadr->addr_refid == pkt->refid)
+ return; /* manycast dropped */
+ }
+
+ /*
+ * Note that we don't require an authentication check
+ * here, since we can't set the system clock; but, we do
+ * send a crypto-NAK to tell the caller about this.
+ */
+ if (has_mac && !is_authentic)
+ fast_xmit(rbufp, MODE_SERVER, 0, restrict_mask);
+ else
+ fast_xmit(rbufp, MODE_SERVER, skeyid,
+ restrict_mask);
+ return;
+
+ case AM_MANYCAST:
+
+ /*
+ * This is a server mode packet returned in response to
+ * a client mode packet sent to a multicast group
+ * address. The originate timestamp is a good nonce to
+ * reliably associate the reply with what was sent. If
+ * there is no match, that's curious and could be an
+ * intruder attempting to clog, so we just ignore it.
+ *
+ * First, make sure the packet is authentic and not
+ * restricted. If so and the manycast association is
+ * found, we mobilize a client association and copy
+ * pertinent variables from the manycast association to
+ * the new client association.
+ *
+ * There is an implosion hazard at the manycast client,
+ * since the manycast servers send the server packet
+ * immediately. If the guy is already here, don't fire
+ * up a duplicate.
+ */
+ if (restrict_mask & RES_DONTTRUST) {
+ sys_restricted++;
+ return; /* no trust */
+ }
+
+ if (sys_authenticate && !is_authentic)
+ return; /* bad auth */
+
+ if ((peer2 = findmanycastpeer(rbufp)) == NULL)
+ return; /* no assoc match */
+
+ if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr,
+ MODE_CLIENT, PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_IBURST, MDF_UCAST |
+ MDF_ACLNT, 0, skeyid)) == NULL)
+ return; /* system error */
+
+ /*
+ * We don't need these, but it warms the billboards.
+ */
+ peer->ttl = peer2->ttl;
+ break;
+
+ case AM_NEWPASS:
+
+ /*
+ * This is the first packet received from a symmetric
+ * active peer. First, make sure it is authentic and not
+ * restricted. If so, mobilize a passive association.
+ * If authentication fails send a crypto-NAK; otherwise,
+ * kiss the frog.
+ */
+ if (restrict_mask & RES_DONTTRUST) {
+ sys_restricted++;
+ return; /* no trust */
+ }
+ if (sys_authenticate && !is_authentic) {
+ fast_xmit(rbufp, MODE_PASSIVE, 0,
+ restrict_mask);
+ return; /* bad auth */
+ }
+ if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr,
+ MODE_PASSIVE, PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXDPOLL, 0, MDF_UCAST, 0,
+ skeyid)) == NULL)
+ return; /* system error */
+
+ break;
+
+ case AM_NEWBCL:
+
+ /*
+ * This is the first packet received from a broadcast
+ * server. First, make sure it is authentic and not
+ * restricted and that we are a broadcast client. If so,
+ * mobilize a broadcast client association. We don't
+ * kiss any frogs here.
+ */
+ if (restrict_mask & RES_DONTTRUST) {
+ sys_restricted++;
+ return; /* no trust */
+ }
+ if (sys_authenticate && !is_authentic)
+ return; /* bad auth */
+
+ if (!sys_bclient)
+ return; /* not a client */
+
+ if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr,
+ MODE_CLIENT, PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_MCAST |
+ FLAG_IBURST, MDF_BCLNT, 0, skeyid)) == NULL)
+ return; /* system error */
+#ifdef OPENSSL
+ /*
+ * Danger looms. If this is autokey, go process the
+ * extension fields. If something goes wrong, abandon
+ * ship and don't trust subsequent packets.
+ */
+ if (crypto_flags) {
+ if ((rval = crypto_recv(peer, rbufp)) !=
+ XEVNT_OK) {
+ struct sockaddr_storage mskadr_sin;
+
+ unpeer(peer);
+ sys_restricted++;
+ SET_HOSTMASK(&mskadr_sin,
+ rbufp->recv_srcadr.ss_family);
+ hack_restrict(RESTRICT_FLAGS,
+ &rbufp->recv_srcadr, &mskadr_sin,
+ 0, RES_DONTTRUST | RES_TIMEOUT);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "packet: bad exten %x\n",
+ rval);
+#endif
+ }
+ }
+#endif /* OPENSSL */
+ return;
+
+ case AM_POSSBCL:
+
+ /*
+ * This is a broadcast packet received in client mode.
+ * It could happen if the initial client/server volley
+ * is not complete before the next broadcast packet is
+ * received. Be liberal in what we accept.
+ */
+ case AM_PROCPKT:
+
+ /*
+ * This is a symmetric mode packet received in symmetric
+ * mode, a server packet received in client mode or a
+ * broadcast packet received in broadcast client mode.
+ * If it is restricted, this is very strange because it
+ * is rude to send a packet to a restricted address. If
+ * anyway, flash a restrain kiss and skedaddle to
+ * Seattle. If not authentic, leave a light on and
+ * continue.
+ */
+ peer->flash = 0;
+ if (restrict_mask & RES_DONTTRUST) {
+ sys_restricted++;
+ if (peer->flags & FLAG_CONFIG)
+ peer_clear(peer, "RSTR");
+ else
+ unpeer(peer);
+ return; /* no trust */
+ }
+ if (has_mac && !is_authentic)
+ peer->flash |= TEST5; /* bad auth */
+ break;
+
+ default:
+
+ /*
+ * Invalid mode combination. This happens when a passive
+ * mode packet arrives and matches another passive
+ * association or no association at all, or when a
+ * server mode packet arrives and matches a broadcast
+ * client association. This is usually the result of
+ * reconfiguring a client on-fly. If authenticated
+ * passive mode packet, send a crypto-NAK; otherwise,
+ * ignore it.
+ */
+ if (has_mac && hismode == MODE_PASSIVE)
+ fast_xmit(rbufp, MODE_ACTIVE, 0, restrict_mask);
+#ifdef DEBUG
+ if (debug)
+ printf("receive: bad protocol %d\n", retcode);
+#endif
+ return;
+ }
+
+ /*
+ * We do a little homework. Note we can get here with an
+ * authentication error. We Need to do this in order to validate
+ * a crypto-NAK later. Note the order of processing; it is very
+ * important to avoid livelocks, deadlocks and lockpicks.
+ */
+ peer->timereceived = current_time;
+ peer->received++;
+ if (peer->flash & TEST5)
+ peer->flags &= ~FLAG_AUTHENTIC;
+ else
+ peer->flags |= FLAG_AUTHENTIC;
+ NTOHL_FP(&pkt->org, &p_org);
+ NTOHL_FP(&pkt->xmt, &p_xmt);
+
+ /*
+ * If the packet is an old duplicate, we let it through so the
+ * extension fields will be processed.
+ */
+ if (L_ISEQU(&peer->org, &p_xmt)) { /* test 1 */
+ peer->flash |= TEST1; /* dupe */
+ /* fall through */
+
+ /*
+ * For broadcast server mode, loopback checking is disabled. An
+ * authentication error probably means the server restarted or
+ * rolled a new private value. If so, dump the association
+ * and wait for the next message.
+ */
+ } else if (hismode == MODE_BROADCAST) {
+ if (peer->flash & TEST5) {
+ unpeer(peer);
+ return;
+ }
+ /* fall through */
+
+ /*
+ * For server and symmetric modes, if the association transmit
+ * timestamp matches the packet originate timestamp, loopback is
+ * confirmed. Note in symmetric modes this also happens when the
+ * first packet from the active peer arrives at the newly
+ * mobilized passive peer. An authentication error probably
+ * means the server or peer restarted or rolled a new private
+ * value, but could be an intruder trying to stir up trouble.
+ * However, if this is a crypto-NAK, we know it is authentic, so
+ * dump the association and wait for the next message.
+ */
+ } else if (L_ISEQU(&peer->xmt, &p_org)) {
+ if (peer->flash & TEST5) {
+ if (has_mac == 4 && pkt->exten[0] == 0) {
+ if (peer->flags & FLAG_CONFIG)
+ peer_clear(peer, "AUTH");
+ else
+ unpeer(peer);
+ }
+ return;
+ }
+ /* fall through */
+
+ /*
+ * If the client or passive peer has never transmitted anything,
+ * this is either the first message from a symmetric peer or
+ * possibly a duplicate received before the transmit timeout.
+ * Pass it on.
+ */
+ } else if (L_ISZERO(&peer->xmt)) {
+ /* fall through */
+
+ /*
+ * Now it gets interesting. We have transmitted at least one
+ * packet. If the packet originate timestamp is nonzero, it
+ * does not match the association transmit timestamp, which is a
+ * loopback error. This error might mean a manycast server has
+ * answered a manycast honk from us and we already have an
+ * association for him, in which case quietly drop the packet
+ * here. It might mean an old duplicate, dropped packet or
+ * intruder replay, in which case we drop it later after
+ * extension field processing, but never let it touch the time
+ * values.
+ */
+ } else if (!L_ISZERO(&p_org)) {
+ if (peer->cast_flags & MDF_ACLNT)
+ return; /* not a client */
+
+ peer->flash |= TEST2;
+ /* fall through */
+
+ /*
+ * The packet originate timestamp is zero, meaning the other guy
+ * either didn't receive the first packet or died and restarted.
+ * If the association originate timestamp is zero, this is the
+ * first packet received, so we pass it on.
+ */
+ } else if (L_ISZERO(&peer->org)) {
+ /* fall through */
+
+ /*
+ * The other guy has restarted and we are still on the wire. We
+ * should demobilize/clear and get out of Dodge. If this is
+ * symmetric mode, we should also send a crypto-NAK.
+ */
+ } else {
+ if (hismode == MODE_ACTIVE)
+ fast_xmit(rbufp, MODE_PASSIVE, 0,
+ restrict_mask);
+ else if (hismode == MODE_PASSIVE)
+ fast_xmit(rbufp, MODE_ACTIVE, 0, restrict_mask);
+#if DEBUG
+ if (debug)
+ printf("receive: dropped %03x\n", peer->flash);
+#endif
+ if (peer->flags & FLAG_CONFIG)
+ peer_clear(peer, "DROP");
+ else
+ unpeer(peer);
+ return;
+ }
+ if (peer->flash & ~TEST2) {
+ return;
+ }
+
+#ifdef OPENSSL
+ /*
+ * More autokey dance. The rules of the cha-cha are as follows:
+ *
+ * 1. If there is no key or the key is not auto, do nothing.
+ *
+ * 2. If this packet is in response to the one just previously
+ * sent or from a broadcast server, do the extension fields.
+ * Otherwise, assume bogosity and bail out.
+ *
+ * 3. If an extension field contains a verified signature, it is
+ * self-authenticated and we sit the dance.
+ *
+ * 4. If this is a server reply, check only to see that the
+ * transmitted key ID matches the received key ID.
+ *
+ * 5. Check to see that one or more hashes of the current key ID
+ * matches the previous key ID or ultimate original key ID
+ * obtained from the broadcaster or symmetric peer. If no
+ * match, sit the dance and wait for timeout.
+ */
+ if (crypto_flags && (peer->flags & FLAG_SKEY)) {
+ peer->flash |= TEST10;
+ rval = crypto_recv(peer, rbufp);
+ if (rval != XEVNT_OK) {
+ /* fall through */
+
+ } else if (hismode == MODE_SERVER) {
+ if (skeyid == peer->keyid)
+ peer->flash &= ~TEST10;
+ } else if (!peer->flash & TEST10) {
+ peer->pkeyid = skeyid;
+ } else if ((ap = (struct autokey *)peer->recval.ptr) !=
+ NULL) {
+ int i;
+
+ for (i = 0; ; i++) {
+ if (tkeyid == peer->pkeyid ||
+ tkeyid == ap->key) {
+ peer->flash &= ~TEST10;
+ peer->pkeyid = skeyid;
+ break;
+ }
+ if (i > ap->seq)
+ break;
+ tkeyid = session_key(
+ &rbufp->recv_srcadr, dstadr_sin,
+ tkeyid, pkeyid, 0);
+ }
+ }
+ if (!(peer->crypto & CRYPTO_FLAG_PROV)) /* test 11 */
+ peer->flash |= TEST11; /* not proventic */
+
+ /*
+ * If the transmit queue is nonempty, clamp the host
+ * poll interval to the packet poll interval.
+ */
+ if (peer->cmmd != 0) {
+ peer->ppoll = pkt->ppoll;
+ poll_update(peer, 0);
+ }
+
+ /*
+ * If the return code from extension field processing is
+ * not okay, we scrub the association and start over.
+ */
+ if (rval != XEVNT_OK) {
+
+ /*
+ * If the return code is bad, the crypto machine
+ * may be jammed or an intruder may lurk. First,
+ * we demobilize the association, then see if
+ * the error is recoverable.
+ */
+ if (peer->flags & FLAG_CONFIG)
+ peer_clear(peer, "CRYP");
+ else
+ unpeer(peer);
+#ifdef DEBUG
+ if (debug)
+ printf("packet: bad exten %x\n", rval);
+#endif
+ return;
+ }
+
+ /*
+ * If TEST10 is lit, the autokey sequence has broken,
+ * which probably means the server has refreshed its
+ * private value. We reset the poll interval to the
+ & minimum and scrub the association clean.
+ */
+ if (peer->flash & TEST10 && peer->crypto &
+ CRYPTO_FLAG_AUTO) {
+ poll_update(peer, peer->minpoll);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "packet: bad auto %03x\n",
+ peer->flash);
+#endif
+ if (peer->flags & FLAG_CONFIG)
+ peer_clear(peer, "AUTO");
+ else
+ unpeer(peer);
+ return;
+ }
+ }
+#endif /* OPENSSL */
+
+ /*
+ * We have survived the gaunt. Forward to the packet routine. If
+ * a symmetric passive association has been mobilized and the
+ * association doesn't deserve to live, it will die in the
+ * transmit routine if not reachable after timeout. However, if
+ * either symmetric mode and the crypto code has something
+ * urgent to say, we expedite the response.
+ */
+ process_packet(peer, pkt, &rbufp->recv_time);
+}
+
+
+/*
+ * process_packet - Packet Procedure, a la Section 3.4.4 of the
+ * specification. Or almost, at least. If we're in here we have a
+ * reasonable expectation that we will be having a long term
+ * relationship with this host.
+ */
+void
+process_packet(
+ register struct peer *peer,
+ register struct pkt *pkt,
+ l_fp *recv_ts
+ )
+{
+ l_fp t34, t21;
+ double p_offset, p_del, p_disp;
+ double dtemp;
+ l_fp p_rec, p_xmt, p_org, p_reftime;
+ l_fp ci;
+ u_char pmode, pleap, pstratum;
+
+ /*
+ * Swap header fields and keep the books. The books amount to
+ * the receive timestamp and poll interval in the header. We
+ * need these even if there are other problems in order to crank
+ * up the state machine.
+ */
+ sys_processed++;
+ peer->processed++;
+ p_del = FPTOD(NTOHS_FP(pkt->rootdelay));
+ p_disp = FPTOD(NTOHS_FP(pkt->rootdispersion));
+ NTOHL_FP(&pkt->reftime, &p_reftime);
+ NTOHL_FP(&pkt->rec, &p_rec);
+ NTOHL_FP(&pkt->xmt, &p_xmt);
+ pmode = PKT_MODE(pkt->li_vn_mode);
+ pleap = PKT_LEAP(pkt->li_vn_mode);
+ if (pmode != MODE_BROADCAST)
+ NTOHL_FP(&pkt->org, &p_org);
+ else
+ p_org = peer->rec;
+ pstratum = PKT_TO_STRATUM(pkt->stratum);
+
+ /*
+ * Test for unsynchronized server.
+ */
+ if (L_ISHIS(&peer->org, &p_xmt)) /* count old packets */
+ peer->oldpkt++;
+ if (pmode != MODE_BROADCAST && (L_ISZERO(&p_rec) ||
+ L_ISZERO(&p_org))) /* test 3 */
+ peer->flash |= TEST3; /* unsynch */
+ if (L_ISZERO(&p_xmt)) /* test 3 */
+ peer->flash |= TEST3; /* unsynch */
+
+ /*
+ * If any tests fail, the packet is discarded leaving only the
+ * timestamps, which are enough to get the protocol started. The
+ * originate timestamp is copied from the packet transmit
+ * timestamp and the receive timestamp is copied from the
+ * packet receive timestamp. If okay so far, we save the leap,
+ * stratum and refid for billboards.
+ */
+ peer->org = p_xmt;
+ peer->rec = *recv_ts;
+ if (peer->flash) {
+#ifdef DEBUG
+ if (debug)
+ printf("packet: bad data %03x from address: %s\n",
+ peer->flash, stoa(&peer->srcadr));
+#endif
+ return;
+ }
+ peer->leap = pleap;
+ peer->stratum = pstratum;
+ peer->refid = pkt->refid;
+
+ /*
+ * Test for valid peer data (tests 6-8)
+ */
+ ci = p_xmt;
+ L_SUB(&ci, &p_reftime);
+ LFPTOD(&ci, dtemp);
+ if (pleap == LEAP_NOTINSYNC || /* test 6 */
+ pstratum >= STRATUM_UNSPEC || dtemp < 0)
+ peer->flash |= TEST6; /* bad synch */
+ if (!(peer->flags & FLAG_CONFIG) && sys_peer != NULL) { /* test 7 */
+ if (pstratum > sys_stratum && pmode != MODE_ACTIVE)
+ peer->flash |= TEST7; /* bad stratum */
+ }
+ if (p_del < 0 || p_disp < 0 || p_del / /* test 8 */
+ 2 + p_disp >= MAXDISPERSE)
+ peer->flash |= TEST8; /* bad peer values */
+
+ /*
+ * If any tests fail at this point, the packet is discarded.
+ */
+ if (peer->flash) {
+#ifdef DEBUG
+ if (debug)
+ printf("packet: bad header %03x\n",
+ peer->flash);
+#endif
+ return;
+ }
+
+ /*
+ * The header is valid. Capture the remaining header values and
+ * mark as reachable.
+ */
+ record_raw_stats(&peer->srcadr, &peer->dstadr->sin, &p_org,
+ &p_rec, &p_xmt, &peer->rec);
+ peer->pmode = pmode;
+ peer->ppoll = pkt->ppoll;
+ peer->precision = pkt->precision;
+ peer->rootdelay = p_del;
+ peer->rootdispersion = p_disp;
+ peer->reftime = p_reftime;
+ if (!(peer->reach)) {
+ report_event(EVNT_REACH, peer);
+ peer->timereachable = current_time;
+ }
+ peer->reach |= 1;
+ peer->unreach = 0;
+ poll_update(peer, 0);
+
+ /*
+ * If running in a client/server association, calculate the
+ * clock offset c, roundtrip delay d and dispersion e. We use
+ * the equations (reordered from those in the spec). Note that,
+ * in a broadcast association, org has been set to the time of
+ * last reception. Note the computation of dispersion includes
+ * the system precision plus that due to the frequency error
+ * since the originate time.
+ *
+ * Let t1 = p_org, t2 = p_rec, t3 = p_xmt, t4 = peer->rec:
+ */
+ t34 = p_xmt; /* t3 - t4 */
+ L_SUB(&t34, &peer->rec);
+ t21 = p_rec; /* t2 - t1 */
+ L_SUB(&t21, &p_org);
+ ci = peer->rec; /* t4 - t1 */
+ L_SUB(&ci, &p_org);
+ LFPTOD(&ci, p_disp);
+ p_disp = clock_phi * max(p_disp, LOGTOD(sys_precision));
+
+ /*
+ * If running in a broadcast association, the clock offset is
+ * (t1 - t0) corrected by the one-way delay, but we can't
+ * measure that directly. Therefore, we start up in MODE_CLIENT
+ * mode, set FLAG_MCAST and exchange eight messages to determine
+ * the clock offset. When the last message is sent, we switch to
+ * MODE_BCLIENT mode. The next broadcast message after that
+ * computes the broadcast offset and clears FLAG_MCAST.
+ */
+ ci = t34;
+ if (pmode == MODE_BROADCAST) {
+ if (peer->flags & FLAG_MCAST) {
+ LFPTOD(&ci, p_offset);
+ peer->estbdelay = peer->offset - p_offset;
+ if (peer->hmode == MODE_CLIENT)
+ return;
+
+ peer->flags &= ~FLAG_MCAST;
+ }
+ DTOLFP(peer->estbdelay, &t34);
+ L_ADD(&ci, &t34);
+ p_del = peer->delay;
+ } else {
+ L_ADD(&ci, &t21); /* (t2 - t1) + (t3 - t4) */
+ L_RSHIFT(&ci);
+ L_SUB(&t21, &t34); /* (t2 - t1) - (t3 - t4) */
+ LFPTOD(&t21, p_del);
+ }
+ p_del = max(p_del, LOGTOD(sys_precision));
+ LFPTOD(&ci, p_offset);
+ if ((peer->rootdelay + p_del) / 2. + peer->rootdispersion +
+ p_disp >= MAXDISPERSE) /* test 9 */
+ peer->flash |= TEST9; /* bad root distance */
+
+ /*
+ * If any flasher bits remain set at this point, abandon ship.
+ * Otherwise, forward to the clock filter.
+ */
+ if (peer->flash) {
+#ifdef DEBUG
+ if (debug)
+ printf("packet: bad packet data %03x\n",
+ peer->flash);
+#endif
+ return;
+ }
+ clock_filter(peer, p_offset, p_del, p_disp);
+ clock_select();
+ record_peer_stats(&peer->srcadr, ctlpeerstatus(peer),
+ peer->offset, peer->delay, peer->disp,
+ SQRT(peer->jitter));
+}
+
+
+/*
+ * clock_update - Called at system process update intervals.
+ */
+static void
+clock_update(void)
+{
+ u_char oleap;
+ u_char ostratum;
+
+ /*
+ * Reset/adjust the system clock. Do this only if there is a
+ * system peer and the peer epoch is not older than the last
+ * update.
+ */
+ if (sys_peer == NULL)
+ return;
+ if (sys_peer->epoch <= last_time)
+ return;
+#ifdef DEBUG
+ if (debug)
+ printf("clock_update: at %ld assoc %d \n", current_time,
+ peer_associations);
+#endif
+ oleap = sys_leap;
+ ostratum = sys_stratum;
+ switch (local_clock(sys_peer, sys_offset, sys_syserr)) {
+
+ /*
+ * Clock is too screwed up. Just exit for now.
+ */
+ case -1:
+ report_event(EVNT_SYSFAULT, NULL);
+ exit (-1);
+ /*NOTREACHED*/
+
+ /*
+ * Clock was stepped. Flush all time values of all peers.
+ */
+ case 1:
+ clear_all();
+ sys_peer = NULL;
+ sys_stratum = STRATUM_UNSPEC;
+ memcpy(&sys_refid, "STEP", 4);
+ sys_poll = NTP_MINPOLL;
+ report_event(EVNT_CLOCKRESET, NULL);
+#ifdef OPENSSL
+ if (oleap != LEAP_NOTINSYNC)
+ expire_all();
+#endif /* OPENSSL */
+ break;
+
+ /*
+ * Update the system stratum, leap bits, root delay, root
+ * dispersion, reference ID and reference time. We also update
+ * select dispersion and max frequency error. If the leap
+ * changes, we gotta reroll the keys.
+ */
+ default:
+ sys_stratum = (u_char) (sys_peer->stratum + 1);
+ if (sys_stratum == 1 || sys_stratum == STRATUM_UNSPEC)
+ sys_refid = sys_peer->refid;
+ else
+ sys_refid = sys_peer_refid;
+ sys_reftime = sys_peer->rec;
+ sys_rootdelay = sys_peer->rootdelay + sys_peer->delay;
+ sys_leap = leap_consensus;
+ if (oleap == LEAP_NOTINSYNC) {
+ report_event(EVNT_SYNCCHG, NULL);
+#ifdef OPENSSL
+ expire_all();
+#endif /* OPENSSL */
+ }
+ }
+ if (ostratum != sys_stratum)
+ report_event(EVNT_PEERSTCHG, NULL);
+}
+
+
+/*
+ * poll_update - update peer poll interval
+ */
+void
+poll_update(
+ struct peer *peer,
+ int hpoll
+ )
+{
+#ifdef OPENSSL
+ int oldpoll;
+#endif /* OPENSSL */
+
+ /*
+ * A little foxtrot to determine what controls the poll
+ * interval. If the peer is reachable, but the last four polls
+ * have not been answered, use the minimum. If declared
+ * truechimer, use the system poll interval. This allows each
+ * association to ramp up the poll interval for useless sources
+ * and to clamp it to the minimum when first starting up.
+ */
+#ifdef OPENSSL
+ oldpoll = peer->kpoll;
+#endif /* OPENSSL */
+ if (hpoll > 0) {
+ if (hpoll > peer->maxpoll)
+ peer->hpoll = peer->maxpoll;
+ else if (hpoll < peer->minpoll)
+ peer->hpoll = peer->minpoll;
+ else
+ peer->hpoll = (u_char)hpoll;
+ }
+
+ /*
+ * Bit of adventure here. If during a burst and not a poll, just
+ * slink away. If a poll, figure what the next poll should be.
+ * If a burst is pending and a reference clock or a pending
+ * crypto response, delay for one second. If the first sent in a
+ * burst, delay ten seconds for the modem to come up. For others
+ * in the burst, delay two seconds.
+ *
+ * In case of manycast server, make the poll interval, which is
+ * axtually the manycast beacon interval, eight times the system
+ * poll interval. Normally when the host poll interval settles
+ * up to 1024 s, the beacon interval settles up to 2.3 hours.
+ */
+#ifdef OPENSSL
+ if (peer->cmmd != NULL && (sys_leap != LEAP_NOTINSYNC ||
+ peer->crypto)) {
+ peer->nextdate = current_time + RESP_DELAY;
+ } else if (peer->burst > 0) {
+#else /* OPENSSL */
+ if (peer->burst > 0) {
+#endif /* OPENSSL */
+ if (hpoll == 0 && peer->nextdate != current_time)
+ return;
+#ifdef REFCLOCK
+ else if (peer->flags & FLAG_REFCLOCK)
+ peer->nextdate += RESP_DELAY;
+#endif
+ else if (peer->flags & (FLAG_IBURST | FLAG_BURST) &&
+ peer->burst == NTP_BURST)
+ peer->nextdate += sys_calldelay;
+ else
+ peer->nextdate += BURST_DELAY;
+ } else if (peer->cast_flags & MDF_ACAST) {
+ if (sys_survivors >= sys_minclock || peer->ttl >=
+ sys_ttlmax)
+ peer->kpoll = (u_char) (peer->hpoll + 3);
+ else
+ peer->kpoll = peer->hpoll;
+ peer->nextdate = peer->outdate + RANDPOLL(peer->kpoll);
+ } else {
+ peer->kpoll = (u_char) max(min(peer->ppoll,
+ peer->hpoll), peer->minpoll);
+ peer->nextdate = peer->outdate + RANDPOLL(peer->kpoll);
+ }
+ if (peer->nextdate < current_time)
+ peer->nextdate = current_time;
+#ifdef OPENSSL
+ /*
+ * Bit of crass arrogance at this point. If the poll interval
+ * has changed and we have a keylist, the lifetimes in the
+ * keylist are probably bogus. In this case purge the keylist
+ * and regenerate it later.
+ */
+ if (peer->kpoll != oldpoll)
+ key_expire(peer);
+#endif /* OPENSSL */
+#ifdef DEBUG
+ if (debug > 1)
+ printf("poll_update: at %lu %s flags %04x poll %d burst %d last %lu next %lu\n",
+ current_time, ntoa(&peer->srcadr), peer->flags,
+ peer->kpoll, peer->burst, peer->outdate,
+ peer->nextdate);
+#endif
+}
+
+
+/*
+ * clear - clear peer filter registers. See Section 3.4.8 of the spec.
+ */
+void
+peer_clear(
+ struct peer *peer, /* peer structure */
+ char *ident /* tally lights */
+ )
+{
+ u_char oreach, i;
+
+ /*
+ * If cryptographic credentials have been acquired, toss them to
+ * Valhalla. Note that autokeys are ephemeral, in that they are
+ * tossed immediately upon use. Therefore, the keylist can be
+ * purged anytime without needing to preserve random keys. Note
+ * that, if the peer is purged, the cryptographic variables are
+ * purged, too. This makes it much harder to sneak in some
+ * unauthenticated data in the clock filter.
+ */
+ oreach = peer->reach;
+#ifdef OPENSSL
+ key_expire(peer);
+ if (peer->pkey != NULL)
+ EVP_PKEY_free(peer->pkey);
+ if (peer->ident_pkey != NULL)
+ EVP_PKEY_free(peer->ident_pkey);
+ if (peer->subject != NULL)
+ free(peer->subject);
+ if (peer->issuer != NULL)
+ free(peer->issuer);
+ if (peer->iffval != NULL)
+ BN_free(peer->iffval);
+ if (peer->grpkey != NULL)
+ BN_free(peer->grpkey);
+ if (peer->cmmd != NULL)
+ free(peer->cmmd);
+ value_free(&peer->cookval);
+ value_free(&peer->recval);
+ value_free(&peer->tai_leap);
+ value_free(&peer->encrypt);
+ value_free(&peer->sndval);
+#endif /* OPENSSL */
+
+ /*
+ * Wipe the association clean and initialize the nonzero values.
+ */
+ memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO);
+ if (peer == sys_peer)
+ sys_peer = NULL;
+ peer->estbdelay = sys_bdelay;
+ peer->hpoll = peer->kpoll = peer->minpoll;
+ peer->ppoll = peer->maxpoll;
+ peer->jitter = MAXDISPERSE;
+ peer->epoch = current_time;
+#ifdef REFCLOCK
+ if (!(peer->flags & FLAG_REFCLOCK)) {
+ peer->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_UNSPEC;
+ memcpy(&peer->refid, ident, 4);
+ }
+#else
+ peer->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_UNSPEC;
+ memcpy(&peer->refid, ident, 4);
+#endif
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_order[i] = i;
+ peer->filter_disp[i] = MAXDISPERSE;
+ peer->filter_epoch[i] = current_time;
+ }
+
+ /*
+ * If he dies as a broadcast client, he comes back to life as
+ * a broadcast client in client mode in order to recover the
+ * initial autokey values.
+ */
+ if (peer->cast_flags & MDF_BCLNT) {
+ peer->flags |= FLAG_MCAST;
+ peer->hmode = MODE_CLIENT;
+ }
+
+ /*
+ * Randomize the first poll to avoid bunching, but only if the
+ * rascal has never been heard. During initialization use the
+ * association count to spread out the polls at one-second
+ * intervals.
+ */
+ peer->nextdate = peer->update = peer->outdate = current_time;
+ peer->burst = 0;
+ if (oreach)
+ poll_update(peer, 0);
+ else if (initializing)
+ peer->nextdate = current_time + peer_associations;
+ else
+ peer->nextdate = current_time + (u_int)RANDOM %
+ peer_associations;
+#ifdef DEBUG
+ if (debug)
+ printf("peer_clear: at %ld assoc ID %d refid %s\n",
+ current_time, peer->associd, ident);
+#endif
+}
+
+
+/*
+ * clock_filter - add incoming clock sample to filter register and run
+ * the filter procedure to find the best sample.
+ */
+void
+clock_filter(
+ struct peer *peer, /* peer structure pointer */
+ double sample_offset, /* clock offset */
+ double sample_delay, /* roundtrip delay */
+ double sample_disp /* dispersion */
+ )
+{
+ double dst[NTP_SHIFT]; /* distance vector */
+ int ord[NTP_SHIFT]; /* index vector */
+ int i, j, k, m;
+ double dsp, jit, dtemp, etemp;
+
+ /*
+ * Shift the new sample into the register and discard the oldest
+ * one. The new offset and delay come directly from the
+ * timestamp calculations. The dispersion grows from the last
+ * outbound packet or reference clock update to the present time
+ * and increased by the sum of the peer precision and the system
+ * precision. The delay can sometimes swing negative due to
+ * frequency skew, so it is clamped non-negative.
+ */
+ dsp = min(LOGTOD(peer->precision) + LOGTOD(sys_precision) +
+ sample_disp, MAXDISPERSE);
+ j = peer->filter_nextpt;
+ peer->filter_offset[j] = sample_offset;
+ peer->filter_delay[j] = max(0, sample_delay);
+ peer->filter_disp[j] = dsp;
+ j++; j %= NTP_SHIFT;
+ peer->filter_nextpt = (u_short) j;
+
+ /*
+ * Update dispersions since the last update and at the same
+ * time initialize the distance and index lists. The distance
+ * list uses a compound metric. If the sample is valid and
+ * younger than the minimum Allan intercept, use delay;
+ * otherwise, use biased dispersion.
+ */
+ dtemp = clock_phi * (current_time - peer->update);
+ peer->update = current_time;
+ for (i = NTP_SHIFT - 1; i >= 0; i--) {
+ if (i != 0)
+ peer->filter_disp[j] += dtemp;
+ if (peer->filter_disp[j] >= MAXDISPERSE)
+ peer->filter_disp[j] = MAXDISPERSE;
+ if (peer->filter_disp[j] >= MAXDISPERSE)
+ dst[i] = MAXDISPERSE;
+ else if (peer->update - peer->filter_epoch[j] >
+ allan_xpt)
+ dst[i] = MAXDISTANCE + peer->filter_disp[j];
+ else
+ dst[i] = peer->filter_delay[j];
+ ord[i] = j;
+ j++; j %= NTP_SHIFT;
+ }
+ peer->filter_epoch[j] = current_time;
+
+ /*
+ * Sort the samples in both lists by distance.
+ */
+ for (i = 1; i < NTP_SHIFT; i++) {
+ for (j = 0; j < i; j++) {
+ if (dst[j] > dst[i]) {
+ k = ord[j];
+ ord[j] = ord[i];
+ ord[i] = k;
+ etemp = dst[j];
+ dst[j] = dst[i];
+ dst[i] = etemp;
+ }
+ }
+ }
+
+ /*
+ * Copy the index list to the association structure so ntpq
+ * can see it later. Prune the distance list to samples less
+ * than MAXDISTANCE, but keep at least two valid samples for
+ * jitter calculation.
+ */
+ m = 0;
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_order[i] = (u_char) ord[i];
+ if (dst[i] >= MAXDISPERSE || (m >= 2 && dst[i] >=
+ MAXDISTANCE))
+ continue;
+ m++;
+ }
+
+ /*
+ * Compute the dispersion and jitter squares. The dispersion
+ * is weighted exponentially by NTP_FWEIGHT (0.5) so it is
+ * normalized close to 1.0. The jitter is the mean of the square
+ * differences relative to the lowest delay sample. If no
+ * acceptable samples remain in the shift register, quietly
+ * tiptoe home leaving only the dispersion.
+ */
+ jit = 0;
+ peer->disp = 0;
+ k = ord[0];
+ for (i = NTP_SHIFT - 1; i >= 0; i--) {
+
+ j = ord[i];
+ peer->disp = NTP_FWEIGHT * (peer->disp +
+ peer->filter_disp[j]);
+ if (i < m)
+ jit += DIFF(peer->filter_offset[j],
+ peer->filter_offset[k]);
+ }
+
+ /*
+ * If no acceptable samples remain in the shift register,
+ * quietly tiptoe home leaving only the dispersion. Otherwise,
+ * save the offset, delay and jitter average. Note the jitter
+ * must not be less than the system precision.
+ */
+ if (m == 0)
+ return;
+ etemp = fabs(peer->offset - peer->filter_offset[k]);
+ dtemp = sqrt(peer->jitter);
+ peer->offset = peer->filter_offset[k];
+ peer->delay = peer->filter_delay[k];
+ if (m > 1)
+ jit /= m - 1;
+ peer->jitter = max(jit, SQUARE(LOGTOD(sys_precision)));
+
+ /*
+ * A new sample is useful only if it is younger than the last
+ * one used, but only if the sucker has been synchronized.
+ */
+ if (peer->filter_epoch[k] <= peer->epoch && sys_leap !=
+ LEAP_NOTINSYNC) {
+#ifdef DEBUG
+ if (debug)
+ printf("clock_filter: discard %lu\n",
+ peer->epoch - peer->filter_epoch[k]);
+#endif
+ return;
+ }
+
+ /*
+ * If the difference between the last offset and the current one
+ * exceeds the jitter by CLOCK_SGATE and the interval since the
+ * last update is less than twice the system poll interval,
+ * consider the update a popcorn spike and ignore it.
+ */
+ if (m > 1 && etemp > CLOCK_SGATE * dtemp &&
+ (long)(peer->filter_epoch[k] - peer->epoch) < (1 << (sys_poll +
+ 1))) {
+#ifdef DEBUG
+ if (debug)
+ printf("clock_filter: popcorn %.6f %.6f\n",
+ etemp, dtemp);
+#endif
+ return;
+ }
+
+ /*
+ * The mitigated sample statistics are saved for later
+ * processing.
+ */
+ peer->epoch = peer->filter_epoch[k];
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "clock_filter: n %d off %.6f del %.6f dsp %.6f jit %.6f, age %lu\n",
+ m, peer->offset, peer->delay, peer->disp,
+ SQRT(peer->jitter), peer->update - peer->epoch);
+#endif
+}
+
+
+/*
+ * clock_select - find the pick-of-the-litter clock
+ *
+ * LOCKCLOCK: If the local clock is the prefer peer, it will always be
+ * enabled, even if declared falseticker, (2) only the prefer peer can
+ * be selected as the system peer, (3) if the external source is down,
+ * the system leap bits are set to 11 and the stratum set to infinity.
+ */
+void
+clock_select(void)
+{
+ struct peer *peer;
+ int i, j, k, n;
+ int nlist, nl3;
+
+ double d, e, f;
+ int allow, sw, osurv;
+ double high, low;
+ double synch[NTP_MAXCLOCK], error[NTP_MAXCLOCK];
+ struct peer *osys_peer;
+ struct peer *typeacts = NULL;
+ struct peer *typelocal = NULL;
+ struct peer *typepps = NULL;
+ struct peer *typesystem = NULL;
+
+ static int list_alloc = 0;
+ static struct endpoint *endpoint = NULL;
+ static int *indx = NULL;
+ static struct peer **peer_list = NULL;
+ static u_int endpoint_size = 0;
+ static u_int indx_size = 0;
+ static u_int peer_list_size = 0;
+
+ /*
+ * Initialize and create endpoint, index and peer lists big
+ * enough to handle all associations.
+ */
+ osys_peer = sys_peer;
+ sys_peer = NULL;
+ osurv = sys_survivors;
+ sys_survivors = 0;
+ sys_prefer = NULL;
+#ifdef LOCKCLOCK
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ memcpy(&sys_refid, "DOWN", 4);
+#endif /* LOCKCLOCK */
+ nlist = 0;
+ for (n = 0; n < HASH_SIZE; n++)
+ nlist += peer_hash_count[n];
+ if (nlist > list_alloc) {
+ if (list_alloc > 0) {
+ free(endpoint);
+ free(indx);
+ free(peer_list);
+ }
+ while (list_alloc < nlist) {
+ list_alloc += 5;
+ endpoint_size += 5 * 3 * sizeof(*endpoint);
+ indx_size += 5 * 3 * sizeof(*indx);
+ peer_list_size += 5 * sizeof(*peer_list);
+ }
+ endpoint = emalloc(endpoint_size);
+ indx = emalloc(indx_size);
+ peer_list = emalloc(peer_list_size);
+ }
+
+ /*
+ * Initially, we populate the island with all the rifraff peers
+ * that happen to be lying around. Those with seriously
+ * defective clocks are immediately booted off the island. Then,
+ * the falsetickers are culled and put to sea. The truechimers
+ * remaining are subject to repeated rounds where the most
+ * unpopular at each round is kicked off. When the population
+ * has dwindled to sys_minclock, the survivors split a million
+ * bucks and collectively crank the chimes.
+ */
+ nlist = nl3 = 0; /* none yet */
+ for (n = 0; n < HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != NULL; peer =
+ peer->next) {
+ peer->flags &= ~FLAG_SYSPEER;
+ peer->status = CTL_PST_SEL_REJECT;
+
+ /*
+ * Leave the island immediately if the peer is
+ * unfit to synchronize.
+ */
+ if (peer_unfit(peer))
+ continue;
+
+ /*
+ * Don't allow the local clock or modem drivers
+ * in the kitchen at this point, unless the
+ * prefer peer. Do that later, but only if
+ * nobody else is around. These guys are all
+ * configured, so we never throw them away.
+ */
+ if (peer->refclktype == REFCLK_LOCALCLOCK
+#if defined(VMS) && defined(VMS_LOCALUNIT)
+ /* wjm: VMS_LOCALUNIT taken seriously */
+ && REFCLOCKUNIT(&peer->srcadr) !=
+ VMS_LOCALUNIT
+#endif /* VMS && VMS_LOCALUNIT */
+ ) {
+ typelocal = peer;
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no local clock */
+#ifdef LOCKCLOCK
+ else
+ sys_prefer = peer;
+#endif /* LOCKCLOCK */
+ }
+ if (peer->sstclktype == CTL_SST_TS_TELEPHONE) {
+ typeacts = peer;
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no acts */
+ }
+
+ /*
+ * If we get this far, the peer can stay on the
+ * island, but does not yet have the immunity
+ * idol.
+ */
+ peer->status = CTL_PST_SEL_SANE;
+ peer_list[nlist++] = peer;
+
+ /*
+ * Insert each interval endpoint on the sorted
+ * list.
+ */
+ e = peer->offset; /* Upper end */
+ f = root_distance(peer);
+ e = e + f;
+ for (i = nl3 - 1; i >= 0; i--) {
+ if (e >= endpoint[indx[i]].val)
+ break;
+ indx[i + 3] = indx[i];
+ }
+ indx[i + 3] = nl3;
+ endpoint[nl3].type = 1;
+ endpoint[nl3++].val = e;
+
+ e = e - f; /* Center point */
+ for (; i >= 0; i--) {
+ if (e >= endpoint[indx[i]].val)
+ break;
+ indx[i + 2] = indx[i];
+ }
+ indx[i + 2] = nl3;
+ endpoint[nl3].type = 0;
+ endpoint[nl3++].val = e;
+
+ e = e - f; /* Lower end */
+ for (; i >= 0; i--) {
+ if (e >= endpoint[indx[i]].val)
+ break;
+ indx[i + 1] = indx[i];
+ }
+ indx[i + 1] = nl3;
+ endpoint[nl3].type = -1;
+ endpoint[nl3++].val = e;
+ }
+ }
+#ifdef DEBUG
+ if (debug > 2)
+ for (i = 0; i < nl3; i++)
+ printf("select: endpoint %2d %.6f\n",
+ endpoint[indx[i]].type,
+ endpoint[indx[i]].val);
+#endif
+ /*
+ * This is the actual algorithm that cleaves the truechimers
+ * from the falsetickers. The original algorithm was described
+ * in Keith Marzullo's dissertation, but has been modified for
+ * better accuracy.
+ *
+ * Briefly put, we first assume there are no falsetickers, then
+ * scan the candidate list first from the low end upwards and
+ * then from the high end downwards. The scans stop when the
+ * number of intersections equals the number of candidates less
+ * the number of falsetickers. If this doesn't happen for a
+ * given number of falsetickers, we bump the number of
+ * falsetickers and try again. If the number of falsetickers
+ * becomes equal to or greater than half the number of
+ * candidates, the Albanians have won the Byzantine wars and
+ * correct synchronization is not possible.
+ *
+ * Here, nlist is the number of candidates and allow is the
+ * number of falsetickers.
+ */
+ low = 1e9;
+ high = -1e9;
+ for (allow = 0; 2 * allow < nlist; allow++) {
+ int found;
+
+ /*
+ * Bound the interval (low, high) as the largest
+ * interval containing points from presumed truechimers.
+ */
+ found = 0;
+ n = 0;
+ for (i = 0; i < nl3; i++) {
+ low = endpoint[indx[i]].val;
+ n -= endpoint[indx[i]].type;
+ if (n >= nlist - allow)
+ break;
+ if (endpoint[indx[i]].type == 0)
+ found++;
+ }
+ n = 0;
+ for (j = nl3 - 1; j >= 0; j--) {
+ high = endpoint[indx[j]].val;
+ n += endpoint[indx[j]].type;
+ if (n >= nlist - allow)
+ break;
+ if (endpoint[indx[j]].type == 0)
+ found++;
+ }
+
+ /*
+ * If the number of candidates found outside the
+ * interval is greater than the number of falsetickers,
+ * then at least one truechimer is outside the interval,
+ * so go around again. This is what makes this algorithm
+ * different than Marzullo's.
+ */
+ if (found > allow)
+ continue;
+
+ /*
+ * If an interval containing truechimers is found, stop.
+ * If not, increase the number of falsetickers and go
+ * around again.
+ */
+ if (high > low)
+ break;
+ }
+
+ /*
+ * If no survivors remain at this point, check if the local
+ * clock or modem drivers have been found. If so, nominate one
+ * of them as the only survivor. Otherwise, give up and leave
+ * the island to the rats.
+ */
+ if (high <= low) {
+ if (typeacts != 0) {
+ typeacts->status = CTL_PST_SEL_SANE;
+ peer_list[0] = typeacts;
+ nlist = 1;
+ } else if (typelocal != 0) {
+ typelocal->status = CTL_PST_SEL_SANE;
+ peer_list[0] = typelocal;
+ nlist = 1;
+ } else {
+ if (osys_peer != NULL) {
+ sys_poll = NTP_MINPOLL;
+ NLOG(NLOG_SYNCSTATUS)
+ msyslog(LOG_INFO,
+ "no servers reachable");
+ report_event(EVNT_PEERSTCHG, NULL);
+ }
+ if (osurv > 0)
+ resetmanycast();
+ return;
+ }
+ }
+
+ /*
+ * We can only trust the survivors if the number of candidates
+ * sys_minsane is at least the number required to detect and
+ * cast out one falsticker. For the Byzantine agreement
+ * algorithm used here, that number is 4; however, the default
+ * sys_minsane is 1 to speed initial synchronization. Careful
+ * operators will tinker the value to 4 and use at least that
+ * number of synchronization sources.
+ */
+ if (nlist < sys_minsane)
+ return;
+
+ /*
+ * Clustering algorithm. Construct candidate list in order first
+ * by stratum then by root distance, but keep only the best
+ * NTP_MAXCLOCK of them. Scan the list to find falsetickers, who
+ * leave the island immediately. If a falseticker is not
+ * configured, his association raft is drowned as well, but only
+ * if at at least eight poll intervals have gone. We must leave
+ * at least one peer to collect the million bucks.
+ *
+ * Note the hysteresis gimmick that increases the effective
+ * distance for those rascals that have not made the final cut.
+ * This is to discourage clockhopping. Note also the prejudice
+ * against lower stratum peers if the floor is elevated.
+ */
+ j = 0;
+ for (i = 0; i < nlist; i++) {
+ peer = peer_list[i];
+ if (nlist > 1 && (peer->offset <= low || peer->offset >=
+ high)) {
+ if (!(peer->flags & FLAG_CONFIG))
+ unpeer(peer);
+ continue;
+ }
+ peer->status = CTL_PST_SEL_DISTSYSPEER;
+ d = peer->stratum;
+ if (d < sys_floor)
+ d += sys_floor;
+ if (d > sys_ceiling)
+ d = STRATUM_UNSPEC;
+ d = root_distance(peer) + d * MAXDISTANCE;
+ d *= 1. - peer->hyst;
+ if (j >= NTP_MAXCLOCK) {
+ if (d >= synch[j - 1])
+ continue;
+ else
+ j--;
+ }
+ for (k = j; k > 0; k--) {
+ if (d >= synch[k - 1])
+ break;
+ peer_list[k] = peer_list[k - 1];
+ error[k] = error[k - 1];
+ synch[k] = synch[k - 1];
+ }
+ peer_list[k] = peer;
+ error[k] = peer->jitter;
+ synch[k] = d;
+ j++;
+ }
+ nlist = j;
+ if (nlist == 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("clock_select: empty intersection interval\n");
+#endif
+ return;
+ }
+ for (i = 0; i < nlist; i++) {
+ peer_list[i]->status = CTL_PST_SEL_SELCAND;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("select: %s distance %.6f jitter %.6f\n",
+ ntoa(&peer_list[i]->srcadr), synch[i],
+ SQRT(error[i]));
+#endif
+ }
+
+ /*
+ * Now, vote outlyers off the island by select jitter weighted
+ * by root dispersion. Continue voting as long as there are more
+ * than sys_minclock survivors and the minimum select jitter
+ * squared is greater than the maximum peer jitter squared. Stop
+ * if we are about to discard a prefer peer, who of course has
+ * the immunity idol.
+ */
+ while (1) {
+ d = 1e9;
+ e = -1e9;
+ k = 0;
+ for (i = 0; i < nlist; i++) {
+ if (error[i] < d)
+ d = error[i];
+ f = 0;
+ if (nlist > 1) {
+ for (j = 0; j < nlist; j++)
+ f += DIFF(peer_list[j]->offset,
+ peer_list[i]->offset);
+ f /= nlist - 1;
+ }
+ if (f * synch[i] > e) {
+ sys_selerr = f;
+ e = f * synch[i];
+ k = i;
+ }
+ }
+ f = max(sys_selerr, SQUARE(LOGTOD(sys_precision)));
+ if (nlist <= sys_minclock || f <= d ||
+ peer_list[k]->flags & FLAG_PREFER)
+ break;
+#ifdef DEBUG
+ if (debug > 2)
+ printf(
+ "select: drop %s select %.6f jitter %.6f\n",
+ ntoa(&peer_list[k]->srcadr),
+ SQRT(sys_selerr), SQRT(d));
+#endif
+ if (!(peer_list[k]->flags & FLAG_CONFIG) &&
+ peer_list[k]->hmode == MODE_CLIENT)
+ unpeer(peer_list[k]);
+ for (j = k + 1; j < nlist; j++) {
+ peer_list[j - 1] = peer_list[j];
+ error[j - 1] = error[j];
+ }
+ nlist--;
+ }
+
+ /*
+ * What remains is a list usually not greater than sys_minclock
+ * peers. We want only a peer at the lowest stratum to become
+ * the system peer, although all survivors are eligible for the
+ * combining algorithm. First record their order, diddle the
+ * flags and clamp the poll intervals. Then, consider each peer
+ * in turn and OR the leap bits on the assumption that, if some
+ * of them honk nonzero bits, they must know what they are
+ * doing. Check for prefer and pps peers at any stratum. Check
+ * if the old system peer is among the peers at the lowest
+ * stratum. Note that the head of the list is at the lowest
+ * stratum and that unsynchronized peers cannot survive this
+ * far.
+ *
+ * Fiddle for hysteresis. Pump it up for a peer only if the peer
+ * stratum is at least the floor and there are enough survivors.
+ * This minimizes the pain when tossing out rascals beneath the
+ * floorboard. Don't count peers with stratum above the ceiling.
+ * Manycast is sooo complicated.
+ */
+ leap_consensus = 0;
+ for (i = nlist - 1; i >= 0; i--) {
+ peer = peer_list[i];
+ leap_consensus |= peer->leap;
+ peer->status = CTL_PST_SEL_SYNCCAND;
+ peer->rank++;
+ peer->flags |= FLAG_SYSPEER;
+ if (peer->stratum >= sys_floor && osurv >= sys_minclock)
+ peer->hyst = HYST;
+ else
+ peer->hyst = 0;
+ if (peer->stratum <= sys_ceiling)
+ sys_survivors++;
+ if (peer->flags & FLAG_PREFER)
+ sys_prefer = peer;
+ if (peer->refclktype == REFCLK_ATOM_PPS &&
+ peer->stratum < STRATUM_UNSPEC)
+ typepps = peer;
+ if (peer->stratum == peer_list[0]->stratum && peer ==
+ osys_peer)
+ typesystem = peer;
+ }
+
+ /*
+ * In manycast client mode we may have spooked a sizeable number
+ * of peers that we don't need. If there are at least
+ * sys_minclock of them, the manycast message will be turned
+ * off. By the time we get here we nay be ready to prune some of
+ * them back, but we want to make sure all the candicates have
+ * had a chance. If they didn't pass the sanity and intersection
+ * tests, they have already been voted off the island.
+ */
+ if (sys_survivors < sys_minclock && osurv >= sys_minclock)
+ resetmanycast();
+
+ /*
+ * Mitigation rules of the game. There are several types of
+ * peers that make a difference here: (1) prefer local peers
+ * (type REFCLK_LOCALCLOCK with FLAG_PREFER) or prefer modem
+ * peers (type REFCLK_NIST_ATOM etc with FLAG_PREFER), (2) pps
+ * peers (type REFCLK_ATOM_PPS), (3) remaining prefer peers
+ * (flag FLAG_PREFER), (4) the existing system peer, if any, (5)
+ * the head of the survivor list. Note that only one peer can be
+ * declared prefer. The order of preference is in the order
+ * stated. Note that all of these must be at the lowest stratum,
+ * i.e., the stratum of the head of the survivor list.
+ */
+ if (sys_prefer)
+ sw = sys_prefer->refclktype == REFCLK_LOCALCLOCK ||
+ sys_prefer->sstclktype == CTL_SST_TS_TELEPHONE ||
+ !typepps;
+ else
+ sw = 0;
+ if (sw) {
+ sys_peer = sys_prefer;
+ sys_peer->status = CTL_PST_SEL_SYSPEER;
+ sys_offset = sys_peer->offset;
+ sys_syserr = sys_peer->jitter;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: prefer offset %.6f\n",
+ sys_offset);
+#endif
+ }
+#ifndef LOCKCLOCK
+ else if (typepps) {
+ sys_peer = typepps;
+ sys_peer->status = CTL_PST_SEL_PPS;
+ sys_offset = sys_peer->offset;
+ sys_syserr = sys_peer->jitter;
+ if (!pps_control)
+ NLOG(NLOG_SYSEVENT)
+ msyslog(LOG_INFO, "pps sync enabled");
+ pps_control = current_time;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: pps offset %.6f\n",
+ sys_offset);
+#endif
+ } else {
+ if (typesystem)
+ sys_peer = osys_peer;
+ else
+ sys_peer = peer_list[0];
+ sys_peer->status = CTL_PST_SEL_SYSPEER;
+ sys_peer->rank++;
+ sys_offset = clock_combine(peer_list, nlist);
+ sys_syserr = sys_peer->jitter + sys_selerr;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: combine offset %.6f\n",
+ sys_offset);
+#endif
+ }
+#endif /* LOCKCLOCK */
+ if (osys_peer != sys_peer) {
+ char *src;
+
+ if (sys_peer == NULL)
+ sys_peer_refid = 0;
+ else
+ sys_peer_refid = addr2refid(&sys_peer->srcadr);
+ report_event(EVNT_PEERSTCHG, NULL);
+
+#ifdef REFCLOCK
+ if (ISREFCLOCKADR(&sys_peer->srcadr))
+ src = refnumtoa(&sys_peer->srcadr);
+ else
+#endif
+ src = ntoa(&sys_peer->srcadr);
+ NLOG(NLOG_SYNCSTATUS)
+ msyslog(LOG_INFO, "synchronized to %s, stratum=%d", src,
+ sys_peer->stratum);
+ }
+ clock_update();
+}
+
+/*
+ * clock_combine - combine offsets from selected peers
+ */
+static double
+clock_combine(
+ struct peer **peers,
+ int npeers
+ )
+{
+ int i;
+ double x, y, z;
+
+ y = z = 0;
+ for (i = 0; i < npeers; i++) {
+ x = root_distance(peers[i]);
+ y += 1. / x;
+ z += peers[i]->offset / x;
+ }
+ return (z / y);
+}
+
+/*
+ * root_distance - compute synchronization distance from peer to root
+ */
+static double
+root_distance(
+ struct peer *peer
+ )
+{
+ /*
+ * Careful squeak here. The value returned must be greater than
+ * zero blamed on the peer jitter, which must be at least the
+ * square of sys_precision.
+ */
+ return ((peer->rootdelay + peer->delay) / 2 +
+ peer->rootdispersion + peer->disp + clock_phi *
+ (current_time - peer->update) + SQRT(peer->jitter));
+}
+
+/*
+ * peer_xmit - send packet for persistent association.
+ */
+static void
+peer_xmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct pkt xpkt; /* transmit packet */
+ int sendlen, authlen;
+ keyid_t xkeyid = 0; /* transmit key ID */
+ l_fp xmt_tx;
+
+ /*
+ * Initialize transmit packet header fields.
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, peer->version,
+ peer->hmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.ppoll = peer->hpoll;
+ xpkt.precision = sys_precision;
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
+ xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion));
+ xpkt.refid = sys_refid;
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ HTONL_FP(&peer->org, &xpkt.org);
+ HTONL_FP(&peer->rec, &xpkt.rec);
+
+ /*
+ * If the received packet contains a MAC, the transmitted packet
+ * is authenticated and contains a MAC. If not, the transmitted
+ * packet is not authenticated.
+ *
+ * In the current I/O semantics the default interface is set
+ * until after receiving a packet and setting the right
+ * interface. So, the first packet goes out unauthenticated.
+ * That's why the really icky test next is here.
+ */
+ sendlen = LEN_PKT_NOMAC;
+ if (!(peer->flags & FLAG_AUTHENABLE)) {
+ get_systime(&peer->xmt);
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl],
+ &xpkt, sendlen);
+ peer->sent++;
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld %s->%s mode %d\n",
+ current_time, stoa(&peer->dstadr->sin),
+ stoa(&peer->srcadr), peer->hmode);
+#endif
+ return;
+ }
+
+ /*
+ * The received packet contains a MAC, so the transmitted packet
+ * must be authenticated. If autokey is enabled, fuss with the
+ * various modes; otherwise, private key cryptography is used.
+ */
+#ifdef OPENSSL
+ if (crypto_flags && (peer->flags & FLAG_SKEY)) {
+ struct exten *exten; /* extension field */
+ u_int opcode;
+
+ /*
+ * The Public Key Dance (PKD): Cryptographic credentials
+ * are contained in extension fields, each including a
+ * 4-octet length/code word followed by a 4-octet
+ * association ID and optional additional data. Optional
+ * data includes a 4-octet data length field followed by
+ * the data itself. Request messages are sent from a
+ * configured association; response messages can be sent
+ * from a configured association or can take the fast
+ * path without ever matching an association. Response
+ * messages have the same code as the request, but have
+ * a response bit and possibly an error bit set. In this
+ * implementation, a message may contain no more than
+ * one command and no more than one response.
+ *
+ * Cryptographic session keys include both a public and
+ * a private componet. Request and response messages
+ * using extension fields are always sent with the
+ * private component set to zero. Packets without
+ * extension fields indlude the private component when
+ * the session key is generated.
+ */
+ while (1) {
+
+ /*
+ * Allocate and initialize a keylist if not
+ * already done. Then, use the list in inverse
+ * order, discarding keys once used. Keep the
+ * latest key around until the next one, so
+ * clients can use client/server packets to
+ * compute propagation delay.
+ *
+ * Note that once a key is used from the list,
+ * it is retained in the key cache until the
+ * next key is used. This is to allow a client
+ * to retrieve the encrypted session key
+ * identifier to verify authenticity.
+ *
+ * If for some reason a key is no longer in the
+ * key cache, a birthday has happened and the
+ * pseudo-random sequence is probably broken. In
+ * that case, purge the keylist and regenerate
+ * it.
+ */
+ if (peer->keynumber == 0)
+ make_keylist(peer, peer->dstadr);
+ else
+ peer->keynumber--;
+ xkeyid = peer->keylist[peer->keynumber];
+ if (authistrusted(xkeyid))
+ break;
+ else
+ key_expire(peer);
+ }
+ peer->keyid = xkeyid;
+ switch (peer->hmode) {
+
+ /*
+ * In broadcast server mode the autokey values are
+ * required by the broadcast clients. Push them when a
+ * new keylist is generated; otherwise, push the
+ * association message so the client can request them at
+ * other times.
+ */
+ case MODE_BROADCAST:
+ if (peer->flags & FLAG_ASSOC)
+ exten = crypto_args(peer, CRYPTO_AUTO |
+ CRYPTO_RESP, NULL);
+ else
+ exten = crypto_args(peer, CRYPTO_ASSOC |
+ CRYPTO_RESP, NULL);
+ sendlen += crypto_xmit(&xpkt, &peer->srcadr,
+ sendlen, exten, 0);
+ free(exten);
+ break;
+
+ /*
+ * In symmetric modes the digest, certificate, agreement
+ * parameters, cookie and autokey values are required.
+ * The leapsecond table is optional. But, a passive peer
+ * will not believe the active peer until the latter has
+ * synchronized, so the agreement must be postponed
+ * until then. In any case, if a new keylist is
+ * generated, the autokey values are pushed.
+ */
+ case MODE_ACTIVE:
+ case MODE_PASSIVE:
+ if (peer->cmmd != NULL) {
+ peer->cmmd->associd =
+ htonl(peer->associd);
+ sendlen += crypto_xmit(&xpkt,
+ &peer->srcadr, sendlen, peer->cmmd,
+ 0);
+ free(peer->cmmd);
+ peer->cmmd = NULL;
+ }
+ exten = NULL;
+ if (!peer->crypto)
+ exten = crypto_args(peer, CRYPTO_ASSOC,
+ sys_hostname);
+ else if (!(peer->crypto & CRYPTO_FLAG_VALID))
+ exten = crypto_args(peer, CRYPTO_CERT,
+ peer->issuer);
+
+ /*
+ * Identity. Note we have to sign the
+ * certificate before the cookie to avoid a
+ * deadlock when the passive peer is walking the
+ * certificate trail. Awesome.
+ */
+ else if ((opcode = crypto_ident(peer)) != 0)
+ exten = crypto_args(peer, opcode, NULL);
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ !(peer->crypto & CRYPTO_FLAG_SIGN))
+ exten = crypto_args(peer, CRYPTO_SIGN,
+ sys_hostname);
+
+ /*
+ * Autokey. We request the cookie only when the
+ * server and client are synchronized and
+ * signatures work both ways. On the other hand,
+ * the active peer needs the autokey values
+ * before then and when the passive peer is
+ * waiting for the active peer to synchronize.
+ * Any time we regenerate the key list, we offer
+ * the autokey values without being asked.
+ */
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ peer->leap != LEAP_NOTINSYNC &&
+ !(peer->crypto & CRYPTO_FLAG_AGREE))
+ exten = crypto_args(peer, CRYPTO_COOK,
+ NULL);
+ else if (peer->flags & FLAG_ASSOC)
+ exten = crypto_args(peer, CRYPTO_AUTO |
+ CRYPTO_RESP, NULL);
+ else if (!(peer->crypto & CRYPTO_FLAG_AUTO))
+ exten = crypto_args(peer, CRYPTO_AUTO,
+ NULL);
+
+ /*
+ * Postamble. We trade leapseconds only when the
+ * server and client are synchronized.
+ */
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ peer->leap != LEAP_NOTINSYNC &&
+ peer->crypto & CRYPTO_FLAG_TAI &&
+ !(peer->crypto & CRYPTO_FLAG_LEAP))
+ exten = crypto_args(peer, CRYPTO_TAI,
+ NULL);
+ if (exten != NULL) {
+ sendlen += crypto_xmit(&xpkt,
+ &peer->srcadr, sendlen, exten, 0);
+ free(exten);
+ }
+ break;
+
+ /*
+ * In client mode the digest, certificate, agreement
+ * parameters and cookie are required. The leapsecond
+ * table is optional. If broadcast client mode, the
+ * autokey values are required as well. In broadcast
+ * client mode, these values must be acquired during the
+ * client/server exchange to avoid having to wait until
+ * the next key list regeneration. Otherwise, the poor
+ * dude may die a lingering death until becoming
+ * unreachable and attempting rebirth.
+ *
+ * If neither the server or client have the agreement
+ * parameters, the protocol transmits the cookie in the
+ * clear. If the server has the parameters, the client
+ * requests them and the protocol blinds it using the
+ * agreed key. It is a protocol error if the client has
+ * the parameters but the server does not.
+ */
+ case MODE_CLIENT:
+ if (peer->cmmd != NULL) {
+ peer->cmmd->associd =
+ htonl(peer->associd);
+ sendlen += crypto_xmit(&xpkt,
+ &peer->srcadr, sendlen, peer->cmmd,
+ 0);
+ free(peer->cmmd);
+ peer->cmmd = NULL;
+ }
+ exten = NULL;
+ if (!peer->crypto)
+ exten = crypto_args(peer, CRYPTO_ASSOC,
+ sys_hostname);
+ else if (!(peer->crypto & CRYPTO_FLAG_VALID))
+ exten = crypto_args(peer, CRYPTO_CERT,
+ peer->issuer);
+
+ /*
+ * Identity.
+ */
+ else if ((opcode = crypto_ident(peer)) != 0)
+ exten = crypto_args(peer, opcode, NULL);
+
+ /*
+ * Autokey
+ */
+ else if (!(peer->crypto & CRYPTO_FLAG_AGREE))
+ exten = crypto_args(peer, CRYPTO_COOK,
+ NULL);
+ else if (!(peer->crypto & CRYPTO_FLAG_AUTO) &&
+ (peer->cast_flags & MDF_BCLNT))
+ exten = crypto_args(peer, CRYPTO_AUTO,
+ NULL);
+
+ /*
+ * Postamble. We can sign the certificate here,
+ * since there is no chance of deadlock.
+ */
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ !(peer->crypto & CRYPTO_FLAG_SIGN))
+ exten = crypto_args(peer, CRYPTO_SIGN,
+ sys_hostname);
+ else if (sys_leap != LEAP_NOTINSYNC &&
+ peer->crypto & CRYPTO_FLAG_TAI &&
+ !(peer->crypto & CRYPTO_FLAG_LEAP))
+ exten = crypto_args(peer, CRYPTO_TAI,
+ NULL);
+ if (exten != NULL) {
+ sendlen += crypto_xmit(&xpkt,
+ &peer->srcadr, sendlen, exten, 0);
+ free(exten);
+ }
+ break;
+ }
+
+ /*
+ * If extension fields are present, we must use a
+ * private value of zero and force min poll interval.
+ * Most intricate.
+ */
+ if (sendlen > LEN_PKT_NOMAC)
+ session_key(&peer->dstadr->sin, &peer->srcadr,
+ xkeyid, 0, 2);
+ }
+#endif /* OPENSSL */
+ xkeyid = peer->keyid;
+ get_systime(&peer->xmt);
+ L_ADD(&peer->xmt, &sys_authdelay);
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+ if (authlen == 0) {
+ msyslog(LOG_INFO,
+ "transmit: encryption key %d not found", xkeyid);
+ if (peer->flags & FLAG_CONFIG)
+ peer_clear(peer, "NKEY");
+ else
+ unpeer(peer);
+ return;
+ }
+ sendlen += authlen;
+#ifdef OPENSSL
+ if (xkeyid > NTP_MAXKEY)
+ authtrust(xkeyid, 0);
+#endif /* OPENSSL */
+ get_systime(&xmt_tx);
+ if (sendlen > sizeof(xpkt)) {
+ msyslog(LOG_ERR, "buffer overflow %u", sendlen);
+ exit (-1);
+ }
+ sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl], &xpkt,
+ sendlen);
+
+ /*
+ * Calculate the encryption delay. Keep the minimum over
+ * the latest two samples.
+ */
+ L_SUB(&xmt_tx, &peer->xmt);
+ L_ADD(&xmt_tx, &sys_authdelay);
+ sys_authdly[1] = sys_authdly[0];
+ sys_authdly[0] = xmt_tx.l_uf;
+ if (sys_authdly[0] < sys_authdly[1])
+ sys_authdelay.l_uf = sys_authdly[0];
+ else
+ sys_authdelay.l_uf = sys_authdly[1];
+ peer->sent++;
+#ifdef OPENSSL
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d index %d\n",
+ current_time, ntoa(&peer->dstadr->sin),
+ ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen -
+ authlen, authlen, peer->keynumber);
+#endif
+#else
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d\n",
+ current_time, ntoa(&peer->dstadr->sin),
+ ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen -
+ authlen, authlen);
+#endif
+#endif /* OPENSSL */
+}
+
+
+/*
+ * fast_xmit - Send packet for nonpersistent association. Note that
+ * neither the source or destination can be a broadcast address.
+ */
+static void
+fast_xmit(
+ struct recvbuf *rbufp, /* receive packet pointer */
+ int xmode, /* transmit mode */
+ keyid_t xkeyid, /* transmit key ID */
+ int mask /* restrict mask */
+ )
+{
+ struct pkt xpkt; /* transmit packet structure */
+ struct pkt *rpkt; /* receive packet structure */
+ l_fp xmt_ts; /* timestamp */
+ l_fp xmt_tx; /* timestamp after authent */
+ int sendlen, authlen;
+#ifdef OPENSSL
+ u_int32 temp32;
+#endif
+
+ /*
+ * Initialize transmit packet header fields from the receive
+ * buffer provided. We leave some fields intact as received. If
+ * the gazinta was from a multicast address, the gazouta must go
+ * out another way.
+ */
+ rpkt = &rbufp->recv_pkt;
+ if (rbufp->dstadr->flags & INT_MULTICAST)
+ rbufp->dstadr = findinterface(&rbufp->recv_srcadr);
+
+ /*
+ * If the packet has picked up a restriction due to either
+ * access denied or rate exceeded, decide what to do with it.
+ */
+ if (mask & (RES_DONTTRUST | RES_LIMITED)) {
+ char *code = "????";
+
+ if (mask & RES_LIMITED) {
+ sys_limitrejected++;
+ code = "RATE";
+ } else if (mask & RES_DONTTRUST) {
+ sys_restricted++;
+ code = "DENY";
+ }
+
+ /*
+ * Here we light up a kiss-of-death packet. Note the
+ * rate limit on these packets. Once a second initialize
+ * a bucket counter. Every packet sent decrements the
+ * counter until reaching zero. If the counter is zero,
+ * drop the kod.
+ */
+ if (sys_kod == 0 || !(mask & RES_DEMOBILIZE))
+ return;
+
+ sys_kod--;
+ memcpy(&xpkt.refid, code, 4);
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_UNSPEC;
+ } else {
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.refid = sys_refid;
+ }
+ xpkt.ppoll = rpkt->ppoll;
+ xpkt.precision = sys_precision;
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
+ xpkt.rootdispersion =
+ HTONS_FP(DTOUFP(sys_rootdispersion));
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ xpkt.org = rpkt->xmt;
+ HTONL_FP(&rbufp->recv_time, &xpkt.rec);
+
+ /*
+ * If the received packet contains a MAC, the transmitted packet
+ * is authenticated and contains a MAC. If not, the transmitted
+ * packet is not authenticated.
+ */
+ sendlen = LEN_PKT_NOMAC;
+ if (rbufp->recv_length == sendlen) {
+ get_systime(&xmt_ts);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt,
+ sendlen);
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld %s->%s mode %d\n",
+ current_time, stoa(&rbufp->dstadr->sin),
+ stoa(&rbufp->recv_srcadr), xmode);
+#endif
+ return;
+ }
+
+ /*
+ * The received packet contains a MAC, so the transmitted packet
+ * must be authenticated. For private-key cryptography, use the
+ * predefined private keys to generate the cryptosum. For
+ * autokey cryptography, use the server private value to
+ * generate the cookie, which is unique for every source-
+ * destination-key ID combination.
+ */
+#ifdef OPENSSL
+ if (xkeyid > NTP_MAXKEY) {
+ keyid_t cookie;
+
+ /*
+ * The only way to get here is a reply to a legitimate
+ * client request message, so the mode must be
+ * MODE_SERVER. If an extension field is present, there
+ * can be only one and that must be a command. Do what
+ * needs, but with private value of zero so the poor
+ * jerk can decode it. If no extension field is present,
+ * use the cookie to generate the session key.
+ */
+ cookie = session_key(&rbufp->recv_srcadr,
+ &rbufp->dstadr->sin, 0, sys_private, 0);
+ if (rbufp->recv_length >= (int)(sendlen + MAX_MAC_LEN + 2 *
+ sizeof(u_int32))) {
+ session_key(&rbufp->dstadr->sin,
+ &rbufp->recv_srcadr, xkeyid, 0, 2);
+ temp32 = CRYPTO_RESP;
+ rpkt->exten[0] |= htonl(temp32);
+ sendlen += crypto_xmit(&xpkt,
+ &rbufp->recv_srcadr, sendlen,
+ (struct exten *)rpkt->exten, cookie);
+ } else {
+ session_key(&rbufp->dstadr->sin,
+ &rbufp->recv_srcadr, xkeyid, cookie, 2);
+ }
+ }
+#endif /* OPENSSL */
+ get_systime(&xmt_ts);
+ L_ADD(&xmt_ts, &sys_authdelay);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+ sendlen += authlen;
+#ifdef OPENSSL
+ if (xkeyid > NTP_MAXKEY)
+ authtrust(xkeyid, 0);
+#endif /* OPENSSL */
+ get_systime(&xmt_tx);
+ if (sendlen > sizeof(xpkt)) {
+ msyslog(LOG_ERR, "buffer overflow %u", sendlen);
+ exit (-1);
+ }
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt, sendlen);
+
+ /*
+ * Calculate the encryption delay. Keep the minimum over the
+ * latest two samples.
+ */
+ L_SUB(&xmt_tx, &xmt_ts);
+ L_ADD(&xmt_tx, &sys_authdelay);
+ sys_authdly[1] = sys_authdly[0];
+ sys_authdly[0] = xmt_tx.l_uf;
+ if (sys_authdly[0] < sys_authdly[1])
+ sys_authdelay.l_uf = sys_authdly[0];
+ else
+ sys_authdelay.l_uf = sys_authdly[1];
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d\n",
+ current_time, ntoa(&rbufp->dstadr->sin),
+ ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen -
+ authlen, authlen);
+#endif
+}
+
+
+#ifdef OPENSSL
+/*
+ * key_expire - purge the key list
+ */
+void
+key_expire(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int i;
+
+ if (peer->keylist != NULL) {
+ for (i = 0; i <= peer->keynumber; i++)
+ authtrust(peer->keylist[i], 0);
+ free(peer->keylist);
+ peer->keylist = NULL;
+ }
+ value_free(&peer->sndval);
+ peer->keynumber = 0;
+#ifdef DEBUG
+ if (debug)
+ printf("key_expire: at %lu\n", current_time);
+#endif
+}
+#endif /* OPENSSL */
+
+
+/*
+ * Determine if the peer is unfit for synchronization
+ *
+ * A peer is unfit for synchronization if
+ * > not reachable
+ * > a synchronization loop would form
+ * > never been synchronized
+ * > stratum undefined or too high
+ * > too long without synchronization
+ * > designated noselect
+ */
+static int /* 0 if no, 1 if yes */
+peer_unfit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ return (!peer->reach || (peer->stratum > 1 && peer->refid ==
+ peer->dstadr->addr_refid) || peer->leap == LEAP_NOTINSYNC ||
+ peer->stratum >= STRATUM_UNSPEC || root_distance(peer) >=
+ MAXDISTANCE + 2. * clock_phi * ULOGTOD(sys_poll) ||
+ peer->flags & FLAG_NOSELECT );
+}
+
+
+/*
+ * Find the precision of this particular machine
+ */
+#define MINSTEP 100e-9 /* minimum clock increment (s) */
+#define MAXSTEP 20e-3 /* maximum clock increment (s) */
+#define MINLOOPS 5 /* minimum number of step samples */
+
+/*
+ * This routine calculates the system precision, defined as the minimum
+ * of a sequency of differences between successive readings of the
+ * system clock. However, if the system clock can be read more than once
+ * during a tick interval, the difference can be zero or one LSB unit,
+ * where the LSB corresponds to one nanosecond or one microsecond.
+ * Conceivably, if some other process preempts this one and reads the
+ * clock, the difference can be more than one LSB unit.
+ *
+ * For hardware clock frequencies of 10 MHz or less, we assume the
+ * logical clock advances only at the hardware clock tick. For higher
+ * frequencies, we assume the logical clock can advance no more than 100
+ * nanoseconds between ticks.
+ */
+int
+default_get_precision(void)
+{
+ l_fp val; /* current seconds fraction */
+ l_fp last; /* last seconds fraction */
+ l_fp diff; /* difference */
+ double tick; /* computed tick value */
+ double dtemp; /* scratch */
+ int i; /* log2 precision */
+
+ /*
+ * Loop to find tick value in nanoseconds. Toss out outlyer
+ * values less than the minimun tick value. In wacky cases, use
+ * the default maximum value.
+ */
+ get_systime(&last);
+ tick = MAXSTEP;
+ for (i = 0; i < MINLOOPS;) {
+ get_systime(&val);
+ diff = val;
+ L_SUB(&diff, &last);
+ last = val;
+ LFPTOD(&diff, dtemp);
+ if (dtemp < MINSTEP)
+ continue;
+ i++;
+ if (dtemp < tick)
+ tick = dtemp;
+ }
+
+ /*
+ * Find the nearest power of two.
+ */
+ NLOG(NLOG_SYSEVENT)
+ msyslog(LOG_INFO, "precision = %.3f usec", tick * 1e6);
+ for (i = 0; tick <= 1; i++)
+ tick *= 2;
+ if (tick - 1. > 1. - tick / 2)
+ i--;
+ return (-i);
+}
+
+
+/*
+ * kod_proto - called once per second to limit kiss-of-death packets
+ */
+void
+kod_proto(void)
+{
+ sys_kod = sys_kod_rate;
+}
+
+
+/*
+ * init_proto - initialize the protocol module's data
+ */
+void
+init_proto(void)
+{
+ l_fp dummy;
+ int i;
+
+ /*
+ * Fill in the sys_* stuff. Default is don't listen to
+ * broadcasting, authenticate.
+ */
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ memcpy(&sys_refid, "INIT", 4);
+ sys_precision = (s_char)default_get_precision();
+ sys_jitter = LOGTOD(sys_precision);
+ sys_rootdelay = 0;
+ sys_rootdispersion = 0;
+ L_CLR(&sys_reftime);
+ sys_peer = NULL;
+ sys_survivors = 0;
+ get_systime(&dummy);
+ sys_manycastserver = 0;
+ sys_bclient = 0;
+ sys_bdelay = DEFBROADDELAY;
+ sys_calldelay = BURST_DELAY;
+ sys_authenticate = 1;
+ L_CLR(&sys_authdelay);
+ sys_authdly[0] = sys_authdly[1] = 0;
+ sys_stattime = 0;
+ proto_clr_stats();
+ for (i = 0; i < MAX_TTL; i++) {
+ sys_ttl[i] = (u_char)((i * 256) / MAX_TTL);
+ sys_ttlmax = i;
+ }
+#ifdef OPENSSL
+ sys_automax = 1 << NTP_AUTOMAX;
+#endif /* OPENSSL */
+
+ /*
+ * Default these to enable
+ */
+ ntp_enable = 1;
+#ifndef KERNEL_FLL_BUG
+ kern_enable = 1;
+#endif
+ pps_enable = 0;
+ stats_control = 1;
+}
+
+
+/*
+ * proto_config - configure the protocol module
+ */
+void
+proto_config(
+ int item,
+ u_long value,
+ double dvalue,
+ struct sockaddr_storage* svalue
+ )
+{
+ /*
+ * Figure out what he wants to change, then do it
+ */
+ switch (item) {
+
+ /*
+ * Turn on/off kernel discipline.
+ */
+ case PROTO_KERNEL:
+ kern_enable = (int)value;
+ break;
+
+ /*
+ * Turn on/off clock discipline.
+ */
+ case PROTO_NTP:
+ ntp_enable = (int)value;
+ break;
+
+ /*
+ * Turn on/off monitoring.
+ */
+ case PROTO_MONITOR:
+ if (value)
+ mon_start(MON_ON);
+ else
+ mon_stop(MON_ON);
+ break;
+
+ /*
+ * Turn on/off statistics.
+ */
+ case PROTO_FILEGEN:
+ stats_control = (int)value;
+ break;
+
+ /*
+ * Turn on/off facility to listen to broadcasts.
+ */
+ case PROTO_BROADCLIENT:
+ sys_bclient = (int)value;
+ if (value)
+ io_setbclient();
+ else
+ io_unsetbclient();
+ break;
+
+ /*
+ * Add muliticast group address.
+ */
+ case PROTO_MULTICAST_ADD:
+ if (svalue)
+ io_multicast_add(*svalue);
+ break;
+
+ /*
+ * Delete multicast group address.
+ */
+ case PROTO_MULTICAST_DEL:
+ if (svalue)
+ io_multicast_del(*svalue);
+ break;
+
+ /*
+ * Set default broadcast delay.
+ */
+ case PROTO_BROADDELAY:
+ sys_bdelay = dvalue;
+ break;
+
+ /*
+ * Set modem call delay.
+ */
+ case PROTO_CALLDELAY:
+ sys_calldelay = (int)value;
+ break;
+
+ /*
+ * Require authentication to mobilize ephemeral associations.
+ */
+ case PROTO_AUTHENTICATE:
+ sys_authenticate = (int)value;
+ break;
+
+ /*
+ * Turn on/off PPS discipline.
+ */
+ case PROTO_PPS:
+ pps_enable = (int)value;
+ break;
+
+ /*
+ * Set the minimum number of survivors.
+ */
+ case PROTO_MINCLOCK:
+ sys_minclock = (int)dvalue;
+ break;
+
+ /*
+ * Set the minimum number of candidates.
+ */
+ case PROTO_MINSANE:
+ sys_minsane = (int)dvalue;
+ break;
+
+ /*
+ * Set the stratum floor.
+ */
+ case PROTO_FLOOR:
+ sys_floor = (int)dvalue;
+ break;
+
+ /*
+ * Set the stratum ceiling.
+ */
+ case PROTO_CEILING:
+ sys_ceiling = (int)dvalue;
+ break;
+
+ /*
+ * Set the cohort switch.
+ */
+ case PROTO_COHORT:
+ sys_cohort= (int)dvalue;
+ break;
+ /*
+ * Set the adjtime() resolution (s).
+ */
+ case PROTO_ADJ:
+ sys_tick = dvalue;
+ break;
+
+#ifdef REFCLOCK
+ /*
+ * Turn on/off refclock calibrate
+ */
+ case PROTO_CAL:
+ cal_enable = (int)value;
+ break;
+#endif
+ default:
+
+ /*
+ * Log this error.
+ */
+ msyslog(LOG_INFO,
+ "proto_config: illegal item %d, value %ld",
+ item, value);
+ }
+}
+
+
+/*
+ * proto_clr_stats - clear protocol stat counters
+ */
+void
+proto_clr_stats(void)
+{
+ sys_stattime = current_time;
+ sys_received = 0;
+ sys_processed = 0;
+ sys_newversionpkt = 0;
+ sys_oldversionpkt = 0;
+ sys_unknownversion = 0;
+ sys_restricted = 0;
+ sys_badlength = 0;
+ sys_badauth = 0;
+ sys_limitrejected = 0;
+}
diff --git a/ntpd/ntp_refclock.c b/ntpd/ntp_refclock.c
new file mode 100644
index 0000000..172fbda
--- /dev/null
+++ b/ntpd/ntp_refclock.c
@@ -0,0 +1,1129 @@
+/*
+ * ntp_refclock - processing support for reference clocks
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_tty.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#ifdef REFCLOCK
+
+#ifdef TTYCLK
+# ifdef HAVE_SYS_CLKDEFS_H
+# include <sys/clkdefs.h>
+# include <stropts.h>
+# endif
+# ifdef HAVE_SYS_SIO_H
+# include <sys/sio.h>
+# endif
+#endif /* TTYCLK */
+
+#ifdef HAVE_PPSCLOCK_H
+#include <sys/ppsclock.h>
+#endif /* HAVE_PPSCLOCK_H */
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * Reference clock support is provided here by maintaining the fiction
+ * that the clock is actually a peer. As no packets are exchanged with a
+ * reference clock, however, we replace the transmit, receive and packet
+ * procedures with separate code to simulate them. Routines
+ * refclock_transmit() and refclock_receive() maintain the peer
+ * variables in a state analogous to an actual peer and pass reference
+ * clock data on through the filters. Routines refclock_peer() and
+ * refclock_unpeer() are called to initialize and terminate reference
+ * clock associations. A set of utility routines is included to open
+ * serial devices, process sample data, edit input lines to extract
+ * embedded timestamps and to peform various debugging functions.
+ *
+ * The main interface used by these routines is the refclockproc
+ * structure, which contains for most drivers the decimal equivalants of
+ * the year, day, month, hour, second and millisecond/microsecond
+ * decoded from the ASCII timecode. Additional information includes the
+ * receive timestamp, exception report, statistics tallies, etc. In
+ * addition, there may be a driver-specific unit structure used for
+ * local control of the device.
+ *
+ * The support routines are passed a pointer to the peer structure,
+ * which is used for all peer-specific processing and contains a pointer
+ * to the refclockproc structure, which in turn containes a pointer to
+ * the unit structure, if used. The peer structure is identified by an
+ * interface address in the dotted quad form 127.127.t.u (for now only IPv4
+ * addresses are used, so we need to be sure the address is it), where t is
+ * the clock type and u the unit. Some legacy drivers derive the
+ * refclockproc structure pointer from the table typeunit[type][unit].
+ * This interface is strongly discouraged and may be abandoned in
+ * future.
+ */
+#define MAXUNIT 4 /* max units */
+#define FUDGEFAC .1 /* fudge correction factor */
+
+int fdpps; /* pps file descriptor */
+int cal_enable; /* enable refclock calibrate */
+
+/*
+ * Type/unit peer index. Used to find the peer structure for control and
+ * debugging. When all clock drivers have been converted to new style,
+ * this dissapears.
+ */
+static struct peer *typeunit[REFCLK_MAX + 1][MAXUNIT];
+
+/*
+ * Forward declarations
+ */
+#ifdef QSORT_USES_VOID_P
+static int refclock_cmpl_fp P((const void *, const void *));
+#else
+static int refclock_cmpl_fp P((const double *, const double *));
+#endif /* QSORT_USES_VOID_P */
+static int refclock_sample P((struct refclockproc *));
+
+/*
+ * refclock_report - note the occurance of an event
+ *
+ * This routine presently just remembers the report and logs it, but
+ * does nothing heroic for the trap handler. It tries to be a good
+ * citizen and bothers the system log only if things change.
+ */
+void
+refclock_report(
+ struct peer *peer,
+ int code
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ if (pp == NULL)
+ return;
+ if (code == CEVNT_BADREPLY)
+ pp->badformat++;
+ if (code == CEVNT_BADTIME)
+ pp->baddata++;
+ if (code == CEVNT_TIMEOUT)
+ pp->noreply++;
+ if (pp->currentstatus != code) {
+ pp->currentstatus = (u_char)code;
+ pp->lastevent = (u_char)code;
+ if (code == CEVNT_FAULT)
+ msyslog(LOG_ERR,
+ "clock %s event '%s' (0x%02x)",
+ refnumtoa(&peer->srcadr),
+ ceventstr(code), code);
+ else {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_INFO,
+ "clock %s event '%s' (0x%02x)",
+ refnumtoa(&peer->srcadr),
+ ceventstr(code), code);
+ }
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("clock %s event '%s' (0x%02x)\n",
+ refnumtoa(&peer->srcadr),
+ ceventstr(code), code);
+#endif
+}
+
+
+/*
+ * init_refclock - initialize the reference clock drivers
+ *
+ * This routine calls each of the drivers in turn to initialize internal
+ * variables, if necessary. Most drivers have nothing to say at this
+ * point.
+ */
+void
+init_refclock(void)
+{
+ int i, j;
+
+ for (i = 0; i < (int)num_refclock_conf; i++) {
+ if (refclock_conf[i]->clock_init != noentry)
+ (refclock_conf[i]->clock_init)();
+ for (j = 0; j < MAXUNIT; j++)
+ typeunit[i][j] = 0;
+ }
+}
+
+
+/*
+ * refclock_newpeer - initialize and start a reference clock
+ *
+ * This routine allocates and initializes the interface structure which
+ * supports a reference clock in the form of an ordinary NTP peer. A
+ * driver-specific support routine completes the initialization, if
+ * used. Default peer variables which identify the clock and establish
+ * its reference ID and stratum are set here. It returns one if success
+ * and zero if the clock address is invalid or already running,
+ * insufficient resources are available or the driver declares a bum
+ * rap.
+ */
+int
+refclock_newpeer(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid clock address. If already running, shut it
+ * down first.
+ */
+ if (peer->srcadr.ss_family != AF_INET) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock address %s invalid, address family not implemented for refclock",
+ stoa(&peer->srcadr));
+ return (0);
+ }
+ if (!ISREFCLOCKADR(&peer->srcadr)) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock address %s invalid",
+ stoa(&peer->srcadr));
+ return (0);
+ }
+ clktype = (u_char)REFCLOCKTYPE(&peer->srcadr);
+ unit = REFCLOCKUNIT(&peer->srcadr);
+ if (clktype >= num_refclock_conf || unit >= MAXUNIT ||
+ refclock_conf[clktype]->clock_start == noentry) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock type %d invalid\n",
+ clktype);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize interface structure
+ */
+ pp = (struct refclockproc *)emalloc(sizeof(struct refclockproc));
+ if (pp == NULL)
+ return (0);
+ memset((char *)pp, 0, sizeof(struct refclockproc));
+ typeunit[clktype][unit] = peer;
+ peer->procptr = pp;
+
+ /*
+ * Initialize structures
+ */
+ peer->refclktype = clktype;
+ peer->refclkunit = (u_char)unit;
+ peer->flags |= FLAG_REFCLOCK;
+ peer->maxpoll = peer->minpoll;
+ peer->stratum = STRATUM_REFCLOCK;
+ pp->type = clktype;
+ pp->timestarted = current_time;
+
+ /*
+ * Set peer.pmode based on the hmode. For appearances only.
+ */
+ switch (peer->hmode) {
+ case MODE_ACTIVE:
+ peer->pmode = MODE_PASSIVE;
+ break;
+
+ default:
+ peer->pmode = MODE_SERVER;
+ break;
+ }
+
+ /*
+ * Do driver dependent initialization. The above defaults
+ * can be wiggled, then finish up for consistency.
+ */
+ if (!((refclock_conf[clktype]->clock_start)(unit, peer))) {
+ refclock_unpeer(peer);
+ return (0);
+ }
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->maxpoll;
+ peer->refid = pp->refid;
+ return (1);
+}
+
+
+/*
+ * refclock_unpeer - shut down a clock
+ */
+void
+refclock_unpeer(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char clktype;
+ int unit;
+
+ /*
+ * Wiggle the driver to release its resources, then give back
+ * the interface structure.
+ */
+ if (!peer->procptr)
+ return;
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ if (refclock_conf[clktype]->clock_shutdown != noentry)
+ (refclock_conf[clktype]->clock_shutdown)(unit, peer);
+ free(peer->procptr);
+ peer->procptr = 0;
+}
+
+
+/*
+ * refclock_transmit - simulate the transmit procedure
+ *
+ * This routine implements the NTP transmit procedure for a reference
+ * clock. This provides a mechanism to call the driver at the NTP poll
+ * interval, as well as provides a reachability mechanism to detect a
+ * broken radio or other madness.
+ */
+void
+refclock_transmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ u_char clktype;
+ int unit;
+ u_long next;
+
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ peer->sent++;
+
+ /*
+ * This is a ripoff of the peer transmit routine, but
+ * specialized for reference clocks. We do a little less
+ * protocol here and call the driver-specific transmit routine.
+ */
+ next = peer->outdate;
+ if (peer->burst == 0) {
+ u_char oreach;
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_transmit: at %ld %s\n",
+ current_time, stoa(&(peer->srcadr)));
+#endif
+
+ /*
+ * Update reachability and poll variables like the
+ * network code.
+ */
+ oreach = peer->reach;
+ peer->reach <<= 1;
+ if (!peer->reach) {
+ if (oreach) {
+ report_event(EVNT_UNREACH, peer);
+ peer->timereachable = current_time;
+ peer_clear(peer, "NONE");
+ }
+ } else {
+ if (!(oreach & 0x03)) {
+ clock_filter(peer, 0., 0., MAXDISPERSE);
+ clock_select();
+ }
+ if (peer->flags & FLAG_BURST)
+ peer->burst = NSTAGE;
+ }
+ next = current_time;
+ }
+ get_systime(&peer->xmt);
+ if (refclock_conf[clktype]->clock_poll != noentry)
+ (refclock_conf[clktype]->clock_poll)(unit, peer);
+ peer->outdate = next;
+ if (peer->burst > 0)
+ peer->burst--;
+ poll_update(peer, 0);
+}
+
+
+/*
+ * Compare two doubles - used with qsort()
+ */
+#ifdef QSORT_USES_VOID_P
+static int
+refclock_cmpl_fp(
+ const void *p1,
+ const void *p2
+ )
+{
+ const double *dp1 = (const double *)p1;
+ const double *dp2 = (const double *)p2;
+
+ if (*dp1 < *dp2)
+ return (-1);
+ if (*dp1 > *dp2)
+ return (1);
+ return (0);
+}
+#else
+static int
+refclock_cmpl_fp(
+ const double *dp1,
+ const double *dp2
+ )
+{
+ if (*dp1 < *dp2)
+ return (-1);
+ if (*dp1 > *dp2)
+ return (1);
+ return (0);
+}
+#endif /* QSORT_USES_VOID_P */
+
+
+/*
+ * refclock_process_offset - update median filter
+ *
+ * This routine uses the given offset and timestamps to construct a new
+ * entry in the median filter circular buffer. Samples that overflow the
+ * filter are quietly discarded.
+ */
+void
+refclock_process_offset(
+ struct refclockproc *pp, /* refclock structure pointer */
+ l_fp lasttim, /* last timecode timestamp */
+ l_fp lastrec, /* last receive timestamp */
+ double fudge
+ )
+{
+ l_fp lftemp;
+ double doffset;
+
+ pp->lastrec = lastrec;
+ lftemp = lasttim;
+ L_SUB(&lftemp, &lastrec);
+ LFPTOD(&lftemp, doffset);
+ SAMPLE(doffset + fudge);
+}
+
+/*
+ * refclock_process - process a sample from the clock
+ *
+ * This routine converts the timecode in the form days, hours, minutes,
+ * seconds and milliseconds/microseconds to internal timestamp format,
+ * then constructs a new entry in the median filter circular buffer.
+ * Return success (1) if the data are correct and consistent with the
+ * converntional calendar.
+*/
+int
+refclock_process(
+ struct refclockproc *pp /* refclock structure pointer */
+ )
+{
+ l_fp offset, ltemp;
+
+ /*
+ * Compute the timecode timestamp from the days, hours, minutes,
+ * seconds and milliseconds/microseconds of the timecode. Use
+ * clocktime() for the aggregate seconds and the msec/usec for
+ * the fraction, when present. Note that this code relies on the
+ * filesystem time for the years and does not use the years of
+ * the timecode.
+ */
+ if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT,
+ pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui))
+ return (0);
+ offset.l_uf = 0;
+ DTOLFP(pp->nsec / 1e9, &ltemp);
+ L_ADD(&offset, &ltemp);
+ refclock_process_offset(pp, offset, pp->lastrec,
+ pp->fudgetime1);
+ return (1);
+}
+
+/*
+ * refclock_sample - process a pile of samples from the clock
+ *
+ * This routine implements a recursive median filter to suppress spikes
+ * in the data, as well as determine a performance statistic. It
+ * calculates the mean offset and jitter (squares). A time adjustment
+ * fudgetime1 can be added to the final offset to compensate for various
+ * systematic errors. The routine returns the number of samples
+ * processed, which could be zero.
+ */
+static int
+refclock_sample(
+ struct refclockproc *pp /* refclock structure pointer */
+ )
+{
+ int i, j, k, m, n;
+ double offset;
+ double off[MAXSTAGE];
+
+ /*
+ * Copy the raw offsets and sort into ascending order. Don't do
+ * anything if the buffer is empty.
+ */
+ n = 0;
+ while (pp->codeproc != pp->coderecv) {
+ pp->codeproc = (pp->codeproc + 1) % MAXSTAGE;
+ off[n] = pp->filter[pp->codeproc];
+ n++;
+ }
+ if (n == 0)
+ return (0);
+ if (n > 1)
+ qsort((char *)off, (size_t)n, sizeof(double), refclock_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median of the samples until
+ * approximately 60 percent of the samples remain.
+ */
+ i = 0; j = n;
+ m = n - (n * 2) / NSTAGE;
+ while ((j - i) > m) {
+ offset = off[(j + i) / 2];
+ if (off[j - 1] - offset < offset - off[i])
+ i++; /* reject low end */
+ else
+ j--; /* reject high end */
+ }
+
+ /*
+ * Determine the offset and jitter.
+ */
+ offset = 0;
+ for (k = i; k < j; k++)
+ offset += off[k];
+ pp->offset = offset / m;
+ if (m > 1)
+ pp->jitter = SQUARE(off[i] - off[j - 1]);
+ else
+ pp->jitter = 0;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "refclock_sample: n %d offset %.6f disp %.6f jitter %.6f\n",
+ n, pp->offset, pp->disp, SQRT(pp->jitter));
+#endif
+ return (n);
+}
+
+
+/*
+ * refclock_receive - simulate the receive and packet procedures
+ *
+ * This routine simulates the NTP receive and packet procedures for a
+ * reference clock. This provides a mechanism in which the ordinary NTP
+ * filter, selection and combining algorithms can be used to suppress
+ * misbehaving radios and to mitigate between them when more than one is
+ * available for backup.
+ */
+void
+refclock_receive(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_receive: at %lu %s\n",
+ current_time, stoa(&peer->srcadr));
+#endif
+
+ /*
+ * Do a little sanity dance and update the peer structure. Groom
+ * the median filter samples and give the data to the clock
+ * filter.
+ */
+ peer->received++;
+ pp = peer->procptr;
+ peer->processed++;
+ peer->timereceived = current_time;
+ peer->leap = pp->leap;
+ if (peer->leap == LEAP_NOTINSYNC) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ if (!peer->reach)
+ report_event(EVNT_REACH, peer);
+ peer->reach |= 1;
+ peer->reftime = pp->lastref;
+ peer->org = pp->lastrec;
+ peer->rootdispersion = pp->disp;
+ get_systime(&peer->rec);
+ if (!refclock_sample(pp))
+ return;
+ clock_filter(peer, pp->offset, 0., pp->jitter);
+ clock_select();
+ record_peer_stats(&peer->srcadr, ctlpeerstatus(peer),
+ peer->offset, peer->delay, clock_phi * (current_time -
+ peer->epoch), SQRT(peer->jitter));
+ if (cal_enable && last_offset < MINDISPERSE) {
+#ifdef KERNEL_PLL
+ if (peer != sys_peer || pll_status & STA_PPSTIME)
+#else
+ if (peer != sys_peer)
+#endif /* KERNEL_PLL */
+ pp->fudgetime1 -= pp->offset * FUDGEFAC;
+ else
+ pp->fudgetime1 -= pp->fudgetime1 * FUDGEFAC;
+ }
+}
+
+/*
+ * refclock_gtlin - groom next input line and extract timestamp
+ *
+ * This routine processes the timecode received from the clock and
+ * removes the parity bit and control characters. If a timestamp is
+ * present in the timecode, as produced by the tty_clk STREAMS module,
+ * it returns that as the timestamp; otherwise, it returns the buffer
+ * timestamp. The routine return code is the number of characters in
+ * the line.
+ */
+int
+refclock_gtlin(
+ struct recvbuf *rbufp, /* receive buffer pointer */
+ char *lineptr, /* current line pointer */
+ int bmax, /* remaining characters in line */
+ l_fp *tsptr /* pointer to timestamp returned */
+ )
+{
+ char *dpt, *dpend, *dp;
+ int i;
+ l_fp trtmp, tstmp;
+ char c;
+
+ /*
+ * Check for the presence of a timestamp left by the tty_clock
+ * module and, if present, use that instead of the buffer
+ * timestamp captured by the I/O routines. We recognize a
+ * timestamp by noting its value is earlier than the buffer
+ * timestamp, but not more than one second earlier.
+ */
+ dpt = (char *)rbufp->recv_buffer;
+ dpend = dpt + rbufp->recv_length;
+ trtmp = rbufp->recv_time;
+
+ if (dpend >= dpt + 8) {
+ if (buftvtots(dpend - 8, &tstmp)) {
+ L_SUB(&trtmp, &tstmp);
+ if (trtmp.l_ui == 0) {
+#ifdef DEBUG
+ if (debug > 1) {
+ printf(
+ "refclock_gtlin: fd %d ldisc %s",
+ rbufp->fd, lfptoa(&trtmp, 6));
+ get_systime(&trtmp);
+ L_SUB(&trtmp, &tstmp);
+ printf(" sigio %s\n", lfptoa(&trtmp, 6));
+ }
+#endif
+ dpend -= 8;
+ trtmp = tstmp;
+ } else
+ trtmp = rbufp->recv_time;
+ }
+ }
+
+ /*
+ * Edit timecode to remove control chars. Don't monkey with the
+ * line buffer if the input buffer contains no ASCII printing
+ * characters.
+ */
+ if (dpend - dpt > bmax - 1)
+ dpend = dpt + bmax - 1;
+ for (dp = lineptr; dpt < dpend; dpt++) {
+ c = (char) (*dpt & 0x7f);
+ if (c >= ' ')
+ *dp++ = c;
+ }
+ i = dp - lineptr;
+ if (i > 0)
+ *dp = '\0';
+#ifdef DEBUG
+ if (debug > 1) {
+ if (i > 0)
+ printf("refclock_gtlin: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr);
+ else
+ printf("refclock_gtlin: fd %d time %s\n",
+ rbufp->fd, ulfptoa(&trtmp, 6));
+ }
+#endif
+ *tsptr = trtmp;
+ return (i);
+}
+
+/*
+ * The following code does not apply to WINNT & VMS ...
+ */
+#if !defined SYS_VXWORKS && !defined SYS_WINNT
+#if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)
+
+/*
+ * refclock_open - open serial port for reference clock
+ *
+ * This routine opens a serial port for I/O and sets default options. It
+ * returns the file descriptor if success and zero if failure.
+ */
+int
+refclock_open(
+ char *dev, /* device name pointer */
+ int speed, /* serial port speed (code) */
+ int lflags /* line discipline flags */
+ )
+{
+ int fd, i;
+ int flags;
+ TTY ttyb, *ttyp;
+#ifdef TIOCMGET
+ u_long ltemp;
+#endif /* TIOCMGET */
+ int omode;
+
+ /*
+ * Open serial port and set default options
+ */
+ flags = lflags;
+
+ omode = O_RDWR;
+#ifdef O_NONBLOCK
+ omode |= O_NONBLOCK;
+#endif
+#ifdef O_NOCTTY
+ omode |= O_NOCTTY;
+#endif
+
+ fd = open(dev, omode, 0777);
+
+ if (fd < 0) {
+ msyslog(LOG_ERR, "refclock_open: %s: %m", dev);
+ return (0);
+ }
+
+ /*
+ * This little jewel lights up the PPS file descriptor if the
+ * device name matches the name in the pps line in the
+ * configuration file. This is so the atom driver can glom onto
+ * the right device. Very silly.
+ */
+ if (strcmp(dev, pps_device) == 0)
+ fdpps = fd;
+
+ /*
+ * The following sections initialize the serial line port in
+ * canonical (line-oriented) mode and set the specified line
+ * speed, 8 bits and no parity. The modem control, break, erase
+ * and kill functions are normally disabled. There is a
+ * different section for each terminal interface, as selected at
+ * compile time.
+ */
+ ttyp = &ttyb;
+
+#ifdef HAVE_TERMIOS
+ /*
+ * POSIX serial line parameters (termios interface)
+ */
+ if (tcgetattr(fd, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_open: fd %d tcgetattr: %m", fd);
+ return (0);
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = CS8 | CLOCAL | CREAD;
+ (void)cfsetispeed(&ttyb, (u_int)speed);
+ (void)cfsetospeed(&ttyb, (u_int)speed);
+ ttyp->c_lflag = ICANON;
+ for (i = 0; i < NCCS; ++i)
+ {
+ ttyp->c_cc[i] = '\0';
+ }
+
+ /*
+ * Some special cases
+ */
+ if (flags & LDISC_RAW) {
+ ttyp->c_iflag = 0;
+ ttyp->c_lflag = 0;
+ ttyp->c_cc[VMIN] = 1;
+ }
+#if defined(TIOCMGET) && !defined(SCO5_CLOCK)
+ /*
+ * If we have modem control, check to see if modem leads are
+ * active; if so, set remote connection. This is necessary for
+ * the kernel pps mods to work.
+ */
+ ltemp = 0;
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_open: fd %d TIOCMGET failed: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_open: fd %d modem status 0x%lx\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
+ if (tcsetattr(fd, TCSANOW, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_open: fd %d TCSANOW failed: %m", fd);
+ return (0);
+ }
+ if (tcflush(fd, TCIOFLUSH) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_open: fd %d TCIOFLUSH failed: %m", fd);
+ return (0);
+ }
+#endif /* HAVE_TERMIOS */
+
+#ifdef HAVE_SYSV_TTYS
+
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ if (ioctl(fd, TCGETA, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_open: fd %d TCGETA failed: %m", fd);
+ return (0);
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+
+ /*
+ * Some special cases
+ */
+ if (flags & LDISC_RAW) {
+ ttyp->c_iflag = 0;
+ ttyp->c_lflag = 0;
+ }
+#ifdef TIOCMGET
+ /*
+ * If we have modem control, check to see if modem leads are
+ * active; if so, set remote connection. This is necessary for
+ * the kernel pps mods to work.
+ */
+ ltemp = 0;
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ msyslog(LOG_ERR,
+ "refclock_open: fd %d TIOCMGET failed: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_open: fd %d modem status %lx\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* TIOCMGET */
+ if (ioctl(fd, TCSETA, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_open: fd %d TCSETA failed: %m", fd);
+ return (0);
+ }
+#endif /* HAVE_SYSV_TTYS */
+
+#ifdef HAVE_BSD_TTYS
+
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ */
+ if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_open: fd %d TIOCGETP %m", fd);
+ return (0);
+ }
+ ttyp->sg_ispeed = ttyp->sg_ospeed = speed;
+ ttyp->sg_flags = EVENP | ODDP | CRMOD;
+ if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_open: TIOCSETP failed: %m");
+ return (0);
+ }
+#endif /* HAVE_BSD_TTYS */
+ if (!refclock_ioctl(fd, flags)) {
+ (void)close(fd);
+ msyslog(LOG_ERR,
+ "refclock_open: fd %d ioctl failed: %m", fd);
+ return (0);
+ }
+ return (fd);
+}
+#endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */
+#endif /* SYS_VXWORKS SYS_WINNT */
+
+/*
+ * refclock_ioctl - set serial port control functions
+ *
+ * This routine attempts to hide the internal, system-specific details
+ * of serial ports. It can handle POSIX (termios), SYSV (termio) and BSD
+ * (sgtty) interfaces with varying degrees of success. The routine sets
+ * up optional features such as tty_clk. The routine returns 1 if
+ * success and 0 if failure.
+ */
+int
+refclock_ioctl(
+ int fd, /* file descriptor */
+ int flags /* line discipline flags */
+ )
+{
+ /* simply return 1 if no UNIX line discipline is supported */
+#if !defined SYS_VXWORKS && !defined SYS_WINNT
+#if defined(HAVE_TERMIOS) || defined(HAVE_SYSV_TTYS) || defined(HAVE_BSD_TTYS)
+
+#ifdef TTYCLK
+ TTY ttyb, *ttyp;
+#endif /* TTYCLK */
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_ioctl: fd %d flags 0x%x\n", fd, flags);
+#endif
+ if (flags == 0)
+ return (1);
+#if !(defined(HAVE_TERMIOS) || defined(HAVE_BSD_TTYS))
+ if (flags & (LDISC_CLK | LDISC_PPS | LDISC_ACTS)) {
+ msyslog(LOG_ERR,
+ "refclock_ioctl: unsupported terminal interface");
+ return (0);
+ }
+#endif /* HAVE_TERMIOS HAVE_BSD_TTYS */
+#ifdef TTYCLK
+ ttyp = &ttyb;
+#endif /* TTYCLK */
+
+ /*
+ * The following features may or may not require System V
+ * STREAMS support, depending on the particular implementation.
+ */
+#if defined(TTYCLK)
+ /*
+ * The TTYCLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module and System V STREAMS
+ * support. If not available, don't complain.
+ */
+ if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
+ int rval = 0;
+
+ if (ioctl(fd, I_PUSH, "clk") < 0) {
+ msyslog(LOG_NOTICE,
+ "refclock_ioctl: I_PUSH clk failed: %m");
+ } else {
+ char *str;
+
+ if (flags & LDISC_CLKPPS)
+ str = "\377";
+ else if (flags & LDISC_ACTS)
+ str = "*";
+ else
+ str = "\n";
+#ifdef CLK_SETSTR
+ if ((rval = ioctl(fd, CLK_SETSTR, str)) < 0)
+ msyslog(LOG_ERR,
+ "refclock_ioctl: CLK_SETSTR failed: %m");
+ if (debug)
+ printf("refclock_ioctl: fd %d CLK_SETSTR %d str %s\n",
+ fd, rval, str);
+#endif
+ }
+ }
+#endif /* TTYCLK */
+#endif /* HAVE_TERMIOS || HAVE_SYSV_TTYS || HAVE_BSD_TTYS */
+#endif /* SYS_VXWORKS SYS_WINNT */
+ return (1);
+}
+
+/*
+ * refclock_control - set and/or return clock values
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * ntpdc and the clockstat command. It can also be used to initialize
+ * configuration variables, such as fudgetimes, fudgevalues, reference
+ * ID and stratum.
+ */
+void
+refclock_control(
+ struct sockaddr_storage *srcadr,
+ struct refclockstat *in,
+ struct refclockstat *out
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid address and running peer
+ */
+ if (srcadr->ss_family != AF_INET)
+ return;
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+ clktype = (u_char)REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit >= MAXUNIT)
+ return;
+ peer = typeunit[clktype][unit];
+ if (peer == NULL)
+ return;
+ if (peer->procptr == NULL)
+ return;
+ pp = peer->procptr;
+
+ /*
+ * Initialize requested data
+ */
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ pp->fudgetime1 = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ pp->fudgetime2 = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ pp->stratum = (u_char) in->fudgeval1;
+ if (in->haveflags & CLK_HAVEVAL2)
+ pp->refid = in->fudgeval2;
+ peer->stratum = pp->stratum;
+ if (peer->stratum == STRATUM_REFCLOCK || peer->stratum ==
+ STRATUM_UNSPEC)
+ peer->refid = pp->refid;
+ else
+ peer->refid = ((struct
+ sockaddr_in*)&peer->srcadr)->sin_addr.s_addr;
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG1;
+ }
+ if (in->haveflags & CLK_HAVEFLAG2) {
+ pp->sloppyclockflag &= ~CLK_FLAG2;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG2;
+ }
+ if (in->haveflags & CLK_HAVEFLAG3) {
+ pp->sloppyclockflag &= ~CLK_FLAG3;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG3;
+ }
+ if (in->haveflags & CLK_HAVEFLAG4) {
+ pp->sloppyclockflag &= ~CLK_FLAG4;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG4;
+ }
+ }
+
+ /*
+ * Readback requested data
+ */
+ if (out != 0) {
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 |
+ CLK_HAVEVAL2 | CLK_HAVEFLAG4;
+ out->fudgetime1 = pp->fudgetime1;
+ out->fudgetime2 = pp->fudgetime2;
+ out->fudgeval1 = pp->stratum;
+ out->fudgeval2 = pp->refid;
+ out->flags = (u_char) pp->sloppyclockflag;
+
+ out->timereset = current_time - pp->timestarted;
+ out->polls = pp->polls;
+ out->noresponse = pp->noreply;
+ out->badformat = pp->badformat;
+ out->baddata = pp->baddata;
+
+ out->lastevent = pp->lastevent;
+ out->currentstatus = pp->currentstatus;
+ out->type = pp->type;
+ out->clockdesc = pp->clockdesc;
+ out->lencode = pp->lencode;
+ out->p_lastcode = pp->a_lastcode;
+ }
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_control != noentry)
+ (refclock_conf[clktype]->clock_control)(unit, in, out, peer);
+}
+
+
+/*
+ * refclock_buginfo - return debugging info
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * ntpdc and the clkbug command.
+ */
+void
+refclock_buginfo(
+ struct sockaddr_storage *srcadr, /* clock address */
+ struct refclockbug *bug /* output structure */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+ int i;
+
+ /*
+ * Check for valid address and peer structure
+ */
+ if (srcadr->ss_family != AF_INET)
+ return;
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+ clktype = (u_char) REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit >= MAXUNIT)
+ return;
+ peer = typeunit[clktype][unit];
+ if (peer == NULL)
+ return;
+ pp = peer->procptr;
+
+ /*
+ * Copy structure values
+ */
+ bug->nvalues = 8;
+ bug->svalues = 0x0000003f;
+ bug->values[0] = pp->year;
+ bug->values[1] = pp->day;
+ bug->values[2] = pp->hour;
+ bug->values[3] = pp->minute;
+ bug->values[4] = pp->second;
+ bug->values[5] = pp->nsec;
+ bug->values[6] = pp->yearstart;
+ bug->values[7] = pp->coderecv;
+ bug->stimes = 0xfffffffc;
+ bug->times[0] = pp->lastref;
+ bug->times[1] = pp->lastrec;
+ for (i = 2; i < (int)bug->ntimes; i++)
+ DTOLFP(pp->filter[i - 2], &bug->times[i]);
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_buginfo != noentry)
+ (refclock_conf[clktype]->clock_buginfo)(unit, bug, peer);
+}
+
+#endif /* REFCLOCK */
diff --git a/ntpd/ntp_request.c b/ntpd/ntp_request.c
new file mode 100644
index 0000000..eacba28
--- /dev/null
+++ b/ntpd/ntp_request.c
@@ -0,0 +1,2756 @@
+/*
+ * ntp_request.c - respond to information requests
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_request.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "recvbuff.h"
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif /* KERNEL_PLL */
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+/*
+ * Because we now have v6 addresses in the messages, we need to compensate
+ * for the larger size. Therefore, we introduce the alternate size to
+ * keep us friendly with older implementations. A little ugly.
+ */
+static int client_v6_capable = 0; /* the client can handle longer messages */
+
+#define v6sizeof(type) (client_v6_capable ? sizeof(type) : v4sizeof(type))
+
+struct req_proc {
+ short request_code; /* defined request code */
+ short needs_auth; /* true when authentication needed */
+ short sizeofitem; /* size of request data item (older size)*/
+ short v6_sizeofitem; /* size of request data item (new size)*/
+ void (*handler) P((struct sockaddr_storage *, struct interface *,
+ struct req_pkt *)); /* routine to handle request */
+};
+
+/*
+ * Universal request codes
+ */
+static struct req_proc univ_codes[] = {
+ { NO_REQUEST, NOAUTH, 0, 0 }
+};
+
+static void req_ack P((struct sockaddr_storage *, struct interface *, struct req_pkt *, int));
+static char * prepare_pkt P((struct sockaddr_storage *, struct interface *, struct req_pkt *, u_int));
+static char * more_pkt P((void));
+static void flush_pkt P((void));
+static void peer_list P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void peer_list_sum P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void peer_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void peer_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void sys_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void sys_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void mem_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void io_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void timer_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void loop_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_conf P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_unconf P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void set_sys_flag P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void clr_sys_flag P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void setclr_flags P((struct sockaddr_storage *, struct interface *, struct req_pkt *, u_long));
+static void list_restrict P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_resaddflags P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_ressubflags P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_unrestrict P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_restrict P((struct sockaddr_storage *, struct interface *, struct req_pkt *, int));
+static void mon_getlist_0 P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void mon_getlist_1 P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void reset_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void reset_peer P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_key_reread P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void trust_key P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void untrust_key P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_trustkey P((struct sockaddr_storage *, struct interface *, struct req_pkt *, u_long));
+static void get_auth_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void reset_auth_stats P((void));
+static void req_get_traps P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void req_set_trap P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void req_clr_trap P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void do_setclr_trap P((struct sockaddr_storage *, struct interface *, struct req_pkt *, int));
+static void set_request_keyid P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void set_control_keyid P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void get_ctl_stats P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+#ifdef KERNEL_PLL
+static void get_kernel_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+#endif /* KERNEL_PLL */
+#ifdef REFCLOCK
+static void get_clock_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+static void set_clock_fudge P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+#ifdef REFCLOCK
+static void get_clkbug_info P((struct sockaddr_storage *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+
+/*
+ * ntpd request codes
+ */
+static struct req_proc ntp_codes[] = {
+ { REQ_PEER_LIST, NOAUTH, 0, 0, peer_list },
+ { REQ_PEER_LIST_SUM, NOAUTH, 0, 0, peer_list_sum },
+ { REQ_PEER_INFO, NOAUTH, v4sizeof(struct info_peer_list),
+ sizeof(struct info_peer_list), peer_info},
+ { REQ_PEER_STATS, NOAUTH, v4sizeof(struct info_peer_list),
+ sizeof(struct info_peer_list), peer_stats},
+ { REQ_SYS_INFO, NOAUTH, 0, 0, sys_info },
+ { REQ_SYS_STATS, NOAUTH, 0, 0, sys_stats },
+ { REQ_IO_STATS, NOAUTH, 0, 0, io_stats },
+ { REQ_MEM_STATS, NOAUTH, 0, 0, mem_stats },
+ { REQ_LOOP_INFO, NOAUTH, 0, 0, loop_info },
+ { REQ_TIMER_STATS, NOAUTH, 0, 0, timer_stats },
+ { REQ_CONFIG, AUTH, v4sizeof(struct conf_peer),
+ sizeof(struct conf_peer), do_conf },
+ { REQ_UNCONFIG, AUTH, v4sizeof(struct conf_unpeer),
+ sizeof(struct conf_unpeer), do_unconf },
+ { REQ_SET_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags),
+ sizeof(struct conf_sys_flags), set_sys_flag },
+ { REQ_CLR_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags),
+ sizeof(struct conf_sys_flags), clr_sys_flag },
+ { REQ_GET_RESTRICT, NOAUTH, 0, 0, list_restrict },
+ { REQ_RESADDFLAGS, AUTH, v4sizeof(struct conf_restrict),
+ sizeof(struct conf_restrict), do_resaddflags },
+ { REQ_RESSUBFLAGS, AUTH, v4sizeof(struct conf_restrict),
+ sizeof(struct conf_restrict), do_ressubflags },
+ { REQ_UNRESTRICT, AUTH, v4sizeof(struct conf_restrict),
+ sizeof(struct conf_restrict), do_unrestrict },
+ { REQ_MON_GETLIST, NOAUTH, 0, 0, mon_getlist_0 },
+ { REQ_MON_GETLIST_1, NOAUTH, 0, 0, mon_getlist_1 },
+ { REQ_RESET_STATS, AUTH, sizeof(struct reset_flags), 0, reset_stats },
+ { REQ_RESET_PEER, AUTH, v4sizeof(struct conf_unpeer),
+ sizeof(struct conf_unpeer), reset_peer },
+ { REQ_REREAD_KEYS, AUTH, 0, 0, do_key_reread },
+ { REQ_TRUSTKEY, AUTH, sizeof(u_long), sizeof(u_long), trust_key },
+ { REQ_UNTRUSTKEY, AUTH, sizeof(u_long), sizeof(u_long), untrust_key },
+ { REQ_AUTHINFO, NOAUTH, 0, 0, get_auth_info },
+ { REQ_TRAPS, NOAUTH, 0, 0, req_get_traps },
+ { REQ_ADD_TRAP, AUTH, v4sizeof(struct conf_trap),
+ sizeof(struct conf_trap), req_set_trap },
+ { REQ_CLR_TRAP, AUTH, v4sizeof(struct conf_trap),
+ sizeof(struct conf_trap), req_clr_trap },
+ { REQ_REQUEST_KEY, AUTH, sizeof(u_long), sizeof(u_long),
+ set_request_keyid },
+ { REQ_CONTROL_KEY, AUTH, sizeof(u_long), sizeof(u_long),
+ set_control_keyid },
+ { REQ_GET_CTLSTATS, NOAUTH, 0, 0, get_ctl_stats },
+#ifdef KERNEL_PLL
+ { REQ_GET_KERNEL, NOAUTH, 0, 0, get_kernel_info },
+#endif
+#ifdef REFCLOCK
+ { REQ_GET_CLOCKINFO, NOAUTH, sizeof(u_int32), sizeof(u_int32),
+ get_clock_info },
+ { REQ_SET_CLKFUDGE, AUTH, sizeof(struct conf_fudge),
+ sizeof(struct conf_fudge), set_clock_fudge },
+ { REQ_GET_CLKBUGINFO, NOAUTH, sizeof(u_int32), sizeof(u_int32),
+ get_clkbug_info },
+#endif
+ { NO_REQUEST, NOAUTH, 0, 0, 0 }
+};
+
+
+/*
+ * Authentication keyid used to authenticate requests. Zero means we
+ * don't allow writing anything.
+ */
+keyid_t info_auth_keyid;
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long numrequests; /* number of requests we've received */
+u_long numresppkts; /* number of resp packets sent with data */
+
+u_long errorcounter[INFO_ERR_AUTH+1]; /* lazy way to count errors, indexed */
+/* by the error code */
+
+/*
+ * A hack. To keep the authentication module clear of ntp-ism's, we
+ * include a time reset variable for its stats here.
+ */
+static u_long auth_timereset;
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct resp_pkt rpkt;
+static int reqver;
+static int seqno;
+static int nitems;
+static int itemsize;
+static int databytes;
+static char exbuf[RESP_DATA_SIZE];
+static int usingexbuf;
+static struct sockaddr_storage *toaddr;
+static struct interface *frominter;
+
+/*
+ * init_request - initialize request data
+ */
+void
+init_request (void)
+{
+ int i;
+
+ numrequests = 0;
+ numresppkts = 0;
+ auth_timereset = 0;
+ info_auth_keyid = 0; /* by default, can't do this */
+
+ for (i = 0; i < sizeof(errorcounter)/sizeof(errorcounter[0]); i++)
+ errorcounter[i] = 0;
+}
+
+
+/*
+ * req_ack - acknowledge request with no data
+ */
+static void
+req_ack(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ int errcode
+ )
+{
+ /*
+ * fill in the fields
+ */
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0, reqver);
+ rpkt.auth_seq = AUTH_SEQ(0, 0);
+ rpkt.implementation = inpkt->implementation;
+ rpkt.request = inpkt->request;
+ rpkt.err_nitems = ERR_NITEMS(errcode, 0);
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(0);
+
+ /*
+ * send packet and bump counters
+ */
+ sendpkt(srcadr, inter, -1, (struct pkt *)&rpkt, RESP_HEADER_SIZE);
+ errorcounter[errcode]++;
+}
+
+
+/*
+ * prepare_pkt - prepare response packet for transmission, return pointer
+ * to storage for data item.
+ */
+static char *
+prepare_pkt(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *pkt,
+ u_int structsize
+ )
+{
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: preparing pkt\n");
+#endif
+
+ /*
+ * Fill in the implementation, request and itemsize fields
+ * since these won't change.
+ */
+ rpkt.implementation = pkt->implementation;
+ rpkt.request = pkt->request;
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(structsize);
+
+ /*
+ * Compute the static data needed to carry on.
+ */
+ toaddr = srcadr;
+ frominter = inter;
+ seqno = 0;
+ nitems = 0;
+ itemsize = structsize;
+ databytes = 0;
+ usingexbuf = 0;
+
+ /*
+ * return the beginning of the packet buffer.
+ */
+ return &rpkt.data[0];
+}
+
+
+/*
+ * more_pkt - return a data pointer for a new item.
+ */
+static char *
+more_pkt(void)
+{
+ /*
+ * If we were using the extra buffer, send the packet.
+ */
+ if (usingexbuf) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("request: sending pkt\n");
+#endif
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, MORE_BIT, reqver);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE+databytes);
+ numresppkts++;
+
+ /*
+ * Copy data out of exbuf into the packet.
+ */
+ memmove(&rpkt.data[0], exbuf, (unsigned)itemsize);
+ seqno++;
+ databytes = 0;
+ nitems = 0;
+ usingexbuf = 0;
+ }
+
+ databytes += itemsize;
+ nitems++;
+ if (databytes + itemsize <= RESP_DATA_SIZE) {
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: giving him more data\n");
+#endif
+ /*
+ * More room in packet. Give him the
+ * next address.
+ */
+ return &rpkt.data[databytes];
+ } else {
+ /*
+ * No room in packet. Give him the extra
+ * buffer unless this was the last in the sequence.
+ */
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: into extra buffer\n");
+#endif
+ if (seqno == MAXSEQ)
+ return (char *)0;
+ else {
+ usingexbuf = 1;
+ return exbuf;
+ }
+ }
+}
+
+
+/*
+ * flush_pkt - we're done, return remaining information.
+ */
+static void
+flush_pkt(void)
+{
+#ifdef DEBUG
+ if (debug > 2)
+ printf("request: flushing packet, %d items\n", nitems);
+#endif
+ /*
+ * Must send the last packet. If nothing in here and nothing
+ * has been sent, send an error saying no data to be found.
+ */
+ if (seqno == 0 && nitems == 0)
+ req_ack(toaddr, frominter, (struct req_pkt *)&rpkt,
+ INFO_ERR_NODATA);
+ else {
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0, reqver);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE+databytes);
+ numresppkts++;
+ }
+}
+
+
+
+/*
+ * process_private - process private mode (7) packets
+ */
+void
+process_private(
+ struct recvbuf *rbufp,
+ int mod_okay
+ )
+{
+ struct req_pkt *inpkt;
+ struct req_pkt_tail *tailinpkt;
+ struct sockaddr_storage *srcadr;
+ struct interface *inter;
+ struct req_proc *proc;
+ int ec;
+ short temp_size;
+
+ /*
+ * Initialize pointers, for convenience
+ */
+ inpkt = (struct req_pkt *)&rbufp->recv_pkt;
+ srcadr = &rbufp->recv_srcadr;
+ inter = rbufp->dstadr;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("process_private: impl %d req %d\n",
+ inpkt->implementation, inpkt->request);
+#endif
+
+ /*
+ * Do some sanity checks on the packet. Return a format
+ * error if it fails.
+ */
+ ec = 0;
+ if ( (++ec, ISRESPONSE(inpkt->rm_vn_mode))
+ || (++ec, ISMORE(inpkt->rm_vn_mode))
+ || (++ec, INFO_VERSION(inpkt->rm_vn_mode) > NTP_VERSION)
+ || (++ec, INFO_VERSION(inpkt->rm_vn_mode) < NTP_OLDVERSION)
+ || (++ec, INFO_SEQ(inpkt->auth_seq) != 0)
+ || (++ec, INFO_ERR(inpkt->err_nitems) != 0)
+ || (++ec, INFO_MBZ(inpkt->mbz_itemsize) != 0)
+ || (++ec, rbufp->recv_length < REQ_LEN_HDR)
+ ) {
+ msyslog(LOG_ERR, "process_private: INFO_ERR_FMT: test %d failed, pkt from %s", ec, stoa(srcadr));
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ reqver = INFO_VERSION(inpkt->rm_vn_mode);
+
+ /*
+ * Get the appropriate procedure list to search.
+ */
+ if (inpkt->implementation == IMPL_UNIV)
+ proc = univ_codes;
+ else if ((inpkt->implementation == IMPL_XNTPD) ||
+ (inpkt->implementation == IMPL_XNTPD_OLD))
+ proc = ntp_codes;
+ else {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_IMPL);
+ return;
+ }
+
+ /*
+ * Search the list for the request codes. If it isn't one
+ * we know, return an error.
+ */
+ while (proc->request_code != NO_REQUEST) {
+ if (proc->request_code == (short) inpkt->request)
+ break;
+ proc++;
+ }
+ if (proc->request_code == NO_REQUEST) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_REQ);
+ return;
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("found request in tables\n");
+#endif
+
+ /*
+ * If we need data, check to see if we have some. If we
+ * don't, check to see that there is none (picky, picky).
+ */
+
+ /* This part is a bit tricky, we want to be sure that the size
+ * returned is either the old or the new size. We also can find
+ * out if the client can accept both types of messages this way.
+ *
+ * Handle the exception of REQ_CONFIG. It can have two data sizes.
+ */
+ temp_size = INFO_ITEMSIZE(inpkt->mbz_itemsize);
+ if ((temp_size != proc->sizeofitem &&
+ temp_size != proc->v6_sizeofitem) &&
+ !(inpkt->implementation == IMPL_XNTPD &&
+ inpkt->request == REQ_CONFIG &&
+ temp_size == sizeof(struct old_conf_peer))) {
+ if (debug > 2)
+ printf("process_private: wrong item size, received %d, should be %d or %d\n",
+ temp_size, proc->sizeofitem, proc->v6_sizeofitem);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if ((proc->sizeofitem != 0) &&
+ ((temp_size * INFO_NITEMS(inpkt->err_nitems)) >
+ (rbufp->recv_length - REQ_LEN_HDR))) {
+ if (debug > 2)
+ printf("process_private: not enough data\n");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ switch (inpkt->implementation) {
+ case IMPL_XNTPD:
+ client_v6_capable = 1;
+ break;
+ case IMPL_XNTPD_OLD:
+ client_v6_capable = 0;
+ break;
+ default:
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * If we need to authenticate, do so. Note that an
+ * authenticatable packet must include a mac field, must
+ * have used key info_auth_keyid and must have included
+ * a time stamp in the appropriate field. The time stamp
+ * must be within INFO_TS_MAXSKEW of the receive
+ * time stamp.
+ */
+ if (proc->needs_auth && sys_authenticate) {
+ l_fp ftmp;
+ double dtemp;
+
+ if (rbufp->recv_length < (int)((REQ_LEN_HDR +
+ (INFO_ITEMSIZE(inpkt->mbz_itemsize) *
+ INFO_NITEMS(inpkt->err_nitems))
+ + sizeof(struct req_pkt_tail)))) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ }
+ tailinpkt = (struct req_pkt_tail *)((char *)&rbufp->recv_pkt +
+ rbufp->recv_length - sizeof(struct req_pkt_tail));
+
+ /*
+ * If this guy is restricted from doing this, don't let him
+ * If wrong key was used, or packet doesn't have mac, return.
+ */
+ if (!INFO_IS_AUTH(inpkt->auth_seq) || info_auth_keyid == 0
+ || ntohl(tailinpkt->keyid) != info_auth_keyid) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("failed auth %d info_auth_keyid %lu pkt keyid %lu\n",
+ INFO_IS_AUTH(inpkt->auth_seq),
+ (u_long)info_auth_keyid,
+ (u_long)ntohl(tailinpkt->keyid));
+ msyslog(LOG_DEBUG,
+ "process_private: failed auth %d info_auth_keyid %lu pkt keyid %lu\n",
+ INFO_IS_AUTH(inpkt->auth_seq),
+ (u_long)info_auth_keyid,
+ (u_long)ntohl(tailinpkt->keyid));
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ if (rbufp->recv_length > REQ_LEN_MAC) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("bad pkt length %d\n",
+ rbufp->recv_length);
+#endif
+ msyslog(LOG_ERR, "process_private: bad pkt length %d",
+ rbufp->recv_length);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if (!mod_okay || !authhavekey(info_auth_keyid)) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("failed auth mod_okay %d\n", mod_okay);
+ msyslog(LOG_DEBUG,
+ "process_private: failed auth mod_okay %d\n",
+ mod_okay);
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * calculate absolute time difference between xmit time stamp
+ * and receive time stamp. If too large, too bad.
+ */
+ NTOHL_FP(&tailinpkt->tstamp, &ftmp);
+ L_SUB(&ftmp, &rbufp->recv_time);
+ LFPTOD(&ftmp, dtemp);
+ if (fabs(dtemp) >= INFO_TS_MAXSKEW) {
+ /*
+ * He's a loser. Tell him.
+ */
+#ifdef DEBUG
+ if (debug > 4)
+ printf("xmit/rcv timestamp delta > INFO_TS_MAXSKEW\n");
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * So far so good. See if decryption works out okay.
+ */
+ if (!authdecrypt(info_auth_keyid, (u_int32 *)inpkt,
+ rbufp->recv_length - sizeof(struct req_pkt_tail) +
+ REQ_LEN_HDR, sizeof(struct req_pkt_tail) - REQ_LEN_HDR)) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("authdecrypt failed\n");
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("process_private: all okay, into handler\n");
+#endif
+
+ /*
+ * Packet is okay. Call the handler to send him data.
+ */
+ (proc->handler)(srcadr, inter, inpkt);
+}
+
+
+/*
+ * peer_list - send a list of the peers
+ */
+static void
+peer_list(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_list *ip;
+ register struct peer *pp;
+ register int i;
+ register int skip = 0;
+
+ ip = (struct info_peer_list *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer_list));
+ for (i = 0; i < HASH_SIZE && ip != 0; i++) {
+ pp = peer_hash[i];
+ while (pp != 0 && ip != 0) {
+ if (pp->srcadr.ss_family == AF_INET6) {
+ if (client_v6_capable) {
+ ip->addr6 = GET_INADDR6(pp->srcadr);
+ ip->v6_flag = 1;
+ skip = 0;
+ } else {
+ skip = 1;
+ break;
+ }
+ } else {
+ ip->addr = GET_INADDR(pp->srcadr);
+ if (client_v6_capable)
+ ip->v6_flag = 0;
+ skip = 0;
+ }
+
+ if(!skip) {
+ ip->port = NSRCPORT(&pp->srcadr);
+ ip->hmode = pp->hmode;
+ ip->flags = 0;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip = (struct info_peer_list *)more_pkt();
+ }
+ pp = pp->next;
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_list_sum - return extended peer list
+ */
+static void
+peer_list_sum(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_summary *ips;
+ register struct peer *pp;
+ register int i;
+ l_fp ltmp;
+ register int skip;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants peer list summary\n");
+#endif
+ ips = (struct info_peer_summary *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer_summary));
+ for (i = 0; i < HASH_SIZE && ips != 0; i++) {
+ pp = peer_hash[i];
+ while (pp != 0 && ips != 0) {
+#ifdef DEBUG
+ if (debug > 3)
+ printf("sum: got one\n");
+#endif
+ /*
+ * Be careful here not to return v6 peers when we
+ * want only v4.
+ */
+ if (pp->srcadr.ss_family == AF_INET6) {
+ if (client_v6_capable) {
+ ips->srcadr6 = GET_INADDR6(pp->srcadr);
+ ips->v6_flag = 1;
+ ips->dstadr6 = GET_INADDR6(pp->dstadr->sin);
+ skip = 0;
+ } else {
+ skip = 1;
+ break;
+ }
+ } else {
+ ips->srcadr = GET_INADDR(pp->srcadr);
+ if (client_v6_capable)
+ ips->v6_flag = 0;
+/* XXX PDM This code is buggy. Need to replace with a straightforward assignment */
+ ips->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ GET_INADDR(pp->dstadr->bcast):
+ pp->cast_flags ?
+ GET_INADDR(pp->dstadr->sin) ?
+ GET_INADDR(pp->dstadr->sin):
+ GET_INADDR(pp->dstadr->bcast):
+ 1 : GET_INADDR(pp->dstadr->sin);
+
+ skip = 0;
+ }
+ if (!skip){
+ ips->srcport = NSRCPORT(&pp->srcadr);
+ ips->stratum = pp->stratum;
+ ips->hpoll = pp->hpoll;
+ ips->ppoll = pp->ppoll;
+ ips->reach = pp->reach;
+ ips->flags = 0;
+ if (pp == sys_peer)
+ ips->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ips->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ips->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ips->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ips->flags |= INFO_FLAG_PREFER;
+ if (pp->flags & FLAG_BURST)
+ ips->flags |= INFO_FLAG_BURST;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ips->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ips->flags |= INFO_FLAG_SHORTLIST;
+ ips->hmode = pp->hmode;
+ ips->delay = HTONS_FP(DTOFP(pp->delay));
+ DTOLFP(pp->offset, &ltmp);
+ HTONL_FP(&ltmp, &ips->offset);
+ ips->dispersion = HTONS_FP(DTOUFP(pp->disp));
+ }
+ pp = pp->next;
+ ips = (struct info_peer_summary *)more_pkt();
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_info - send information for one or more peers
+ */
+static void
+peer_info (
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_list *ipl;
+ register struct peer *pp;
+ register struct info_peer *ip;
+ register int items;
+ register int i, j;
+ struct sockaddr_storage addr;
+ extern struct peer *sys_peer;
+ l_fp ltmp;
+
+ memset((char *)&addr, 0, sizeof addr);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+
+ ip = (struct info_peer *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer));
+ while (items-- > 0 && ip != 0) {
+ memset((char *)&addr, 0, sizeof(addr));
+ NSRCPORT(&addr) = ipl->port;
+ if (client_v6_capable && ipl->v6_flag != 0) {
+ addr.ss_family = AF_INET6;
+ GET_INADDR6(addr) = ipl->addr6;
+ } else {
+ addr.ss_family = AF_INET;
+ GET_INADDR(addr) = ipl->addr;
+ }
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+ ipl++;
+ if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0)
+ continue;
+ if (pp->srcadr.ss_family == AF_INET6) {
+ ip->dstadr6 = pp->cast_flags == MDF_BCAST ?
+ GET_INADDR6(pp->dstadr->bcast) :
+ GET_INADDR6(pp->dstadr->sin);
+ ip->srcadr6 = GET_INADDR6(pp->srcadr);
+ ip->v6_flag = 1;
+ } else {
+/* XXX PDM This code is buggy. Need to replace with a straightforward assignment */
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ GET_INADDR(pp->dstadr->bcast):
+ pp->cast_flags ?
+ GET_INADDR(pp->dstadr->sin) ?
+ GET_INADDR(pp->dstadr->sin):
+ GET_INADDR(pp->dstadr->bcast):
+ 2 : GET_INADDR(pp->dstadr->sin);
+
+ ip->srcadr = GET_INADDR(pp->srcadr);
+ if (client_v6_capable)
+ ip->v6_flag = 0;
+ }
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ip->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->flags & FLAG_BURST)
+ ip->flags |= INFO_FLAG_BURST;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->leap = pp->leap;
+ ip->hmode = pp->hmode;
+ ip->keyid = pp->keyid;
+ ip->stratum = pp->stratum;
+ ip->ppoll = pp->ppoll;
+ ip->hpoll = pp->hpoll;
+ ip->precision = pp->precision;
+ ip->version = pp->version;
+ ip->reach = pp->reach;
+ ip->unreach = (u_char) pp->unreach;
+ ip->flash = (u_char)pp->flash;
+ ip->flash2 = (u_short) pp->flash;
+ ip->estbdelay = HTONS_FP(DTOFP(pp->estbdelay));
+ ip->ttl = pp->ttl;
+ ip->associd = htons(pp->associd);
+ ip->rootdelay = HTONS_FP(DTOUFP(pp->rootdelay));
+ ip->rootdispersion = HTONS_FP(DTOUFP(pp->rootdispersion));
+ ip->refid = pp->refid;
+ HTONL_FP(&pp->reftime, &ip->reftime);
+ HTONL_FP(&pp->org, &ip->org);
+ HTONL_FP(&pp->rec, &ip->rec);
+ HTONL_FP(&pp->xmt, &ip->xmt);
+ j = pp->filter_nextpt - 1;
+ for (i = 0; i < NTP_SHIFT; i++, j--) {
+ if (j < 0)
+ j = NTP_SHIFT-1;
+ ip->filtdelay[i] = HTONS_FP(DTOFP(pp->filter_delay[j]));
+ DTOLFP(pp->filter_offset[j], &ltmp);
+ HTONL_FP(&ltmp, &ip->filtoffset[i]);
+ ip->order[i] = (u_char)((pp->filter_nextpt+NTP_SHIFT-1)
+ - pp->filter_order[i]);
+ if (ip->order[i] >= NTP_SHIFT)
+ ip->order[i] -= NTP_SHIFT;
+ }
+ DTOLFP(pp->offset, &ltmp);
+ HTONL_FP(&ltmp, &ip->offset);
+ ip->delay = HTONS_FP(DTOFP(pp->delay));
+ ip->dispersion = HTONS_FP(DTOUFP(SQRT(pp->disp)));
+ ip->selectdisp = HTONS_FP(DTOUFP(SQRT(pp->jitter)));
+ ip = (struct info_peer *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_stats - send statistics for one or more peers
+ */
+static void
+peer_stats (
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_list *ipl;
+ register struct peer *pp;
+ register struct info_peer_stats *ip;
+ register int items;
+ struct sockaddr_storage addr;
+ extern struct peer *sys_peer;
+
+ printf("peer_stats: called\n");
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+ ip = (struct info_peer_stats *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_peer_stats));
+ while (items-- > 0 && ip != 0) {
+ memset((char *)&addr, 0, sizeof(addr));
+ NSRCPORT(&addr) = ipl->port;
+ if (client_v6_capable && ipl->v6_flag) {
+ addr.ss_family = AF_INET6;
+ GET_INADDR6(addr) = ipl->addr6;
+ } else {
+ addr.ss_family = AF_INET;
+ GET_INADDR(addr) = ipl->addr;
+ }
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+ printf("peer_stats: looking for %s, %d, %d\n", stoa(&addr),
+ ipl->port, ((struct sockaddr_in6 *)&addr)->sin6_port);
+ ipl = (struct info_peer_list *)((char *)ipl +
+ INFO_ITEMSIZE(inpkt->mbz_itemsize));
+
+ if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0)
+ continue;
+ printf("peer_stats: found %s\n", stoa(&addr));
+ if (pp->srcadr.ss_family == AF_INET) {
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ GET_INADDR(pp->dstadr->bcast):
+ pp->cast_flags ?
+ GET_INADDR(pp->dstadr->sin) ?
+ GET_INADDR(pp->dstadr->sin):
+ GET_INADDR(pp->dstadr->bcast):
+ 3 : 7;
+ ip->srcadr = GET_INADDR(pp->srcadr);
+ if (client_v6_capable)
+ ip->v6_flag = 0;
+ } else {
+ ip->dstadr6 = pp->cast_flags == MDF_BCAST ?
+ GET_INADDR6(pp->dstadr->bcast):
+ GET_INADDR6(pp->dstadr->sin);
+ ip->srcadr6 = GET_INADDR6(pp->srcadr);
+ ip->v6_flag = 1;
+ }
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ip->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->flags & FLAG_BURST)
+ ip->flags |= INFO_FLAG_BURST;
+ if (pp->status == CTL_PST_SEL_SYNCCAND)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->status >= CTL_PST_SEL_SYSPEER)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->timereceived = htonl((u_int32)(current_time - pp->timereceived));
+ ip->timetosend = htonl(pp->nextdate - current_time);
+ ip->timereachable = htonl((u_int32)(current_time - pp->timereachable));
+ ip->sent = htonl((u_int32)(pp->sent));
+ ip->processed = htonl((u_int32)(pp->processed));
+ ip->badauth = htonl((u_int32)(pp->badauth));
+ ip->bogusorg = htonl((u_int32)(pp->bogusorg));
+ ip->oldpkt = htonl((u_int32)(pp->oldpkt));
+ ip->seldisp = htonl((u_int32)(pp->seldisptoolarge));
+ ip->selbroken = htonl((u_int32)(pp->selbroken));
+ ip->candidate = pp->status;
+ ip = (struct info_peer_stats *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * sys_info - return system info
+ */
+static void
+sys_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_sys *is;
+
+ /*
+ * Importations from the protocol module
+ */
+ extern u_char sys_leap;
+ extern u_char sys_stratum;
+ extern s_char sys_precision;
+ extern double sys_rootdelay;
+ extern double sys_rootdispersion;
+ extern u_int32 sys_refid;
+ extern l_fp sys_reftime;
+ extern u_char sys_poll;
+ extern struct peer *sys_peer;
+ extern int sys_bclient;
+ extern double sys_bdelay;
+ extern l_fp sys_authdelay;
+ extern double clock_stability;
+ extern double sys_jitter;
+
+ is = (struct info_sys *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_sys));
+
+ if (sys_peer != 0) {
+ if (sys_peer->srcadr.ss_family == AF_INET) {
+ is->peer = GET_INADDR(sys_peer->srcadr);
+ if (client_v6_capable)
+ is->v6_flag = 0;
+ } else if (client_v6_capable) {
+ is->peer6 = GET_INADDR6(sys_peer->srcadr);
+ is->v6_flag = 1;
+ }
+ is->peer_mode = sys_peer->hmode;
+ } else {
+ is->peer = 0;
+ if (client_v6_capable) {
+ is->v6_flag = 0;
+ }
+ is->peer_mode = 0;
+ }
+
+ is->leap = sys_leap;
+ is->stratum = sys_stratum;
+ is->precision = sys_precision;
+ is->rootdelay = htonl(DTOFP(sys_rootdelay));
+ is->rootdispersion = htonl(DTOUFP(sys_rootdispersion));
+ is->frequency = htonl(DTOFP(sys_jitter));
+ is->stability = htonl(DTOUFP(clock_stability * 1e6));
+ is->refid = sys_refid;
+ HTONL_FP(&sys_reftime, &is->reftime);
+
+ is->poll = sys_poll;
+
+ is->flags = 0;
+ if (sys_authenticate)
+ is->flags |= INFO_FLAG_AUTHENTICATE;
+ if (sys_bclient)
+ is->flags |= INFO_FLAG_BCLIENT;
+#ifdef REFCLOCK
+ if (cal_enable)
+ is->flags |= INFO_FLAG_CAL;
+#endif /* REFCLOCK */
+ if (kern_enable)
+ is->flags |= INFO_FLAG_KERNEL;
+ if (mon_enabled != MON_OFF)
+ is->flags |= INFO_FLAG_MONITOR;
+ if (ntp_enable)
+ is->flags |= INFO_FLAG_NTP;
+ if (pps_enable)
+ is->flags |= INFO_FLAG_PPS_SYNC;
+ if (stats_control)
+ is->flags |= INFO_FLAG_FILEGEN;
+ is->bdelay = HTONS_FP(DTOFP(sys_bdelay));
+ HTONL_UF(sys_authdelay.l_f, &is->authdelay);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * sys_stats - return system statistics
+ */
+static void
+sys_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_sys_stats *ss;
+
+ /*
+ * Importations from the protocol module
+ */
+ ss = (struct info_sys_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_sys_stats));
+ ss->timeup = htonl((u_int32)current_time);
+ ss->timereset = htonl((u_int32)(current_time - sys_stattime));
+ ss->denied = htonl((u_int32)sys_restricted);
+ ss->oldversionpkt = htonl((u_int32)sys_oldversionpkt);
+ ss->newversionpkt = htonl((u_int32)sys_newversionpkt);
+ ss->unknownversion = htonl((u_int32)sys_unknownversion);
+ ss->badlength = htonl((u_int32)sys_badlength);
+ ss->processed = htonl((u_int32)sys_processed);
+ ss->badauth = htonl((u_int32)sys_badauth);
+ ss->limitrejected = htonl((u_int32)sys_limitrejected);
+ ss->received = htonl((u_int32)sys_received);
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * mem_stats - return memory statistics
+ */
+static void
+mem_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_mem_stats *ms;
+ register int i;
+
+ /*
+ * Importations from the peer module
+ */
+ extern int peer_hash_count[HASH_SIZE];
+ extern int peer_free_count;
+ extern u_long peer_timereset;
+ extern u_long findpeer_calls;
+ extern u_long peer_allocations;
+ extern u_long peer_demobilizations;
+ extern int total_peer_structs;
+
+ ms = (struct info_mem_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_mem_stats));
+
+ ms->timereset = htonl((u_int32)(current_time - peer_timereset));
+ ms->totalpeermem = htons((u_short)total_peer_structs);
+ ms->freepeermem = htons((u_short)peer_free_count);
+ ms->findpeer_calls = htonl((u_int32)findpeer_calls);
+ ms->allocations = htonl((u_int32)peer_allocations);
+ ms->demobilizations = htonl((u_int32)peer_demobilizations);
+
+ for (i = 0; i < HASH_SIZE; i++) {
+ if (peer_hash_count[i] > 255)
+ ms->hashcount[i] = 255;
+ else
+ ms->hashcount[i] = (u_char)peer_hash_count[i];
+ }
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * io_stats - return io statistics
+ */
+static void
+io_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_io_stats *io;
+
+ /*
+ * Importations from the io module
+ */
+ extern u_long io_timereset;
+
+ io = (struct info_io_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_io_stats));
+
+ io->timereset = htonl((u_int32)(current_time - io_timereset));
+ io->totalrecvbufs = htons((u_short) total_recvbuffs());
+ io->freerecvbufs = htons((u_short) free_recvbuffs());
+ io->fullrecvbufs = htons((u_short) full_recvbuffs());
+ io->lowwater = htons((u_short) lowater_additions());
+ io->dropped = htonl((u_int32)packets_dropped);
+ io->ignored = htonl((u_int32)packets_ignored);
+ io->received = htonl((u_int32)packets_received);
+ io->sent = htonl((u_int32)packets_sent);
+ io->notsent = htonl((u_int32)packets_notsent);
+ io->interrupts = htonl((u_int32)handler_calls);
+ io->int_received = htonl((u_int32)handler_pkts);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * timer_stats - return timer statistics
+ */
+static void
+timer_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_timer_stats *ts;
+
+ /*
+ * Importations from the timer module
+ */
+ extern u_long timer_timereset;
+ extern u_long timer_overflows;
+ extern u_long timer_xmtcalls;
+
+ ts = (struct info_timer_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_timer_stats));
+
+ ts->timereset = htonl((u_int32)(current_time - timer_timereset));
+ ts->alarms = htonl((u_int32)alarm_overflow);
+ ts->overflows = htonl((u_int32)timer_overflows);
+ ts->xmtcalls = htonl((u_int32)timer_xmtcalls);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * loop_info - return the current state of the loop filter
+ */
+static void
+loop_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_loop *li;
+ l_fp ltmp;
+
+ /*
+ * Importations from the loop filter module
+ */
+ extern double last_offset;
+ extern double drift_comp;
+ extern int tc_counter;
+ extern u_long last_time;
+
+ li = (struct info_loop *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_loop));
+
+ DTOLFP(last_offset, &ltmp);
+ HTONL_FP(&ltmp, &li->last_offset);
+ DTOLFP(drift_comp * 1e6, &ltmp);
+ HTONL_FP(&ltmp, &li->drift_comp);
+ li->compliance = htonl((u_int32)(tc_counter));
+ li->watchdog_timer = htonl((u_int32)(current_time - last_time));
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * do_conf - add a peer to the configuration list
+ */
+static void
+do_conf(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ int items;
+ u_int fl;
+ struct conf_peer *cp;
+ struct conf_peer temp_cp;
+ struct sockaddr_storage peeraddr;
+ struct sockaddr_in tmp_clock;
+
+ /*
+ * Do a check of everything to see that it looks
+ * okay. If not, complain about it. Note we are
+ * very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_peer *)inpkt->data;
+ memset(&temp_cp, 0, sizeof(struct conf_peer));
+ memcpy(&temp_cp, (char *)cp, INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ fl = 0;
+ while (items-- > 0 && !fl) {
+ if (((temp_cp.version) > NTP_VERSION)
+ || ((temp_cp.version) < NTP_OLDVERSION))
+ fl = 1;
+ if (temp_cp.hmode != MODE_ACTIVE
+ && temp_cp.hmode != MODE_CLIENT
+ && temp_cp.hmode != MODE_BROADCAST)
+ fl = 1;
+ if (temp_cp.flags & ~(CONF_FLAG_AUTHENABLE | CONF_FLAG_PREFER
+ | CONF_FLAG_BURST | CONF_FLAG_SKEY))
+ fl = 1;
+ cp = (struct conf_peer *)
+ ((char *)cp + INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ if (fl) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_peer *)inpkt->data;
+
+ while (items-- > 0) {
+ memset(&temp_cp, 0, sizeof(struct conf_peer));
+ memcpy(&temp_cp, (char *)cp, INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ memset((char *)&peeraddr, 0, sizeof(struct sockaddr_storage));
+
+ fl = 0;
+ if (temp_cp.flags & CONF_FLAG_AUTHENABLE)
+ fl |= FLAG_AUTHENABLE;
+ if (temp_cp.flags & CONF_FLAG_PREFER)
+ fl |= FLAG_PREFER;
+ if (temp_cp.flags & CONF_FLAG_BURST)
+ fl |= FLAG_BURST;
+ if (temp_cp.flags & CONF_FLAG_SKEY)
+ fl |= FLAG_SKEY;
+ if (client_v6_capable && temp_cp.v6_flag != 0) {
+ peeraddr.ss_family = AF_INET6;
+ GET_INADDR6(peeraddr) = temp_cp.peeraddr6;
+ } else {
+ peeraddr.ss_family = AF_INET;
+ GET_INADDR(peeraddr) = temp_cp.peeraddr;
+ /*
+ * Make sure the address is valid
+ */
+ tmp_clock = *CAST_V4(peeraddr);
+ if (
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&tmp_clock) &&
+#endif
+ ISBADADR(&tmp_clock)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ }
+ NSRCPORT(&peeraddr) = htons(NTP_PORT);
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+
+ /* XXX W2DO? minpoll/maxpoll arguments ??? */
+ if (peer_config(&peeraddr, (struct interface *)0,
+ temp_cp.hmode, temp_cp.version, temp_cp.minpoll,
+ temp_cp.maxpoll, fl, temp_cp.ttl, temp_cp.keyid,
+ NULL) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ cp = (struct conf_peer *)
+ ((char *)cp + INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+#if 0
+/* XXX */
+/*
+ * dns_a - Snarf DNS info for an association ID
+ */
+static void
+dns_a(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_dns_assoc *dp;
+ register int items;
+ struct sockaddr_in peeraddr;
+
+ /*
+ * Do a check of everything to see that it looks
+ * okay. If not, complain about it. Note we are
+ * very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ dp = (struct info_dns_assoc *)inpkt->data;
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ dp = (struct info_dns_assoc *)inpkt->data;
+ memset((char *)&peeraddr, 0, sizeof(struct sockaddr_in));
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ /*
+ * Make sure the address is valid
+ */
+ if (
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ ISBADADR(&peeraddr)) {
+#ifdef REFCLOCK
+ msyslog(LOG_ERR, "dns_a: !ISREFCLOCK && ISBADADR");
+#else
+ msyslog(LOG_ERR, "dns_a: ISBADADR");
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ while (items-- > 0) {
+ associd_t associd;
+ size_t hnl;
+ struct peer *peer;
+ int bogon = 0;
+
+ associd = dp->associd;
+ peer = findpeerbyassoc(associd);
+ if (peer == 0 || peer->flags & FLAG_REFCLOCK) {
+ msyslog(LOG_ERR, "dns_a: %s",
+ (peer == 0)
+ ? "peer == 0"
+ : "peer->flags & FLAG_REFCLOCK");
+ ++bogon;
+ }
+ peeraddr.sin_addr.s_addr = dp->peeraddr;
+ for (hnl = 0; dp->hostname[hnl] && hnl < sizeof dp->hostname; ++hnl) ;
+ if (hnl >= sizeof dp->hostname) {
+ msyslog(LOG_ERR, "dns_a: hnl (%ld) >= %ld",
+ (long)hnl, (long)sizeof dp->hostname);
+ ++bogon;
+ }
+
+ msyslog(LOG_INFO, "dns_a: <%s> for %s, AssocID %d, bogon %d",
+ dp->hostname,
+ stoa((struct sockaddr_storage *)&peeraddr), associd,
+ bogon);
+
+ if (bogon) {
+ /* If it didn't work */
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ } else {
+#if 0
+#ifdef PUBKEY
+ crypto_public(peer, dp->hostname);
+#endif /* PUBKEY */
+#endif
+ }
+
+ dp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+#endif /* 0 */
+
+/*
+ * do_unconf - remove a peer from the configuration list
+ */
+static void
+do_unconf(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_unpeer *cp;
+ struct conf_unpeer temp_cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_storage peeraddr;
+ int bad, found;
+
+ /*
+ * This is a bit unstructured, but I like to be careful.
+ * We check to see that every peer exists and is actually
+ * configured. If so, we remove them. If not, we return
+ * an error.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ memset(&temp_cp, 0, sizeof(temp_cp));
+ memset(&peeraddr, 0, sizeof(peeraddr));
+ memcpy(&temp_cp, cp, INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ if (client_v6_capable && temp_cp.v6_flag != 0) {
+ peeraddr.ss_family = AF_INET6;
+ GET_INADDR6(peeraddr) = temp_cp.peeraddr6;
+ } else {
+ peeraddr.ss_family = AF_INET;
+ GET_INADDR(peeraddr) = temp_cp.peeraddr;
+ }
+ NSRCPORT(&peeraddr) = htons(NTP_PORT);
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+ found = 0;
+ peer = (struct peer *)0;
+ printf("searching for %s\n", stoa(&peeraddr));
+ while (!found) {
+ peer = findexistingpeer(&peeraddr, peer, -1);
+ if (peer == (struct peer *)0)
+ break;
+ if (peer->flags & FLAG_CONFIG)
+ found = 1;
+ }
+ if (!found)
+ bad = 1;
+ cp = (struct conf_unpeer *)
+ ((char *)cp + INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+ while (items-- > 0) {
+ memset(&temp_cp, 0, sizeof(temp_cp));
+ memset(&peeraddr, 0, sizeof(peeraddr));
+ memcpy(&temp_cp, cp, INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ if (client_v6_capable && temp_cp.v6_flag != 0) {
+ peeraddr.ss_family = AF_INET6;
+ GET_INADDR6(peeraddr) = temp_cp.peeraddr6;
+ } else {
+ peeraddr.ss_family = AF_INET;
+ GET_INADDR(peeraddr) = temp_cp.peeraddr;
+ }
+ NSRCPORT(&peeraddr) = htons(NTP_PORT);
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+ peer_unconfig(&peeraddr, (struct interface *)0, -1);
+ cp = (struct conf_unpeer *)
+ ((char *)cp + INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * set_sys_flag - set system flags
+ */
+static void
+set_sys_flag(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ setclr_flags(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * clr_sys_flag - clear system flags
+ */
+static void
+clr_sys_flag(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ setclr_flags(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * setclr_flags - do the grunge work of flag setting/clearing
+ */
+static void
+setclr_flags(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ u_long set
+ )
+{
+ register u_int flags;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "setclr_flags: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ flags = ((struct conf_sys_flags *)inpkt->data)->flags;
+
+ if (flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_PPS |
+ SYS_FLAG_NTP | SYS_FLAG_KERNEL | SYS_FLAG_MONITOR |
+ SYS_FLAG_FILEGEN | SYS_FLAG_AUTH | SYS_FLAG_CAL)) {
+ msyslog(LOG_ERR, "setclr_flags: extra flags: %#x",
+ flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_PPS |
+ SYS_FLAG_NTP | SYS_FLAG_KERNEL |
+ SYS_FLAG_MONITOR | SYS_FLAG_FILEGEN |
+ SYS_FLAG_AUTH | SYS_FLAG_CAL));
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ if (flags & SYS_FLAG_BCLIENT)
+ proto_config(PROTO_BROADCLIENT, set, 0., NULL);
+ if (flags & SYS_FLAG_PPS)
+ proto_config(PROTO_PPS, set, 0., NULL);
+ if (flags & SYS_FLAG_NTP)
+ proto_config(PROTO_NTP, set, 0., NULL);
+ if (flags & SYS_FLAG_KERNEL)
+ proto_config(PROTO_KERNEL, set, 0., NULL);
+ if (flags & SYS_FLAG_MONITOR)
+ proto_config(PROTO_MONITOR, set, 0., NULL);
+ if (flags & SYS_FLAG_FILEGEN)
+ proto_config(PROTO_FILEGEN, set, 0., NULL);
+ if (flags & SYS_FLAG_AUTH)
+ proto_config(PROTO_AUTHENTICATE, set, 0., NULL);
+ if (flags & SYS_FLAG_CAL)
+ proto_config(PROTO_CAL, set, 0., NULL);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * list_restrict - return the restrict list
+ */
+static void
+list_restrict(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_restrict *ir;
+ register struct restrictlist *rl;
+ register struct restrictlist6 *rl6;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants restrict list summary\n");
+#endif
+
+ ir = (struct info_restrict *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_restrict));
+
+ for (rl = restrictlist; rl != 0 && ir != 0; rl = rl->next) {
+ ir->addr = htonl(rl->addr);
+ if (client_v6_capable)
+ ir->v6_flag = 0;
+ ir->mask = htonl(rl->mask);
+ ir->count = htonl((u_int32)rl->count);
+ ir->flags = htons(rl->flags);
+ ir->mflags = htons(rl->mflags);
+ ir = (struct info_restrict *)more_pkt();
+ }
+ if (client_v6_capable)
+ for (rl6 = restrictlist6; rl6 != 0 && ir != 0; rl6 = rl6->next) {
+ ir->addr6 = rl6->addr6;
+ ir->mask6 = rl6->mask6;
+ ir->v6_flag = 1;
+ ir->count = htonl((u_int32)rl6->count);
+ ir->flags = htons(rl6->flags);
+ ir->mflags = htons(rl6->mflags);
+ ir = (struct info_restrict *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * do_resaddflags - add flags to a restrict entry (or create one)
+ */
+static void
+do_resaddflags(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_FLAGS);
+}
+
+
+
+/*
+ * do_ressubflags - remove flags from a restrict entry
+ */
+static void
+do_ressubflags(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_UNFLAG);
+}
+
+
+/*
+ * do_unrestrict - remove a restrict entry from the list
+ */
+static void
+do_unrestrict(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_REMOVE);
+}
+
+
+
+
+
+/*
+ * do_restrict - do the dirty stuff of dealing with restrictions
+ */
+static void
+do_restrict(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ int op
+ )
+{
+ register struct conf_restrict *cr;
+ register int items;
+ struct sockaddr_storage matchaddr;
+ struct sockaddr_storage matchmask;
+ int bad;
+
+ /*
+ * Do a check of the flags to make sure that only
+ * the NTPPORT flag is set, if any. If not, complain
+ * about it. Note we are very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cr = (struct conf_restrict *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ if (cr->mflags & ~(RESM_NTPONLY))
+ bad |= 1;
+ if (cr->flags & ~(RES_ALLFLAGS))
+ bad |= 2;
+ if (cr->mask != htonl(INADDR_ANY)) {
+ if (client_v6_capable && cr->v6_flag != 0) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&cr->addr6))
+ bad |= 4;
+ } else
+ if (cr->addr == htonl(INADDR_ANY))
+ bad |= 8;
+ }
+ cr = (struct conf_restrict *)((char *)cr +
+ INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ if (bad) {
+ msyslog(LOG_ERR, "do_restrict: bad = %#x", bad);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cr = (struct conf_restrict *)inpkt->data;
+ memset((char *)&matchaddr, 0, sizeof(struct sockaddr_storage));
+ memset((char *)&matchmask, 0, sizeof(struct sockaddr_storage));
+
+ while (items-- > 0) {
+ if (client_v6_capable && cr->v6_flag != 0) {
+ GET_INADDR6(matchaddr) = cr->addr6;
+ GET_INADDR6(matchmask) = cr->mask6;
+ matchaddr.ss_family = AF_INET6;
+ matchmask.ss_family = AF_INET6;
+ } else {
+ GET_INADDR(matchaddr) = cr->addr;
+ GET_INADDR(matchmask) = cr->mask;
+ matchaddr.ss_family = AF_INET;
+ matchmask.ss_family = AF_INET;
+ }
+ hack_restrict(op, &matchaddr, &matchmask, cr->mflags,
+ cr->flags);
+ cr++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * mon_getlist - return monitor data
+ */
+static void
+mon_getlist_0(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_monitor *im;
+ register struct mon_data *md;
+ extern struct mon_data mon_mru_list;
+ extern int mon_enabled;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants monitor 0 list\n");
+#endif
+ if (!mon_enabled) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ im = (struct info_monitor *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_monitor));
+ for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0;
+ md = md->mru_next) {
+ im->lasttime = htonl((u_int32)md->avg_interval);
+ im->firsttime = htonl((u_int32)(current_time - md->lasttime));
+ im->lastdrop = htonl((u_int32)md->drop_count);
+ im->count = htonl((u_int32)(md->count));
+ if (md->rmtadr.ss_family == AF_INET6) {
+ if (!client_v6_capable)
+ continue;
+ im->addr6 = GET_INADDR6(md->rmtadr);
+ im->v6_flag = 1;
+ } else {
+ im->addr = GET_INADDR(md->rmtadr);
+ if (client_v6_capable)
+ im->v6_flag = 0;
+ }
+ im->port = md->rmtport;
+ im->mode = md->mode;
+ im->version = md->version;
+ im = (struct info_monitor *)more_pkt();
+ }
+ flush_pkt();
+}
+
+/*
+ * mon_getlist - return monitor data
+ */
+static void
+mon_getlist_1(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_monitor_1 *im;
+ register struct mon_data *md;
+ extern struct mon_data mon_mru_list;
+ extern int mon_enabled;
+
+ if (!mon_enabled) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ im = (struct info_monitor_1 *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_monitor_1));
+ for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0;
+ md = md->mru_next) {
+ im->lasttime = htonl((u_int32)md->avg_interval);
+ im->firsttime = htonl((u_int32)(current_time - md->lasttime));
+ im->lastdrop = htonl((u_int32)md->drop_count);
+ im->count = htonl((u_int32)md->count);
+ if (md->rmtadr.ss_family == AF_INET6) {
+ if (!client_v6_capable)
+ continue;
+ im->addr6 = GET_INADDR6(md->rmtadr);
+ im->v6_flag = 1;
+ im->daddr6 = GET_INADDR6(md->interface->sin);
+ } else {
+ im->addr = GET_INADDR(md->rmtadr);
+ if (client_v6_capable)
+ im->v6_flag = 0;
+ im->daddr = (md->cast_flags == MDF_BCAST)
+ ? GET_INADDR(md->interface->bcast)
+ : (md->cast_flags
+ ? (GET_INADDR(md->interface->sin)
+ ? GET_INADDR(md->interface->sin)
+ : GET_INADDR(md->interface->bcast))
+ : 4);
+ }
+ im->flags = md->cast_flags;
+ im->port = md->rmtport;
+ im->mode = md->mode;
+ im->version = md->version;
+ im = (struct info_monitor_1 *)more_pkt();
+ }
+ flush_pkt();
+}
+
+/*
+ * Module entry points and the flags they correspond with
+ */
+struct reset_entry {
+ int flag; /* flag this corresponds to */
+ void (*handler) P((void)); /* routine to handle request */
+};
+
+struct reset_entry reset_entries[] = {
+ { RESET_FLAG_ALLPEERS, peer_all_reset },
+ { RESET_FLAG_IO, io_clr_stats },
+ { RESET_FLAG_SYS, proto_clr_stats },
+ { RESET_FLAG_MEM, peer_clr_stats },
+ { RESET_FLAG_TIMER, timer_clr_stats },
+ { RESET_FLAG_AUTH, reset_auth_stats },
+ { RESET_FLAG_CTL, ctl_clr_stats },
+ { 0, 0 }
+};
+
+/*
+ * reset_stats - reset statistic counters here and there
+ */
+static void
+reset_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_long flags;
+ struct reset_entry *rent;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "reset_stats: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ flags = ((struct reset_flags *)inpkt->data)->flags;
+
+ if (flags & ~RESET_ALLFLAGS) {
+ msyslog(LOG_ERR, "reset_stats: reset leaves %#lx",
+ flags & ~RESET_ALLFLAGS);
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ for (rent = reset_entries; rent->flag != 0; rent++) {
+ if (flags & rent->flag)
+ (rent->handler)();
+ }
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * reset_peer - clear a peer's statistics
+ */
+static void
+reset_peer(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_unpeer *cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_storage peeraddr;
+ int bad;
+
+ /*
+ * We check first to see that every peer exists. If not,
+ * we return an error.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ if (client_v6_capable && cp->v6_flag != 0) {
+ GET_INADDR6(peeraddr) = cp->peeraddr6;
+ peeraddr.ss_family = AF_INET6;
+ } else {
+ GET_INADDR(peeraddr) = cp->peeraddr;
+ peeraddr.ss_family = AF_INET;
+ }
+ NSRCPORT(&peeraddr) = htons(NTP_PORT);
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+ peer = findexistingpeer(&peeraddr, (struct peer *)0, -1);
+ if (peer == (struct peer *)0)
+ bad++;
+ cp = (struct conf_unpeer *)((char *)cp +
+ INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+ while (items-- > 0) {
+ memset((char *)&peeraddr, 0, sizeof(peeraddr));
+ if (client_v6_capable && cp->v6_flag != 0) {
+ GET_INADDR6(peeraddr) = cp->peeraddr6;
+ peeraddr.ss_family = AF_INET6;
+ } else {
+ GET_INADDR(peeraddr) = cp->peeraddr;
+ peeraddr.ss_family = AF_INET;
+ }
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ peeraddr.ss_len = SOCKLEN(&peeraddr);
+#endif
+ peer = findexistingpeer(&peeraddr, (struct peer *)0, -1);
+ while (peer != 0) {
+ peer_reset(peer);
+ peer = findexistingpeer(&peeraddr, (struct peer *)peer, -1);
+ }
+ cp = (struct conf_unpeer *)((char *)cp +
+ INFO_ITEMSIZE(inpkt->mbz_itemsize));
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_key_reread - reread the encryption key file
+ */
+static void
+do_key_reread(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ rereadkeys();
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * trust_key - make one or more keys trusted
+ */
+static void
+trust_key(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_trustkey(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * untrust_key - make one or more keys untrusted
+ */
+static void
+untrust_key(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_trustkey(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * do_trustkey - make keys either trustable or untrustable
+ */
+static void
+do_trustkey(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ u_long trust
+ )
+{
+ register u_long *kp;
+ register int items;
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ kp = (u_long *)inpkt->data;
+ while (items-- > 0) {
+ authtrust(*kp, trust);
+ kp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * get_auth_info - return some stats concerning the authentication module
+ */
+static void
+get_auth_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_auth *ia;
+
+ /*
+ * Importations from the authentication module
+ */
+ extern u_long authnumkeys;
+ extern int authnumfreekeys;
+ extern u_long authkeylookups;
+ extern u_long authkeynotfound;
+ extern u_long authencryptions;
+ extern u_long authdecryptions;
+ extern u_long authkeyuncached;
+ extern u_long authkeyexpired;
+
+ ia = (struct info_auth *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_auth));
+
+ ia->numkeys = htonl((u_int32)authnumkeys);
+ ia->numfreekeys = htonl((u_int32)authnumfreekeys);
+ ia->keylookups = htonl((u_int32)authkeylookups);
+ ia->keynotfound = htonl((u_int32)authkeynotfound);
+ ia->encryptions = htonl((u_int32)authencryptions);
+ ia->decryptions = htonl((u_int32)authdecryptions);
+ ia->keyuncached = htonl((u_int32)authkeyuncached);
+ ia->expired = htonl((u_int32)authkeyexpired);
+ ia->timereset = htonl((u_int32)(current_time - auth_timereset));
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+
+/*
+ * reset_auth_stats - reset the authentication stat counters. Done here
+ * to keep ntp-isms out of the authentication module
+ */
+static void
+reset_auth_stats(void)
+{
+ /*
+ * Importations from the authentication module
+ */
+ extern u_long authkeylookups;
+ extern u_long authkeynotfound;
+ extern u_long authencryptions;
+ extern u_long authdecryptions;
+ extern u_long authkeyuncached;
+
+ authkeylookups = 0;
+ authkeynotfound = 0;
+ authencryptions = 0;
+ authdecryptions = 0;
+ authkeyuncached = 0;
+ auth_timereset = current_time;
+}
+
+
+/*
+ * req_get_traps - return information about current trap holders
+ */
+static void
+req_get_traps(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_trap *it;
+ register struct ctl_trap *tr;
+ register int i;
+
+ /*
+ * Imported from the control module
+ */
+ extern struct ctl_trap ctl_trap[];
+ extern int num_ctl_traps;
+
+ if (num_ctl_traps == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ it = (struct info_trap *)prepare_pkt(srcadr, inter, inpkt,
+ v6sizeof(struct info_trap));
+
+ for (i = 0, tr = ctl_trap; i < CTL_MAXTRAPS; i++, tr++) {
+ if (tr->tr_flags & TRAP_INUSE) {
+ if (tr->tr_addr.ss_family == AF_INET) {
+ if (tr->tr_localaddr == any_interface)
+ it->local_address = 0;
+ else
+ it->local_address
+ = GET_INADDR(tr->tr_localaddr->sin);
+ it->trap_address = GET_INADDR(tr->tr_addr);
+ if (client_v6_capable)
+ it->v6_flag = 0;
+ } else {
+ if (!client_v6_capable)
+ continue;
+ it->local_address6
+ = GET_INADDR6(tr->tr_localaddr->sin);
+ it->trap_address6 = GET_INADDR6(tr->tr_addr);
+ it->v6_flag = 1;
+ }
+ it->trap_port = NSRCPORT(&tr->tr_addr);
+ it->sequence = htons(tr->tr_sequence);
+ it->settime = htonl((u_int32)(current_time - tr->tr_settime));
+ it->origtime = htonl((u_int32)(current_time - tr->tr_origtime));
+ it->resets = htonl((u_int32)tr->tr_resets);
+ it->flags = htonl((u_int32)tr->tr_flags);
+ it = (struct info_trap *)more_pkt();
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * req_set_trap - configure a trap
+ */
+static void
+req_set_trap(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_setclr_trap(srcadr, inter, inpkt, 1);
+}
+
+
+
+/*
+ * req_clr_trap - unconfigure a trap
+ */
+static void
+req_clr_trap(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ do_setclr_trap(srcadr, inter, inpkt, 0);
+}
+
+
+
+/*
+ * do_setclr_trap - do the grunge work of (un)configuring a trap
+ */
+static void
+do_setclr_trap(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ int set
+ )
+{
+ register struct conf_trap *ct;
+ register struct interface *linter;
+ int res;
+ struct sockaddr_storage laddr;
+
+ /*
+ * Prepare sockaddr_storage structure
+ */
+ memset((char *)&laddr, 0, sizeof laddr);
+ laddr.ss_family = srcadr->ss_family;
+ NSRCPORT(&laddr) = ntohs(NTP_PORT);
+
+ /*
+ * Restrict ourselves to one item only. This eliminates
+ * the error reporting problem.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "do_setclr_trap: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ ct = (struct conf_trap *)inpkt->data;
+
+ /*
+ * Look for the local interface. If none, use the default.
+ */
+ if (ct->local_address == 0) {
+ linter = any_interface;
+ } else {
+ if (laddr.ss_family == AF_INET)
+ GET_INADDR(laddr) = ct->local_address;
+ else
+ GET_INADDR6(laddr) = ct->local_address6;
+ linter = findinterface(&laddr);
+ if (linter == NULL) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ }
+
+ if (laddr.ss_family == AF_INET)
+ GET_INADDR(laddr) = ct->trap_address;
+ else
+ GET_INADDR6(laddr) = ct->trap_address6;
+ if (ct->trap_port != 0)
+ NSRCPORT(&laddr) = ct->trap_port;
+ else
+ NSRCPORT(&laddr) = htons(TRAPPORT);
+
+ if (set) {
+ res = ctlsettrap(&laddr, linter, 0,
+ INFO_VERSION(inpkt->rm_vn_mode));
+ } else {
+ res = ctlclrtrap(&laddr, linter, 0);
+ }
+
+ if (!res) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ } else {
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+ }
+ return;
+}
+
+
+
+/*
+ * set_request_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_request_keyid(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ keyid_t keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "set_request_keyid: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ keyid = ntohl(*((u_int32 *)(inpkt->data)));
+ info_auth_keyid = keyid;
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * set_control_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_control_keyid(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ keyid_t keyid;
+ extern keyid_t ctl_auth_keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ msyslog(LOG_ERR, "set_control_keyid: err_nitems > 1");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ keyid = ntohl(*((u_int32 *)(inpkt->data)));
+ ctl_auth_keyid = keyid;
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * get_ctl_stats - return some stats concerning the control message module
+ */
+static void
+get_ctl_stats(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_control *ic;
+
+ /*
+ * Importations from the control module
+ */
+ extern u_long ctltimereset;
+ extern u_long numctlreq;
+ extern u_long numctlbadpkts;
+ extern u_long numctlresponses;
+ extern u_long numctlfrags;
+ extern u_long numctlerrors;
+ extern u_long numctltooshort;
+ extern u_long numctlinputresp;
+ extern u_long numctlinputfrag;
+ extern u_long numctlinputerr;
+ extern u_long numctlbadoffset;
+ extern u_long numctlbadversion;
+ extern u_long numctldatatooshort;
+ extern u_long numctlbadop;
+ extern u_long numasyncmsgs;
+
+ ic = (struct info_control *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_control));
+
+ ic->ctltimereset = htonl((u_int32)(current_time - ctltimereset));
+ ic->numctlreq = htonl((u_int32)numctlreq);
+ ic->numctlbadpkts = htonl((u_int32)numctlbadpkts);
+ ic->numctlresponses = htonl((u_int32)numctlresponses);
+ ic->numctlfrags = htonl((u_int32)numctlfrags);
+ ic->numctlerrors = htonl((u_int32)numctlerrors);
+ ic->numctltooshort = htonl((u_int32)numctltooshort);
+ ic->numctlinputresp = htonl((u_int32)numctlinputresp);
+ ic->numctlinputfrag = htonl((u_int32)numctlinputfrag);
+ ic->numctlinputerr = htonl((u_int32)numctlinputerr);
+ ic->numctlbadoffset = htonl((u_int32)numctlbadoffset);
+ ic->numctlbadversion = htonl((u_int32)numctlbadversion);
+ ic->numctldatatooshort = htonl((u_int32)numctldatatooshort);
+ ic->numctlbadop = htonl((u_int32)numctlbadop);
+ ic->numasyncmsgs = htonl((u_int32)numasyncmsgs);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+#ifdef KERNEL_PLL
+/*
+ * get_kernel_info - get kernel pll/pps information
+ */
+static void
+get_kernel_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_kernel *ik;
+ struct timex ntx;
+
+ if (!pll_control) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ memset((char *)&ntx, 0, sizeof(ntx));
+ if (ntp_adjtime(&ntx) < 0)
+ msyslog(LOG_ERR, "get_kernel_info: ntp_adjtime() failed: %m");
+ ik = (struct info_kernel *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_kernel));
+
+ /*
+ * pll variables
+ */
+ ik->offset = htonl((u_int32)ntx.offset);
+ ik->freq = htonl((u_int32)ntx.freq);
+ ik->maxerror = htonl((u_int32)ntx.maxerror);
+ ik->esterror = htonl((u_int32)ntx.esterror);
+ ik->status = htons(ntx.status);
+ ik->constant = htonl((u_int32)ntx.constant);
+ ik->precision = htonl((u_int32)ntx.precision);
+ ik->tolerance = htonl((u_int32)ntx.tolerance);
+
+ /*
+ * pps variables
+ */
+ ik->ppsfreq = htonl((u_int32)ntx.ppsfreq);
+ ik->jitter = htonl((u_int32)ntx.jitter);
+ ik->shift = htons(ntx.shift);
+ ik->stabil = htonl((u_int32)ntx.stabil);
+ ik->jitcnt = htonl((u_int32)ntx.jitcnt);
+ ik->calcnt = htonl((u_int32)ntx.calcnt);
+ ik->errcnt = htonl((u_int32)ntx.errcnt);
+ ik->stbcnt = htonl((u_int32)ntx.stbcnt);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+#endif /* KERNEL_PLL */
+
+
+#ifdef REFCLOCK
+/*
+ * get_clock_info - get info about a clock
+ */
+static void
+get_clock_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_clock *ic;
+ register u_int32 *clkaddr;
+ register int items;
+ struct refclockstat clock_stat;
+ struct sockaddr_storage addr;
+ struct sockaddr_in tmp_clock;
+ l_fp ltmp;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.ss_family = AF_INET;
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+ NSRCPORT(&addr) = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = (u_int32 *) inpkt->data;
+
+ ic = (struct info_clock *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clock));
+
+ while (items-- > 0) {
+ tmp_clock.sin_addr.s_addr = *clkaddr++;
+ CAST_V4(addr)->sin_addr = tmp_clock.sin_addr;
+ if (!ISREFCLOCKADR(&tmp_clock) ||
+ findexistingpeer(&addr, (struct peer *)0, -1) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ clock_stat.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&addr, (struct refclockstat *)0, &clock_stat);
+
+ ic->clockadr = tmp_clock.sin_addr.s_addr;
+ ic->type = clock_stat.type;
+ ic->flags = clock_stat.flags;
+ ic->lastevent = clock_stat.lastevent;
+ ic->currentstatus = clock_stat.currentstatus;
+ ic->polls = htonl((u_int32)clock_stat.polls);
+ ic->noresponse = htonl((u_int32)clock_stat.noresponse);
+ ic->badformat = htonl((u_int32)clock_stat.badformat);
+ ic->baddata = htonl((u_int32)clock_stat.baddata);
+ ic->timestarted = htonl((u_int32)clock_stat.timereset);
+ DTOLFP(clock_stat.fudgetime1, &ltmp);
+ HTONL_FP(&ltmp, &ic->fudgetime1);
+ DTOLFP(clock_stat.fudgetime2, &ltmp);
+ HTONL_FP(&ltmp, &ic->fudgetime2);
+ ic->fudgeval1 = htonl((u_int32)clock_stat.fudgeval1);
+ ic->fudgeval2 = htonl((u_int32)clock_stat.fudgeval2);
+
+ free_varlist(clock_stat.kv_list);
+
+ ic = (struct info_clock *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * set_clock_fudge - get a clock's fudge factors
+ */
+static void
+set_clock_fudge(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_fudge *cf;
+ register int items;
+ struct refclockstat clock_stat;
+ struct sockaddr_storage addr;
+ struct sockaddr_in tmp_clock;
+ l_fp ltmp;
+
+ memset((char *)&addr, 0, sizeof addr);
+ memset((char *)&clock_stat, 0, sizeof clock_stat);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cf = (struct conf_fudge *) inpkt->data;
+
+ while (items-- > 0) {
+ tmp_clock.sin_addr.s_addr = cf->clockadr;
+ *CAST_V4(addr) = tmp_clock;
+ addr.ss_family = AF_INET;
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+ NSRCPORT(&addr) = htons(NTP_PORT);
+ if (!ISREFCLOCKADR(&tmp_clock) ||
+ findexistingpeer(&addr, (struct peer *)0, -1) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ switch(ntohl(cf->which)) {
+ case FUDGE_TIME1:
+ NTOHL_FP(&cf->fudgetime, &ltmp);
+ LFPTOD(&ltmp, clock_stat.fudgetime1);
+ clock_stat.haveflags = CLK_HAVETIME1;
+ break;
+ case FUDGE_TIME2:
+ NTOHL_FP(&cf->fudgetime, &ltmp);
+ LFPTOD(&ltmp, clock_stat.fudgetime2);
+ clock_stat.haveflags = CLK_HAVETIME2;
+ break;
+ case FUDGE_VAL1:
+ clock_stat.fudgeval1 = ntohl(cf->fudgeval_flags);
+ clock_stat.haveflags = CLK_HAVEVAL1;
+ break;
+ case FUDGE_VAL2:
+ clock_stat.fudgeval2 = ntohl(cf->fudgeval_flags);
+ clock_stat.haveflags = CLK_HAVEVAL2;
+ break;
+ case FUDGE_FLAGS:
+ clock_stat.flags = (u_char) (ntohl(cf->fudgeval_flags) & 0xf);
+ clock_stat.haveflags =
+ (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4);
+ break;
+ default:
+ msyslog(LOG_ERR, "set_clock_fudge: default!");
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ refclock_control(&addr, &clock_stat, (struct refclockstat *)0);
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+#endif
+
+#ifdef REFCLOCK
+/*
+ * get_clkbug_info - get debugging info about a clock
+ */
+static void
+get_clkbug_info(
+ struct sockaddr_storage *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register int i;
+ register struct info_clkbug *ic;
+ register u_int32 *clkaddr;
+ register int items;
+ struct refclockbug bug;
+ struct sockaddr_storage addr;
+ struct sockaddr_in tmp_clock;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.ss_family = AF_INET;
+#ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ addr.ss_len = SOCKLEN(&addr);
+#endif
+ NSRCPORT(&addr) = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = (u_int32 *) inpkt->data;
+
+ ic = (struct info_clkbug *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clkbug));
+
+ while (items-- > 0) {
+ tmp_clock.sin_addr.s_addr = *clkaddr++;
+ GET_INADDR(addr) = tmp_clock.sin_addr.s_addr;
+ if (!ISREFCLOCKADR(&tmp_clock) ||
+ findexistingpeer(&addr, (struct peer *)0, -1) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ memset((char *)&bug, 0, sizeof bug);
+ refclock_buginfo(&addr, &bug);
+ if (bug.nvalues == 0 && bug.ntimes == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ ic->clockadr = tmp_clock.sin_addr.s_addr;
+ i = bug.nvalues;
+ if (i > NUMCBUGVALUES)
+ i = NUMCBUGVALUES;
+ ic->nvalues = (u_char)i;
+ ic->svalues = htons((u_short) (bug.svalues & ((1<<i)-1)));
+ while (--i >= 0)
+ ic->values[i] = htonl(bug.values[i]);
+
+ i = bug.ntimes;
+ if (i > NUMCBUGTIMES)
+ i = NUMCBUGTIMES;
+ ic->ntimes = (u_char)i;
+ ic->stimes = htonl(bug.stimes);
+ while (--i >= 0) {
+ HTONL_FP(&bug.times[i], &ic->times[i]);
+ }
+
+ ic = (struct info_clkbug *)more_pkt();
+ }
+ flush_pkt();
+}
+#endif
diff --git a/ntpd/ntp_restrict.c b/ntpd/ntp_restrict.c
new file mode 100644
index 0000000..ede4225
--- /dev/null
+++ b/ntpd/ntp_restrict.c
@@ -0,0 +1,586 @@
+/*
+ * ntp_restrict.c - determine host restrictions
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This code keeps a simple address-and-mask list of hosts we want
+ * to place restrictions on (or remove them from). The restrictions
+ * are implemented as a set of flags which tell you what the host
+ * can't do. There is a subroutine entry to return the flags. The
+ * list is kept sorted to reduce the average number of comparisons
+ * and make sure you get the set of restrictions most specific to
+ * the address.
+ *
+ * The algorithm is that, when looking up a host, it is first assumed
+ * that the default set of restrictions will apply. It then searches
+ * down through the list. Whenever it finds a match it adopts the
+ * match's flags instead. When you hit the point where the sorted
+ * address is greater than the target, you return with the last set of
+ * flags you found. Because of the ordering of the list, the most
+ * specific match will provide the final set of flags.
+ *
+ * This was originally intended to restrict you from sync'ing to your
+ * own broadcasts when you are doing that, by restricting yourself from
+ * your own interfaces. It was also thought it would sometimes be useful
+ * to keep a misbehaving host or two from abusing your primary clock. It
+ * has been expanded, however, to suit the needs of those with more
+ * restrictive access policies.
+ */
+/*
+ * We will use two lists, one for IPv4 addresses and one for IPv6
+ * addresses. This is not protocol-independant but for now I can't
+ * find a way to respect this. We'll check this later... JFB 07/2001
+ */
+#define SET_IPV6_ADDR_MASK(dst, src, msk) \
+ do { \
+ int idx; \
+ for (idx = 0; idx < 16; idx++) { \
+ (dst)->s6_addr[idx] = \
+ (u_char) ((src)->s6_addr[idx] & (msk)->s6_addr[idx]); \
+ } \
+ } while (0)
+
+/*
+ * Memory allocation parameters. We allocate INITRESLIST entries
+ * initially, and add INCRESLIST entries to the free list whenever
+ * we run out.
+ */
+#define INITRESLIST 10
+#define INCRESLIST 5
+
+#define RES_AVG 8. /* interpacket averaging factor */
+
+/*
+ * The restriction list
+ */
+struct restrictlist *restrictlist;
+struct restrictlist6 *restrictlist6;
+static int restrictcount; /* count of entries in the res list */
+static int restrictcount6; /* count of entries in the res list 2*/
+
+/*
+ * The free list and associated counters. Also some uninteresting
+ * stat counters.
+ */
+static struct restrictlist *resfree;
+static struct restrictlist6 *resfree6;
+static int numresfree; /* number of structures on free list */
+static int numresfree6; /* number of structures on free list 2 */
+
+static u_long res_calls;
+static u_long res_found;
+static u_long res_not_found;
+
+/*
+ * Parameters of the RES_LIMITED restriction option.
+ */
+u_long res_avg_interval = 5; /* min average interpacket interval */
+u_long res_min_interval = 1; /* min interpacket interval */
+
+/*
+ * Count number of restriction entries referring to RES_LIMITED controls
+ * activation/deactivation of monitoring (with respect to RES_LIMITED
+ * control)
+ */
+static u_long res_limited_refcnt;
+static u_long res_limited_refcnt6;
+
+/*
+ * Our initial allocation of lists entries.
+ */
+static struct restrictlist resinit[INITRESLIST];
+static struct restrictlist6 resinit6[INITRESLIST];
+
+/*
+ * init_restrict - initialize the restriction data structures
+ */
+void
+init_restrict(void)
+{
+ register int i;
+
+ /*
+ * Zero the list and put all but one on the free list
+ */
+ resfree = 0;
+ memset((char *)resinit, 0, sizeof resinit);
+ resfree6 = 0;
+ memset((char *)resinit6, 0, sizeof resinit6);
+ for (i = 1; i < INITRESLIST; i++) {
+ resinit[i].next = resfree;
+ resinit6[i].next = resfree6;
+ resfree = &resinit[i];
+ resfree6 = &resinit6[i];
+ }
+ numresfree = INITRESLIST-1;
+ numresfree6 = INITRESLIST-1;
+
+ /*
+ * Put the remaining item at the head of the list as our default
+ * entry. Everything in here should be zero for now.
+ */
+ resinit[0].addr = htonl(INADDR_ANY);
+ resinit[0].mask = 0;
+ memset(&resinit6[0].addr6, 0, sizeof(struct in6_addr));
+ memset(&resinit6[0].mask6, 0, sizeof(struct in6_addr));
+ restrictlist = &resinit[0];
+ restrictlist6 = &resinit6[0];
+ restrictcount = 1;
+ restrictcount = 2;
+
+ /*
+ * fix up stat counters
+ */
+ res_calls = 0;
+ res_found = 0;
+ res_not_found = 0;
+
+ /*
+ * set default values for RES_LIMIT functionality
+ */
+ res_limited_refcnt = 0;
+ res_limited_refcnt6 = 0;
+}
+
+
+/*
+ * restrictions - return restrictions for this host
+ */
+int
+restrictions(
+ struct sockaddr_storage *srcadr
+ )
+{
+ struct restrictlist *rl;
+ struct restrictlist *match = NULL;
+ struct restrictlist6 *rl6;
+ struct restrictlist6 *match6 = NULL;
+ struct in6_addr hostaddr6;
+ struct in6_addr hostservaddr6;
+ u_int32 hostaddr;
+ int flags = 0;
+ int isntpport;
+
+ res_calls++;
+ if (srcadr->ss_family == AF_INET) {
+ /*
+ * We need the host address in host order. Also need to
+ * know whether this is from the ntp port or not.
+ */
+ hostaddr = SRCADR(srcadr);
+ isntpport = (SRCPORT(srcadr) == NTP_PORT);
+
+ /*
+ * Ignore any packets with a multicast source address
+ * (this should be done early in the receive process,
+ * later!)
+ */
+ if (IN_CLASSD(SRCADR(srcadr)))
+ return (int)RES_IGNORE;
+
+ /*
+ * Set match to first entry, which is default entry.
+ * Work our way down from there.
+ */
+ match = restrictlist;
+ for (rl = match->next; rl != 0 && rl->addr <= hostaddr;
+ rl = rl->next)
+ if ((hostaddr & rl->mask) == rl->addr) {
+ if ((rl->mflags & RESM_NTPONLY) &&
+ !isntpport)
+ continue;
+ match = rl;
+ }
+ match->count++;
+ if (match == restrictlist)
+ res_not_found++;
+ else
+ res_found++;
+ flags = match->flags;
+ }
+
+ /* IPv6 source address */
+ if (srcadr->ss_family == AF_INET6) {
+ /*
+ * Need to know whether this is from the ntp port or
+ * not.
+ */
+ hostaddr6 = GET_INADDR6(*srcadr);
+ isntpport = (ntohs((
+ (struct sockaddr_in6 *)srcadr)->sin6_port) ==
+ NTP_PORT);
+
+ /*
+ * Ignore any packets with a multicast source address
+ * (this should be done early in the receive process,
+ * later!)
+ */
+ if (IN6_IS_ADDR_MULTICAST(&hostaddr6))
+ return (int)RES_IGNORE;
+
+ /*
+ * Set match to first entry, which is default entry.
+ * Work our way down from there.
+ */
+ match6 = restrictlist6;
+ for (rl6 = match6->next; rl6 != 0 &&
+ (memcmp(&(rl6->addr6), &hostaddr6,
+ sizeof(hostaddr6)) <= 0); rl6 = rl6->next) {
+ SET_IPV6_ADDR_MASK(&hostservaddr6, &hostaddr6,
+ &rl6->mask6);
+ if (memcmp(&hostservaddr6, &(rl6->addr6),
+ sizeof(hostservaddr6)) == 0) {
+ if ((rl6->mflags & RESM_NTPONLY) &&
+ !isntpport)
+ continue;
+ match6 = rl6;
+ }
+ }
+ match6->count++;
+ if (match6 == restrictlist6)
+ res_not_found++;
+ else
+ res_found++;
+ flags = match6->flags;
+ }
+
+ /*
+ * The following implements a generalized call gap facility.
+ * Douse the RES_LIMITED bit only if the interval since the last
+ * packet is greater than res_min_interval and the average is
+ * greater thatn res_avg_interval.
+ */
+ if (mon_enabled == MON_OFF) {
+ flags &= ~RES_LIMITED;
+ } else {
+ struct mon_data *md;
+
+ /*
+ * At this poin the most recent arrival is first in the
+ * MRU list. Let the first 10 packets in for free until
+ * the average stabilizes.
+ */
+ md = mon_mru_list.mru_next;
+ if (md->avg_interval == 0)
+ md->avg_interval = md->drop_count;
+ else
+ md->avg_interval += (md->drop_count -
+ md->avg_interval) / RES_AVG;
+ if (md->count < 10 || (md->drop_count >
+ res_min_interval && md->avg_interval >
+ res_avg_interval))
+ flags &= ~RES_LIMITED;
+ md->drop_count = flags;
+ }
+ return (flags);
+}
+
+
+/*
+ * hack_restrict - add/subtract/manipulate entries on the restrict list
+ */
+void
+hack_restrict(
+ int op,
+ struct sockaddr_storage *resaddr,
+ struct sockaddr_storage *resmask,
+ int mflags,
+ int flags
+ )
+{
+ register u_int32 addr = 0;
+ register u_int32 mask = 0;
+ struct in6_addr addr6;
+ struct in6_addr mask6;
+ register struct restrictlist *rl = NULL;
+ register struct restrictlist *rlprev = NULL;
+ register struct restrictlist6 *rl6 = NULL;
+ register struct restrictlist6 *rlprev6 = NULL;
+ int i, addr_cmp, mask_cmp;
+ memset(&addr6, 0, sizeof(struct in6_addr));
+ memset(&mask6, 0, sizeof(struct in6_addr));
+
+ if (resaddr->ss_family == AF_INET) {
+ /*
+ * Get address and mask in host byte order
+ */
+ addr = SRCADR(resaddr);
+ mask = SRCADR(resmask);
+ addr &= mask; /* make sure low bits zero */
+
+ /*
+ * If this is the default address, point at first on
+ * list. Else go searching for it.
+ */
+ if (addr == 0) {
+ rlprev = 0;
+ rl = restrictlist;
+ } else {
+ rlprev = restrictlist;
+ rl = rlprev->next;
+ while (rl != 0) {
+ if (rl->addr > addr) {
+ rl = 0;
+ break;
+ } else if (rl->addr == addr) {
+ if (rl->mask == mask) {
+ if ((mflags &
+ RESM_NTPONLY) ==
+ (rl->mflags &
+ RESM_NTPONLY))
+ break;
+
+ if (!(mflags &
+ RESM_NTPONLY)) {
+ rl = 0;
+ break;
+ }
+ } else if (rl->mask > mask) {
+ rl = 0;
+ break;
+ }
+ }
+ rlprev = rl;
+ rl = rl->next;
+ }
+ }
+ }
+
+ if (resaddr->ss_family == AF_INET6) {
+ mask6 = GET_INADDR6(*resmask);
+ SET_IPV6_ADDR_MASK(&addr6,
+ &GET_INADDR6(*resaddr), &mask6);
+ if (IN6_IS_ADDR_UNSPECIFIED(&addr6)) {
+ rlprev6 = 0;
+ rl6 = restrictlist6;
+ } else {
+ rlprev6 = restrictlist6;
+ rl6 = rlprev6->next;
+ while (rl6 != 0) {
+ addr_cmp = memcmp(&rl6->addr6, &addr6,
+ sizeof(addr6));
+ if (addr_cmp > 0) {
+ rl6 = 0;
+ break;
+ } else if (addr_cmp == 0) {
+ mask_cmp = memcmp(&rl6->mask6,
+ &mask6, sizeof(mask6));
+ if (mask_cmp == 0) {
+ if ((mflags &
+ RESM_NTPONLY) ==
+ (rl6->mflags &
+ RESM_NTPONLY))
+ break;
+
+ if (!(mflags &
+ RESM_NTPONLY)) {
+ rl6 = 0;
+ break;
+ }
+ } else if (mask_cmp > 0) {
+ rl6 = 0;
+ break;
+ }
+ }
+ rlprev6 = rl6;
+ rl6 = rl6->next;
+ }
+ }
+ }
+
+ /*
+ * In case the above wasn't clear :-), either rl now points
+ * at the entry this call refers to, or rl is zero and rlprev
+ * points to the entry prior to where this one should go in
+ * the sort.
+ */
+
+ /*
+ * Switch based on operation
+ */
+ if (resaddr->ss_family == AF_INET) {
+ switch (op) {
+ case RESTRICT_FLAGS:
+ /*
+ * Here we add bits to the flags. If this is a
+ * new restriction add it.
+ */
+ if (rl == 0) {
+ if (numresfree == 0) {
+ rl = (struct restrictlist *)
+ emalloc(INCRESLIST *
+ sizeof(struct
+ restrictlist));
+ memset((char *)rl, 0,
+ INCRESLIST * sizeof(struct
+ restrictlist));
+ for (i = 0; i < INCRESLIST; i++) {
+ rl->next = resfree;
+ resfree = rl;
+ rl++;
+ }
+ numresfree = INCRESLIST;
+ }
+
+ rl = resfree;
+ resfree = rl->next;
+ numresfree--;
+
+ rl->addr = addr;
+ rl->mask = mask;
+ rl->mflags = (u_short)mflags;
+
+ rl->next = rlprev->next;
+ rlprev->next = rl;
+ restrictcount++;
+ }
+ if ((rl->flags ^ (u_short)flags) &
+ RES_LIMITED) {
+ res_limited_refcnt++;
+ mon_start(MON_RES);
+ }
+ rl->flags |= (u_short)flags;
+ break;
+
+ case RESTRICT_UNFLAG:
+ /*
+ * Remove some bits from the flags. If we didn't
+ * find this one, just return.
+ */
+ if (rl != 0) {
+ if ((rl->flags ^ (u_short)flags) &
+ RES_LIMITED) {
+ res_limited_refcnt--;
+ if (res_limited_refcnt == 0)
+ mon_stop(MON_RES);
+ }
+ rl->flags &= (u_short)~flags;
+ }
+ break;
+
+ case RESTRICT_REMOVE:
+ /*
+ * Remove an entry from the table entirely if we
+ * found one. Don't remove the default entry and
+ * don't remove an interface entry.
+ */
+ if (rl != 0
+ && rl->addr != htonl(INADDR_ANY)
+ && !(rl->mflags & RESM_INTERFACE)) {
+ rlprev->next = rl->next;
+ restrictcount--;
+ if (rl->flags & RES_LIMITED) {
+ res_limited_refcnt--;
+ if (res_limited_refcnt == 0)
+ mon_stop(MON_RES);
+ }
+ memset((char *)rl, 0,
+ sizeof(struct restrictlist));
+
+ rl->next = resfree;
+ resfree = rl;
+ numresfree++;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else if (resaddr->ss_family == AF_INET6) {
+ switch (op) {
+ case RESTRICT_FLAGS:
+ /*
+ * Here we add bits to the flags. If this is a
+ * new restriction add it.
+ */
+ if (rl6 == 0) {
+ if (numresfree6 == 0) {
+ rl6 = (struct
+ restrictlist6 *)emalloc(
+ INCRESLIST * sizeof(struct
+ restrictlist6));
+ memset((char *)rl6, 0,
+ INCRESLIST * sizeof(struct
+ restrictlist6));
+
+ for (i = 0; i < INCRESLIST;
+ i++) {
+ rl6->next = resfree6;
+ resfree6 = rl6;
+ rl6++;
+ }
+ numresfree6 = INCRESLIST;
+ }
+ rl6 = resfree6;
+ resfree6 = rl6->next;
+ numresfree6--;
+ rl6->addr6 = addr6;
+ rl6->mask6 = mask6;
+ rl6->mflags = (u_short)mflags;
+ rl6->next = rlprev6->next;
+ rlprev6->next = rl6;
+ restrictcount6++;
+ }
+ if ((rl6->flags ^ (u_short)flags) &
+ RES_LIMITED) {
+ res_limited_refcnt6++;
+ mon_start(MON_RES);
+ }
+ rl6->flags |= (u_short)flags;
+ break;
+
+ case RESTRICT_UNFLAG:
+ /*
+ * Remove some bits from the flags. If we didn't
+ * find this one, just return.
+ */
+ if (rl6 != 0) {
+ if ((rl6->flags ^ (u_short)flags) &
+ RES_LIMITED) {
+ res_limited_refcnt6--;
+ if (res_limited_refcnt6 == 0)
+ mon_stop(MON_RES);
+ }
+ rl6->flags &= (u_short)~flags;
+ }
+ break;
+
+ case RESTRICT_REMOVE:
+ /*
+ * Remove an entry from the table entirely if we
+ * found one. Don't remove the default entry and
+ * don't remove an interface entry.
+ */
+ if (rl6 != 0 &&
+ !IN6_IS_ADDR_UNSPECIFIED(&rl6->addr6)
+ && !(rl6->mflags & RESM_INTERFACE)) {
+ rlprev6->next = rl6->next;
+ restrictcount6--;
+ if (rl6->flags & RES_LIMITED) {
+ res_limited_refcnt6--;
+ if (res_limited_refcnt6 == 0)
+ mon_stop(MON_RES);
+ }
+ memset((char *)rl6, 0,
+ sizeof(struct restrictlist6));
+ rl6->next = resfree6;
+ resfree6 = rl6;
+ numresfree6++;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
diff --git a/ntpd/ntp_timer.c b/ntpd/ntp_timer.c
new file mode 100644
index 0000000..6f0f18b
--- /dev/null
+++ b/ntpd/ntp_timer.c
@@ -0,0 +1,377 @@
+/*
+ * ntp_timer.c - event timer support routines
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <signal.h>
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if defined(HAVE_IO_COMPLETION_PORT)
+# include "ntp_iocompletionport.h"
+# include "ntp_timer.h"
+#endif
+
+/*
+ * These routines provide support for the event timer. The timer is
+ * implemented by an interrupt routine which sets a flag once every
+ * 2**EVENT_TIMEOUT seconds (currently 4), and a timer routine which
+ * is called when the mainline code gets around to seeing the flag.
+ * The timer routine dispatches the clock adjustment code if its time
+ * has come, then searches the timer queue for expiries which are
+ * dispatched to the transmit procedure. Finally, we call the hourly
+ * procedure to do cleanup and print a message.
+ */
+
+/*
+ * Alarm flag. The mainline code imports this.
+ */
+volatile int alarm_flag;
+
+/*
+ * The counters
+ */
+static u_long adjust_timer; /* second timer */
+static u_long keys_timer; /* minute timer */
+static u_long hourly_timer; /* hour timer */
+static u_long huffpuff_timer; /* huff-n'-puff timer */
+#ifdef OPENSSL
+static u_long revoke_timer; /* keys revoke timer */
+u_char sys_revoke = KEY_REVOKE; /* keys revoke timeout (log2 s) */
+#endif /* OPENSSL */
+
+/*
+ * Statistics counter for the interested.
+ */
+volatile u_long alarm_overflow;
+
+#define MINUTE 60
+#define HOUR (60*60)
+
+u_long current_time;
+
+/*
+ * Stats. Number of overflows and number of calls to transmit().
+ */
+u_long timer_timereset;
+u_long timer_overflows;
+u_long timer_xmtcalls;
+
+#if defined(VMS)
+static int vmstimer[2]; /* time for next timer AST */
+static int vmsinc[2]; /* timer increment */
+#endif /* VMS */
+
+#if defined SYS_WINNT
+static HANDLE WaitableTimerHandle = NULL;
+#else
+static RETSIGTYPE alarming P((int));
+#endif /* SYS_WINNT */
+
+#if !defined(VMS)
+# if !defined SYS_WINNT || defined(SYS_CYGWIN32)
+# ifndef HAVE_TIMER_SETTIME
+ struct itimerval itimer;
+# else
+ static timer_t ntpd_timerid;
+ struct itimerspec itimer;
+# endif /* HAVE_TIMER_SETTIME */
+# endif /* SYS_WINNT */
+#endif /* VMS */
+
+/*
+ * reinit_timer - reinitialize interval timer.
+ */
+void
+reinit_timer(void)
+{
+#if !defined(SYS_WINNT) && !defined(VMS)
+# if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
+ timer_gettime(ntpd_timerid, &itimer);
+ if (itimer.it_value.tv_sec < 0 || itimer.it_value.tv_sec > (1<<EVENT_TIMEOUT)) {
+ itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ }
+ if (itimer.it_value.tv_nsec < 0 ) {
+ itimer.it_value.tv_nsec = 0;
+ }
+ if (itimer.it_value.tv_sec == 0 && itimer.it_value.tv_nsec == 0) {
+ itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_value.tv_nsec = 0;
+ }
+ itimer.it_interval.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_nsec = 0;
+ timer_settime(ntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL);
+# else
+ getitimer(ITIMER_REAL, &itimer);
+ if (itimer.it_value.tv_sec < 0 || itimer.it_value.tv_sec > (1<<EVENT_TIMEOUT)) {
+ itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ }
+ if (itimer.it_value.tv_usec < 0 ) {
+ itimer.it_value.tv_usec = 0;
+ }
+ if (itimer.it_value.tv_sec == 0 && itimer.it_value.tv_usec == 0) {
+ itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_value.tv_usec = 0;
+ }
+ itimer.it_interval.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+# endif
+# endif /* VMS */
+}
+
+/*
+ * init_timer - initialize the timer data structures
+ */
+void
+init_timer(void)
+{
+# if defined SYS_WINNT & !defined(SYS_CYGWIN32)
+ HANDLE hToken;
+ TOKEN_PRIVILEGES tkp;
+# endif /* SYS_WINNT */
+
+ /*
+ * Initialize...
+ */
+ alarm_flag = 0;
+ alarm_overflow = 0;
+ adjust_timer = 1;
+ hourly_timer = HOUR;
+ huffpuff_timer = 0;
+ current_time = 0;
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = 0;
+
+#if !defined(SYS_WINNT)
+ /*
+ * Set up the alarm interrupt. The first comes 2**EVENT_TIMEOUT
+ * seconds from now and they continue on every 2**EVENT_TIMEOUT
+ * seconds.
+ */
+# if !defined(VMS)
+# if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
+ if (timer_create (CLOCK_REALTIME, NULL, &ntpd_timerid) ==
+# ifdef SYS_VXWORKS
+ ERROR
+# else
+ -1
+# endif
+ )
+ {
+ fprintf (stderr, "timer create FAILED\n");
+ exit (0);
+ }
+ (void) signal_no_reset(SIGALRM, alarming);
+ itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_nsec = itimer.it_value.tv_nsec = 0;
+ timer_settime(ntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL);
+# else
+ (void) signal_no_reset(SIGALRM, alarming);
+ itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_usec = itimer.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+# endif
+# else /* VMS */
+ vmsinc[0] = 10000000; /* 1 sec */
+ vmsinc[1] = 0;
+ lib$emul(&(1<<EVENT_TIMEOUT), &vmsinc, &0, &vmsinc);
+
+ sys$gettim(&vmstimer); /* that's "now" as abstime */
+
+ lib$addx(&vmsinc, &vmstimer, &vmstimer);
+ sys$setimr(0, &vmstimer, alarming, alarming, 0);
+# endif /* VMS */
+#else /* SYS_WINNT */
+ _tzset();
+
+ /*
+ * Get privileges needed for fiddling with the clock
+ */
+
+ /* get the current process token handle */
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
+ msyslog(LOG_ERR, "OpenProcessToken failed: %m");
+ exit(1);
+ }
+ /* get the LUID for system-time privilege. */
+ LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
+ tkp.PrivilegeCount = 1; /* one privilege to set */
+ tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ /* get set-time privilege for this process. */
+ AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
+ /* cannot test return value of AdjustTokenPrivileges. */
+ if (GetLastError() != ERROR_SUCCESS) {
+ msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");
+ }
+
+ /*
+ * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds
+ * Under Windows/NT,
+ */
+
+ WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
+ if (WaitableTimerHandle == NULL) {
+ msyslog(LOG_ERR, "CreateWaitableTimer failed: %m");
+ exit(1);
+ }
+ else {
+ DWORD Period = (1<<EVENT_TIMEOUT) * 1000;
+ LARGE_INTEGER DueTime;
+ DueTime.QuadPart = Period * 10000i64;
+ if (!SetWaitableTimer(WaitableTimerHandle, &DueTime, Period, NULL, NULL, FALSE) != NO_ERROR) {
+ msyslog(LOG_ERR, "SetWaitableTimer failed: %m");
+ exit(1);
+ }
+ }
+
+#endif /* SYS_WINNT */
+}
+
+#if defined(SYS_WINNT)
+extern HANDLE
+get_timer_handle(void)
+{
+ return WaitableTimerHandle;
+}
+#endif
+
+/*
+ * timer - dispatch anyone who needs to be
+ */
+void
+timer(void)
+{
+ register struct peer *peer, *next_peer;
+#ifdef OPENSSL
+ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
+#endif /* OPENSSL */
+ u_int n;
+
+ current_time += (1<<EVENT_TIMEOUT);
+
+ /*
+ * Adjustment timeout first.
+ */
+ if (adjust_timer <= current_time) {
+ adjust_timer += 1;
+ adj_host_clock();
+ kod_proto();
+ }
+
+ /*
+ * Now dispatch any peers whose event timer has expired. Be careful
+ * here, since the peer structure might go away as the result of
+ * the call.
+ */
+ for (n = 0; n < HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+ next_peer = peer->next;
+ if (peer->action && peer->nextaction <= current_time)
+ peer->action(peer);
+ if (peer->nextdate <= current_time) {
+#ifdef REFCLOCK
+ if (peer->flags & FLAG_REFCLOCK)
+ refclock_transmit(peer);
+ else
+ transmit(peer);
+#else /* REFCLOCK */
+ transmit(peer);
+#endif /* REFCLOCK */
+ }
+ }
+ }
+
+ /*
+ * Garbage collect expired keys.
+ */
+ if (keys_timer <= current_time) {
+ keys_timer += MINUTE;
+ auth_agekeys();
+ }
+
+ /*
+ * Huff-n'-puff filter
+ */
+ if (huffpuff_timer <= current_time) {
+ huffpuff_timer += HUFFPUFF;
+ huffpuff();
+ }
+
+#ifdef OPENSSL
+ /*
+ * Garbage collect old keys and generate new private value
+ */
+ if (revoke_timer <= current_time) {
+ revoke_timer += RANDPOLL(sys_revoke);
+ expire_all();
+ sprintf(statstr, "refresh ts %u", ntohl(hostval.tstamp));
+ record_crypto_stats(NULL, statstr);
+#ifdef DEBUG
+ if (debug)
+ printf("timer: %s\n", statstr);
+#endif
+ }
+#endif /* OPENSSL */
+
+ /*
+ * Finally, call the hourly routine.
+ */
+ if (hourly_timer <= current_time) {
+ hourly_timer += HOUR;
+ hourly_stats();
+ }
+}
+
+
+#ifndef SYS_WINNT
+/*
+ * alarming - tell the world we've been alarmed
+ */
+static RETSIGTYPE
+alarming(
+ int sig
+ )
+{
+#if !defined(VMS)
+ if (initializing)
+ return;
+ if (alarm_flag)
+ alarm_overflow++;
+ else
+ alarm_flag++;
+#else /* VMS AST routine */
+ if (!initializing) {
+ if (alarm_flag) alarm_overflow++;
+ else alarm_flag = 1; /* increment is no good */
+ }
+ lib$addx(&vmsinc,&vmstimer,&vmstimer);
+ sys$setimr(0,&vmstimer,alarming,alarming,0);
+#endif /* VMS */
+}
+#endif /* SYS_WINNT */
+
+
+/*
+ * timer_clr_stats - clear timer module stat counters
+ */
+void
+timer_clr_stats(void)
+{
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = current_time;
+}
+
diff --git a/ntpd/ntp_util.c b/ntpd/ntp_util.c
new file mode 100644
index 0000000..135f9b3
--- /dev/null
+++ b/ntpd/ntp_util.c
@@ -0,0 +1,797 @@
+/*
+ * ntp_util.c - stuff I didn't have any other place for
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_filegen.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_IEEEFP_H
+# include <ieeefp.h>
+#endif
+#ifdef HAVE_MATH_H
+# include <math.h>
+#endif
+
+#ifdef DOSYNCTODR
+#if !defined(VMS)
+#include <sys/resource.h>
+#endif /* VMS */
+#endif
+
+#if defined(VMS)
+#include <descrip.h>
+#endif /* VMS */
+
+/*
+ * This contains odds and ends. Right now the only thing you'll find
+ * in here is the hourly stats printer and some code to support
+ * rereading the keys file, but I may eventually put other things in
+ * here such as code to do something with the leap bits.
+ */
+/*
+ * Name of the keys file
+ */
+static char *key_file_name;
+
+/*
+ * The name of the drift_comp file and the temporary.
+ */
+static char *stats_drift_file;
+static char *stats_temp_file;
+
+/*
+ * Statistics file stuff
+ */
+#ifndef NTP_VAR
+#ifndef SYS_WINNT
+#define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */
+#else
+#define NTP_VAR "c:\\var\\ntp\\" /* NOTE the trailing '\\' */
+#endif /* SYS_WINNT */
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 256
+#endif
+
+static char statsdir[MAXPATHLEN] = NTP_VAR;
+
+static FILEGEN peerstats;
+static FILEGEN loopstats;
+static FILEGEN clockstats;
+static FILEGEN rawstats;
+static FILEGEN sysstats;
+#ifdef OPENSSL
+static FILEGEN cryptostats;
+#endif /* OPENSSL */
+
+/*
+ * This controls whether stats are written to the fileset. Provided
+ * so that ntpdc can turn off stats when the file system fills up.
+ */
+int stats_control;
+
+/*
+ * init_util - initialize the utilities
+ */
+void
+init_util(void)
+{
+ stats_drift_file = 0;
+ stats_temp_file = 0;
+ key_file_name = 0;
+
+#define PEERNAME "peerstats"
+#define LOOPNAME "loopstats"
+#define CLOCKNAME "clockstats"
+#define RAWNAME "rawstats"
+#define STANAME "systats"
+#ifdef OPENSSL
+#define CRYPTONAME "cryptostats"
+#endif /* OPENSSL */
+
+ peerstats.fp = NULL;
+ peerstats.prefix = &statsdir[0];
+ peerstats.basename = (char*)emalloc(strlen(PEERNAME)+1);
+ strcpy(peerstats.basename, PEERNAME);
+ peerstats.id = 0;
+ peerstats.type = FILEGEN_DAY;
+ peerstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("peerstats", &peerstats);
+
+ loopstats.fp = NULL;
+ loopstats.prefix = &statsdir[0];
+ loopstats.basename = (char*)emalloc(strlen(LOOPNAME)+1);
+ strcpy(loopstats.basename, LOOPNAME);
+ loopstats.id = 0;
+ loopstats.type = FILEGEN_DAY;
+ loopstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("loopstats", &loopstats);
+
+ clockstats.fp = NULL;
+ clockstats.prefix = &statsdir[0];
+ clockstats.basename = (char*)emalloc(strlen(CLOCKNAME)+1);
+ strcpy(clockstats.basename, CLOCKNAME);
+ clockstats.id = 0;
+ clockstats.type = FILEGEN_DAY;
+ clockstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("clockstats", &clockstats);
+
+ rawstats.fp = NULL;
+ rawstats.prefix = &statsdir[0];
+ rawstats.basename = (char*)emalloc(strlen(RAWNAME)+1);
+ strcpy(rawstats.basename, RAWNAME);
+ rawstats.id = 0;
+ rawstats.type = FILEGEN_DAY;
+ rawstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("rawstats", &rawstats);
+
+ sysstats.fp = NULL;
+ sysstats.prefix = &statsdir[0];
+ sysstats.basename = (char*)emalloc(strlen(STANAME)+1);
+ strcpy(sysstats.basename, STANAME);
+ sysstats.id = 0;
+ sysstats.type = FILEGEN_DAY;
+ sysstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("sysstats", &sysstats);
+
+#ifdef OPENSSL
+ cryptostats.fp = NULL;
+ cryptostats.prefix = &statsdir[0];
+ cryptostats.basename = (char*)emalloc(strlen(CRYPTONAME)+1);
+ strcpy(cryptostats.basename, CRYPTONAME);
+ cryptostats.id = 0;
+ cryptostats.type = FILEGEN_DAY;
+ cryptostats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("cryptostats", &cryptostats);
+#endif /* OPENSSL */
+
+#undef PEERNAME
+#undef LOOPNAME
+#undef CLOCKNAME
+#undef RAWNAME
+#undef STANAME
+#ifdef OPENSSL
+#undef CRYPTONAME
+#endif /* OPENSSL */
+}
+
+
+/*
+ * hourly_stats - print some interesting stats
+ */
+void
+hourly_stats(void)
+{
+ FILE *fp;
+
+#ifdef DOSYNCTODR
+ struct timeval tv;
+#if !defined(VMS)
+ int prio_set;
+#endif
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ int o_prio;
+
+ /*
+ * Sometimes having a Sun can be a drag.
+ *
+ * The kernel variable dosynctodr controls whether the system's
+ * soft clock is kept in sync with the battery clock. If it
+ * is zero, then the soft clock is not synced, and the battery
+ * clock is simply left to rot. That means that when the system
+ * reboots, the battery clock (which has probably gone wacky)
+ * sets the soft clock. That means ntpd starts off with a very
+ * confused idea of what time it is. It then takes a large
+ * amount of time to figure out just how wacky the battery clock
+ * has made things drift, etc, etc. The solution is to make the
+ * battery clock sync up to system time. The way to do THAT is
+ * to simply set the time of day to the current time of day, but
+ * as quickly as possible. This may, or may not be a sensible
+ * thing to do.
+ *
+ * CAVEAT: settimeofday() steps the sun clock by about 800 us,
+ * so setting DOSYNCTODR seems a bad idea in the
+ * case of us resolution
+ */
+
+#if !defined(VMS)
+ /* (prr) getpriority returns -1 on error, but -1 is also a valid
+ * return value (!), so instead we have to zero errno before the
+ * call and check it for non-zero afterwards.
+ */
+ errno = 0;
+ prio_set = 0;
+ o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
+
+ /*
+ * (prr) if getpriority succeeded, call setpriority to raise
+ * scheduling priority as high as possible. If that succeeds
+ * as well, set the prio_set flag so we remember to reset
+ * priority to its previous value below. Note that on Solaris
+ * 2.6 (and beyond?), both getpriority and setpriority will fail
+ * with ESRCH, because sched_setscheduler (called from main) put
+ * us in the real-time scheduling class which setpriority
+ * doesn't know about. Being in the real-time class is better
+ * than anything setpriority can do, anyhow, so this error is
+ * silently ignored.
+ */
+ if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
+ prio_set = 1; /* overdrive */
+#endif /* VMS */
+#ifdef HAVE_GETCLOCK
+ (void) getclock(TIMEOFDAY, &ts);
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = ts.tv_nsec / 1000;
+#else /* not HAVE_GETCLOCK */
+ GETTIMEOFDAY(&tv,(struct timezone *)NULL);
+#endif /* not HAVE_GETCLOCK */
+ if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) {
+ msyslog(LOG_ERR, "can't sync battery time: %m");
+ }
+#if !defined(VMS)
+ if (prio_set)
+ setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
+#endif /* VMS */
+#endif /* DOSYNCTODR */
+
+ NLOG(NLOG_SYSSTATIST)
+ msyslog(LOG_INFO,
+ "offset %.6f sec freq %.3f ppm error %.6f poll %d",
+ last_offset, drift_comp * 1e6, sys_jitter,
+ sys_poll);
+
+
+ record_sys_stats();
+ if (stats_drift_file != 0) {
+ if ((fp = fopen(stats_temp_file, "w")) == NULL) {
+ msyslog(LOG_ERR, "can't open %s: %m",
+ stats_temp_file);
+ return;
+ }
+ fprintf(fp, "%.3f\n", drift_comp * 1e6);
+ (void)fclose(fp);
+ /* atomic */
+#ifdef SYS_WINNT
+ (void) _unlink(stats_drift_file); /* rename semantics differ under NT */
+#endif /* SYS_WINNT */
+
+#ifndef NO_RENAME
+ (void) rename(stats_temp_file, stats_drift_file);
+#else
+ /* we have no rename NFS of ftp in use*/
+ if ((fp = fopen(stats_drift_file, "w")) == NULL) {
+ msyslog(LOG_ERR, "can't open %s: %m",
+ stats_drift_file);
+ return;
+ }
+
+#endif
+
+#if defined(VMS)
+ /* PURGE */
+ {
+ $DESCRIPTOR(oldvers,";-1");
+ struct dsc$descriptor driftdsc = {
+ strlen(stats_drift_file),0,0,stats_drift_file };
+
+ while(lib$delete_file(&oldvers,&driftdsc) & 1) ;
+ }
+#endif
+ }
+}
+
+
+/*
+ * stats_config - configure the stats operation
+ */
+void
+stats_config(
+ int item,
+ char *invalue /* only one type so far */
+ )
+{
+ FILE *fp;
+ char *value;
+ double old_drift;
+ int len;
+
+ /*
+ * Expand environment strings under Windows NT, since the
+ * command interpreter doesn't do this, the program must.
+ */
+#ifdef SYS_WINNT
+ char newvalue[MAX_PATH], parameter[MAX_PATH];
+
+ if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
+ switch(item) {
+ case STATS_FREQ_FILE:
+ strcpy(parameter,"STATS_FREQ_FILE");
+ break;
+ case STATS_STATSDIR:
+ strcpy(parameter,"STATS_STATSDIR");
+ break;
+ case STATS_PID_FILE:
+ strcpy(parameter,"STATS_PID_FILE");
+ break;
+ default:
+ strcpy(parameter,"UNKNOWN");
+ break;
+ }
+ value = invalue;
+
+ msyslog(LOG_ERR,
+ "ExpandEnvironmentStrings(%s) failed: %m\n", parameter);
+ } else {
+ value = newvalue;
+ }
+#else
+ value = invalue;
+#endif /* SYS_WINNT */
+
+ switch(item) {
+ case STATS_FREQ_FILE:
+ if (stats_drift_file != 0) {
+ (void) free(stats_drift_file);
+ (void) free(stats_temp_file);
+ stats_drift_file = 0;
+ stats_temp_file = 0;
+ }
+
+ if (value == 0 || (len = strlen(value)) == 0)
+ break;
+
+ stats_drift_file = (char*)emalloc((u_int)(len + 1));
+#if !defined(VMS)
+ stats_temp_file = (char*)emalloc((u_int)(len +
+ sizeof(".TEMP")));
+#else
+ stats_temp_file = (char*)emalloc((u_int)(len +
+ sizeof("-TEMP")));
+#endif /* VMS */
+ memmove(stats_drift_file, value, (unsigned)(len+1));
+ memmove(stats_temp_file, value, (unsigned)len);
+#if !defined(VMS)
+ memmove(stats_temp_file + len, ".TEMP",
+ sizeof(".TEMP"));
+#else
+ memmove(stats_temp_file + len, "-TEMP",
+ sizeof("-TEMP"));
+#endif /* VMS */
+
+ /*
+ * Open drift file and read frequency. If the file is
+ * missing or contains errors, tell the loop to reset.
+ */
+ if ((fp = fopen(stats_drift_file, "r")) == NULL) {
+ loop_config(LOOP_DRIFTCOMP, 1e9);
+ break;
+ }
+ if (fscanf(fp, "%lf", &old_drift) != 1) {
+ msyslog(LOG_ERR, "Frequency format error in %s",
+ stats_drift_file);
+ loop_config(LOOP_DRIFTCOMP, 1e9);
+ fclose(fp);
+ break;
+ }
+ fclose(fp);
+ msyslog(LOG_INFO,
+ "frequency initialized %.3f PPM from %s",
+ old_drift, stats_drift_file);
+ loop_config(LOOP_DRIFTCOMP, old_drift / 1e6);
+ break;
+
+ case STATS_STATSDIR:
+ if (strlen(value) >= sizeof(statsdir)) {
+ msyslog(LOG_ERR,
+ "value for statsdir too long (>%d, sigh)",
+ (int)sizeof(statsdir)-1);
+ } else {
+ l_fp now;
+
+ get_systime(&now);
+ strcpy(statsdir,value);
+ if(peerstats.prefix == &statsdir[0] &&
+ peerstats.fp != NULL) {
+ fclose(peerstats.fp);
+ peerstats.fp = NULL;
+ filegen_setup(&peerstats, now.l_ui);
+ }
+ if(loopstats.prefix == &statsdir[0] &&
+ loopstats.fp != NULL) {
+ fclose(loopstats.fp);
+ loopstats.fp = NULL;
+ filegen_setup(&loopstats, now.l_ui);
+ }
+ if(clockstats.prefix == &statsdir[0] &&
+ clockstats.fp != NULL) {
+ fclose(clockstats.fp);
+ clockstats.fp = NULL;
+ filegen_setup(&clockstats, now.l_ui);
+ }
+ if(rawstats.prefix == &statsdir[0] &&
+ rawstats.fp != NULL) {
+ fclose(rawstats.fp);
+ rawstats.fp = NULL;
+ filegen_setup(&rawstats, now.l_ui);
+ }
+ if(sysstats.prefix == &statsdir[0] &&
+ sysstats.fp != NULL) {
+ fclose(sysstats.fp);
+ sysstats.fp = NULL;
+ filegen_setup(&sysstats, now.l_ui);
+ }
+#ifdef OPENSSL
+ if(cryptostats.prefix == &statsdir[0] &&
+ cryptostats.fp != NULL) {
+ fclose(cryptostats.fp);
+ cryptostats.fp = NULL;
+ filegen_setup(&cryptostats, now.l_ui);
+ }
+#endif /* OPENSSL */
+ }
+ break;
+
+ case STATS_PID_FILE:
+ if ((fp = fopen(value, "w")) == NULL) {
+ msyslog(LOG_ERR, "Can't open %s: %m", value);
+ break;
+ }
+ fprintf(fp, "%d", (int) getpid());
+ fclose(fp);;
+ break;
+
+ default:
+ /* oh well */
+ break;
+ }
+}
+
+/*
+ * record_peer_stats - write peer statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past UTC midnight)
+ * peer (ip address)
+ * peer status word (hex)
+ * peer offset (s)
+ * peer delay (s)
+ * peer error bound (s)
+ * peer error (s)
+*/
+void
+record_peer_stats(
+ struct sockaddr_storage *addr,
+ int status,
+ double offset,
+ double delay,
+ double dispersion,
+ double skew
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&peerstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (peerstats.fp != NULL) {
+ fprintf(peerstats.fp,
+ "%lu %s %s %x %.9f %.9f %.9f %.9f\n",
+ day, ulfptoa(&now, 3), stoa(addr), status, offset,
+ delay, dispersion, skew);
+ fflush(peerstats.fp);
+ }
+}
+/*
+ * record_loop_stats - write loop filter statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * offset (s)
+ * frequency (approx ppm)
+ * time constant (log base 2)
+ */
+void
+record_loop_stats(
+ double offset,
+ double freq,
+ double jitter,
+ double stability,
+ int spoll
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&loopstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (loopstats.fp != NULL) {
+ fprintf(loopstats.fp, "%lu %s %.9f %.6f %.9f %.6f %d\n",
+ day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
+ stability * 1e6, spoll);
+ fflush(loopstats.fp);
+ }
+}
+
+/*
+ * record_clock_stats - write clock statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * peer (ip address)
+ * text message
+ */
+void
+record_clock_stats(
+ struct sockaddr_storage *addr,
+ const char *text
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&clockstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (clockstats.fp != NULL) {
+ fprintf(clockstats.fp, "%lu %s %s %s\n",
+ day, ulfptoa(&now, 3), stoa(addr), text);
+ fflush(clockstats.fp);
+ }
+}
+
+/*
+ * record_raw_stats - write raw timestamps to file
+ *
+ *
+ * file format
+ * time (s past midnight)
+ * peer ip address
+ * local ip address
+ * t1 t2 t3 t4 timestamps
+ */
+void
+record_raw_stats(
+ struct sockaddr_storage *srcadr,
+ struct sockaddr_storage *dstadr,
+ l_fp *t1,
+ l_fp *t2,
+ l_fp *t3,
+ l_fp *t4
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&rawstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (rawstats.fp != NULL) {
+ fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s\n",
+ day, ulfptoa(&now, 3), stoa(srcadr), stoa(dstadr),
+ ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9),
+ ulfptoa(t4, 9));
+ fflush(rawstats.fp);
+ }
+}
+
+
+/*
+ * record_sys_stats - write system statistics to file
+ *
+ * file format
+ * time (s past midnight)
+ * time since startup (hr)
+ * packets recieved
+ * packets processed
+ * current version
+ * previous version
+ * bad version
+ * access denied
+ * bad length or format
+ * bad authentication
+ * rate exceeded
+ */
+void
+record_sys_stats(void)
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&sysstats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (sysstats.fp != NULL) {
+ fprintf(sysstats.fp,
+ "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ day, ulfptoa(&now, 3), sys_stattime / 3600,
+ sys_received, sys_processed, sys_newversionpkt,
+ sys_oldversionpkt, sys_unknownversion,
+ sys_restricted, sys_badlength, sys_badauth,
+ sys_limitrejected);
+ fflush(sysstats.fp);
+ proto_clr_stats();
+ }
+}
+
+
+#ifdef OPENSSL
+/*
+ * record_crypto_stats - write crypto statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * peer (ip address)
+ * text message
+ */
+void
+record_crypto_stats(
+ struct sockaddr_storage *addr,
+ const char *text
+ )
+{
+ l_fp now;
+ u_long day;
+
+ if (!stats_control)
+ return;
+
+ get_systime(&now);
+ filegen_setup(&cryptostats, now.l_ui);
+ day = now.l_ui / 86400 + MJD_1900;
+ now.l_ui %= 86400;
+ if (cryptostats.fp != NULL) {
+ if (addr == NULL)
+ fprintf(cryptostats.fp, "%lu %s %s\n",
+ day, ulfptoa(&now, 3), text);
+ else
+ fprintf(cryptostats.fp, "%lu %s %s %s\n",
+ day, ulfptoa(&now, 3), stoa(addr), text);
+ fflush(cryptostats.fp);
+ }
+}
+#endif /* OPENSSL */
+
+
+/*
+ * getauthkeys - read the authentication keys from the specified file
+ */
+void
+getauthkeys(
+ char *keyfile
+ )
+{
+ int len;
+
+ len = strlen(keyfile);
+ if (len == 0)
+ return;
+
+ if (key_file_name != 0) {
+ if (len > (int)strlen(key_file_name)) {
+ (void) free(key_file_name);
+ key_file_name = 0;
+ }
+ }
+
+ if (key_file_name == 0) {
+#ifndef SYS_WINNT
+ key_file_name = (char*)emalloc((u_int) (len + 1));
+#else
+ key_file_name = (char*)emalloc((u_int) (MAXPATHLEN));
+#endif
+ }
+#ifndef SYS_WINNT
+ memmove(key_file_name, keyfile, (unsigned)(len+1));
+#else
+ if (!ExpandEnvironmentStrings(keyfile, key_file_name, MAXPATHLEN))
+ {
+ msyslog(LOG_ERR,
+ "ExpandEnvironmentStrings(KEY_FILE) failed: %m\n");
+ }
+#endif /* SYS_WINNT */
+
+ authreadkeys(key_file_name);
+}
+
+
+/*
+ * rereadkeys - read the authentication key file over again.
+ */
+void
+rereadkeys(void)
+{
+ if (key_file_name != 0)
+ authreadkeys(key_file_name);
+}
+
+/*
+ * sock_hash - hash an sockaddr_storage structure
+ */
+int
+sock_hash(
+ struct sockaddr_storage *addr
+ )
+{
+ int hashVal;
+ int i;
+ int len;
+ char *ch;
+
+ hashVal = 0;
+ len = 0;
+ /*
+ * We can't just hash the whole thing because there are hidden
+ * fields in sockaddr_in6 that might be filled in by recvfrom(),
+ * so just use the family, port and address.
+ */
+ ch = (char *)&addr->ss_family;
+ hashVal = 37 * hashVal + (int)*ch;
+ if (sizeof(addr->ss_family) > 1) {
+ ch++;
+ hashVal = 37 * hashVal + (int)*ch;
+ }
+ switch(addr->ss_family) {
+ case AF_INET:
+ ch = (char *)&((struct sockaddr_in *)addr)->sin_addr;
+ len = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ ch = (char *)&((struct sockaddr_in6 *)addr)->sin6_addr;
+ len = sizeof(struct in6_addr);
+ break;
+ }
+
+ for (i = 0; i < len ; i++)
+ hashVal = 37 * hashVal + (int)*(ch + i);
+
+ hashVal = hashVal % 128; /* % MON_HASH_SIZE hardcoded */
+
+ if (hashVal < 0)
+ hashVal += 128;
+
+ return hashVal;
+}
diff --git a/ntpd/ntpd.c b/ntpd/ntpd.c
new file mode 100644
index 0000000..0b05253
--- /dev/null
+++ b/ntpd/ntpd.c
@@ -0,0 +1,1274 @@
+/*
+ * ntpd.c - main program for the fixed point NTP daemon
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+#ifdef SIM
+#include "ntpsim.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <stdio.h>
+#ifndef SYS_WINNT
+# if !defined(VMS) /*wjm*/
+# ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+# endif
+# endif /* VMS */
+# ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+# else
+# include <signal.h>
+# endif
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif /* HAVE_SYS_IOCTL_H */
+# ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+# endif /* HAVE_SYS_RESOURCE_H */
+#else
+# include <signal.h>
+# include <process.h>
+# include <io.h>
+# include "../libntp/log.h"
+# include <clockstuff.h>
+# include <crtdbg.h>
+#endif /* SYS_WINNT */
+#if defined(HAVE_RTPRIO)
+# ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+# endif
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# include <sys/rtprio.h>
+#else
+# ifdef HAVE_PLOCK
+# ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SCHED_SETSCHEDULER)
+# ifdef HAVE_SCHED_H
+# include <sched.h>
+# else
+# ifdef HAVE_SYS_SCHED_H
+# include <sys/sched.h>
+# endif
+# endif
+#endif
+#if defined(HAVE_SYS_MMAN_H)
+# include <sys/mman.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+#ifdef SYS_DOMAINOS
+# include <apollo/base.h>
+#endif /* SYS_DOMAINOS */
+
+#include "recvbuff.h"
+#include "ntp_cmdargs.h"
+
+#if 0 /* HMS: I don't think we need this. 961223 */
+#ifdef LOCK_PROCESS
+# ifdef SYS_SOLARIS
+# include <sys/mman.h>
+# else
+# include <sys/lock.h>
+# endif
+#endif
+#endif
+
+#ifdef _AIX
+# include <ulimit.h>
+#endif /* _AIX */
+
+#ifdef SCO5_CLOCK
+# include <sys/ci/ciioctl.h>
+#endif
+
+#ifdef HAVE_CLOCKCTL
+# include <ctype.h>
+# include <grp.h>
+# include <pwd.h>
+#endif
+
+/*
+ * Signals we catch for debugging. If not debugging we ignore them.
+ */
+#define MOREDEBUGSIG SIGUSR1
+#define LESSDEBUGSIG SIGUSR2
+
+/*
+ * Signals which terminate us gracefully.
+ */
+#ifndef SYS_WINNT
+# define SIGDIE1 SIGHUP
+# define SIGDIE3 SIGQUIT
+# define SIGDIE2 SIGINT
+# define SIGDIE4 SIGTERM
+#endif /* SYS_WINNT */
+
+#if defined SYS_WINNT
+/* handles for various threads, process, and objects */
+HANDLE ResolverThreadHandle = NULL;
+/* variables used to inform the Service Control Manager of our current state */
+BOOL NoWinService = FALSE;
+SERVICE_STATUS ssStatus;
+SERVICE_STATUS_HANDLE sshStatusHandle;
+HANDLE WaitHandles[3] = { NULL, NULL, NULL };
+char szMsgPath[255];
+static BOOL WINAPI OnConsoleEvent(DWORD dwCtrlType);
+BOOL init_randfile();
+#endif /* SYS_WINNT */
+
+/*
+ * Scheduling priority we run at
+ */
+#define NTPD_PRIO (-12)
+
+int priority_done = 2; /* 0 - Set priority */
+ /* 1 - priority is OK where it is */
+ /* 2 - Don't set priority */
+ /* 1 and 2 are pretty much the same */
+
+/*
+ * Debugging flag
+ */
+volatile int debug;
+
+/*
+ * Set the processing not to be in the forground
+ */
+int forground_process = FALSE;
+
+/*
+ * No-fork flag. If set, we do not become a background daemon.
+ */
+int nofork;
+
+#ifdef HAVE_CLOCKCTL
+char *user = NULL; /* User to switch to */
+char *group = NULL; /* group to switch to */
+char *chrootdir = NULL; /* directory to chroot to */
+int sw_uid;
+int sw_gid;
+char *endp;
+struct group *gr;
+struct passwd *pw;
+#endif /* HAVE_CLOCKCTL */
+
+/*
+ * Initializing flag. All async routines watch this and only do their
+ * thing when it is clear.
+ */
+int initializing;
+
+/*
+ * Version declaration
+ */
+extern const char *Version;
+
+int was_alarmed;
+
+#ifdef DECL_SYSCALL
+/*
+ * We put this here, since the argument profile is syscall-specific
+ */
+extern int syscall P((int, ...));
+#endif /* DECL_SYSCALL */
+
+
+#ifdef SIGDIE2
+static RETSIGTYPE finish P((int));
+#endif /* SIGDIE2 */
+
+#ifdef DEBUG
+#ifndef SYS_WINNT
+static RETSIGTYPE moredebug P((int));
+static RETSIGTYPE lessdebug P((int));
+#endif
+#else /* not DEBUG */
+static RETSIGTYPE no_debug P((int));
+#endif /* not DEBUG */
+
+int ntpdmain P((int, char **));
+static void set_process_priority P((void));
+
+#ifdef SIM
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ return ntpsim(argc, argv);
+}
+#else /* SIM */
+#ifdef NO_MAIN_ALLOWED
+CALL(ntpd,"ntpd",ntpdmain);
+#else
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ return ntpdmain(argc, argv);
+}
+#endif
+#endif /* SIM */
+
+#ifdef _AIX
+/*
+ * OK. AIX is different than solaris in how it implements plock().
+ * If you do NOT adjust the stack limit, you will get the MAXIMUM
+ * stack size allocated and PINNED with you program. To check the
+ * value, use ulimit -a.
+ *
+ * To fix this, we create an automatic variable and set our stack limit
+ * to that PLUS 32KB of extra space (we need some headroom).
+ *
+ * This subroutine gets the stack address.
+ *
+ * Grover Davidson and Matt Ladendorf
+ *
+ */
+static char *
+get_aix_stack(void)
+{
+ char ch;
+ return (&ch);
+}
+
+/*
+ * Signal handler for SIGDANGER.
+ */
+static void
+catch_danger(int signo)
+{
+ msyslog(LOG_INFO, "ntpd: setpgid(): %m");
+ /* Make the system believe we'll free something, but don't do it! */
+ return;
+}
+#endif /* _AIX */
+
+/*
+ * Set the process priority
+ */
+static void
+set_process_priority(void)
+{
+
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_DEBUG, "set_process_priority: %s: priority_done is <%d>",
+ ((priority_done)
+ ? "Leave priority alone"
+ : "Attempt to set priority"
+ ),
+ priority_done);
+#endif /* DEBUG */
+
+#ifdef SYS_WINNT
+ priority_done += NT_set_process_priority();
+#endif
+
+#if defined(HAVE_SCHED_SETSCHEDULER)
+ if (!priority_done) {
+ extern int config_priority_override, config_priority;
+ int pmax, pmin;
+ struct sched_param sched;
+
+ pmax = sched_get_priority_max(SCHED_FIFO);
+ sched.sched_priority = pmax;
+ if ( config_priority_override ) {
+ pmin = sched_get_priority_min(SCHED_FIFO);
+ if ( config_priority > pmax )
+ sched.sched_priority = pmax;
+ else if ( config_priority < pmin )
+ sched.sched_priority = pmin;
+ else
+ sched.sched_priority = config_priority;
+ }
+ if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 )
+ msyslog(LOG_ERR, "sched_setscheduler(): %m");
+ else
+ ++priority_done;
+ }
+#endif /* HAVE_SCHED_SETSCHEDULER */
+#if defined(HAVE_RTPRIO)
+# ifdef RTP_SET
+ if (!priority_done) {
+ struct rtprio srtp;
+
+ srtp.type = RTP_PRIO_REALTIME; /* was: RTP_PRIO_NORMAL */
+ srtp.prio = 0; /* 0 (hi) -> RTP_PRIO_MAX (31,lo) */
+
+ if (rtprio(RTP_SET, getpid(), &srtp) < 0)
+ msyslog(LOG_ERR, "rtprio() error: %m");
+ else
+ ++priority_done;
+ }
+# else /* not RTP_SET */
+ if (!priority_done) {
+ if (rtprio(0, 120) < 0)
+ msyslog(LOG_ERR, "rtprio() error: %m");
+ else
+ ++priority_done;
+ }
+# endif /* not RTP_SET */
+#endif /* HAVE_RTPRIO */
+#if defined(NTPD_PRIO) && NTPD_PRIO != 0
+# ifdef HAVE_ATT_NICE
+ if (!priority_done) {
+ errno = 0;
+ if (-1 == nice (NTPD_PRIO) && errno != 0)
+ msyslog(LOG_ERR, "nice() error: %m");
+ else
+ ++priority_done;
+ }
+# endif /* HAVE_ATT_NICE */
+# ifdef HAVE_BSD_NICE
+ if (!priority_done) {
+ if (-1 == setpriority(PRIO_PROCESS, 0, NTPD_PRIO))
+ msyslog(LOG_ERR, "setpriority() error: %m");
+ else
+ ++priority_done;
+ }
+# endif /* HAVE_BSD_NICE */
+#endif /* NTPD_PRIO && NTPD_PRIO != 0 */
+ if (!priority_done)
+ msyslog(LOG_ERR, "set_process_priority: No way found to improve our priority");
+}
+
+
+/*
+ * Main program. Initialize us, disconnect us from the tty if necessary,
+ * and loop waiting for I/O and/or timer expiries.
+ */
+int
+ntpdmain(
+ int argc,
+ char *argv[]
+ )
+{
+ l_fp now;
+ char *cp;
+ struct recvbuf *rbuflist;
+ struct recvbuf *rbuf;
+#ifdef _AIX /* HMS: ifdef SIGDANGER? */
+ struct sigaction sa;
+#endif
+
+ initializing = 1; /* mark that we are initializing */
+ debug = 0; /* no debugging by default */
+ nofork = 0; /* will fork by default */
+
+#ifdef HAVE_UMASK
+ {
+ mode_t uv;
+
+ uv = umask(0);
+ if(uv)
+ (void) umask(uv);
+ else
+ (void) umask(022);
+ }
+#endif
+
+#if defined(HAVE_GETUID) && !defined(MPE) /* MPE lacks the concept of root */
+ {
+ uid_t uid;
+
+ uid = getuid();
+ if (uid)
+ {
+ msyslog(LOG_ERR, "ntpd: must be run as root, not uid %ld", (long)uid);
+ exit(1);
+ }
+ }
+#endif
+
+#ifdef SYS_WINNT
+ /* Set the Event-ID message-file name. */
+ if (!GetModuleFileName(NULL, szMsgPath, sizeof(szMsgPath))) {
+ msyslog(LOG_ERR, "GetModuleFileName(PGM_EXE_FILE) failed: %m\n");
+ exit(1);
+ }
+ addSourceToRegistry("NTP", szMsgPath);
+#endif
+ getstartup(argc, argv); /* startup configuration, may set debug */
+
+ if (debug)
+ printf("%s\n", Version);
+
+ /*
+ * Initialize random generator and public key pair
+ */
+#ifdef SYS_WINNT
+ /* Initialize random file before OpenSSL checks */
+ if(!init_randfile())
+ msyslog(LOG_ERR, "Unable to initialize .rnd file\n");
+#endif
+ get_systime(&now);
+ SRANDOM((int)(now.l_i * now.l_uf));
+
+#if !defined(VMS)
+# ifndef NODETACH
+ /*
+ * Detach us from the terminal. May need an #ifndef GIZMO.
+ */
+# ifdef DEBUG
+ if (!debug && !nofork)
+# else /* DEBUG */
+ if (!nofork)
+# endif /* DEBUG */
+ {
+# ifndef SYS_WINNT
+# ifdef HAVE_DAEMON
+ daemon(0, 0);
+# else /* not HAVE_DAEMON */
+ if (fork()) /* HMS: What about a -1? */
+ exit(0);
+
+ {
+#if !defined(F_CLOSEM)
+ u_long s;
+ int max_fd;
+#endif /* not F_CLOSEM */
+
+#if defined(F_CLOSEM)
+ /*
+ * From 'Writing Reliable AIX Daemons,' SG24-4946-00,
+ * by Eric Agar (saves us from doing 32767 system
+ * calls)
+ */
+ if (fcntl(0, F_CLOSEM, 0) == -1)
+ msyslog(LOG_ERR, "ntpd: failed to close open files(): %m");
+#else /* not F_CLOSEM */
+
+# if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
+ max_fd = sysconf(_SC_OPEN_MAX);
+# else /* HAVE_SYSCONF && _SC_OPEN_MAX */
+ max_fd = getdtablesize();
+# endif /* HAVE_SYSCONF && _SC_OPEN_MAX */
+ for (s = 0; s < max_fd; s++)
+ (void) close((int)s);
+#endif /* not F_CLOSEM */
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+#ifdef SYS_DOMAINOS
+ {
+ uid_$t puid;
+ status_$t st;
+
+ proc2_$who_am_i(&puid);
+ proc2_$make_server(&puid, &st);
+ }
+#endif /* SYS_DOMAINOS */
+#if defined(HAVE_SETPGID) || defined(HAVE_SETSID)
+# ifdef HAVE_SETSID
+ if (setsid() == (pid_t)-1)
+ msyslog(LOG_ERR, "ntpd: setsid(): %m");
+# else
+ if (setpgid(0, 0) == -1)
+ msyslog(LOG_ERR, "ntpd: setpgid(): %m");
+# endif
+#else /* HAVE_SETPGID || HAVE_SETSID */
+ {
+# if defined(TIOCNOTTY)
+ int fid;
+
+ fid = open("/dev/tty", 2);
+ if (fid >= 0)
+ {
+ (void) ioctl(fid, (u_long) TIOCNOTTY, (char *) 0);
+ (void) close(fid);
+ }
+# endif /* defined(TIOCNOTTY) */
+# ifdef HAVE_SETPGRP_0
+ (void) setpgrp();
+# else /* HAVE_SETPGRP_0 */
+ (void) setpgrp(0, getpid());
+# endif /* HAVE_SETPGRP_0 */
+ }
+#endif /* HAVE_SETPGID || HAVE_SETSID */
+#ifdef _AIX
+ /* Don't get killed by low-on-memory signal. */
+ sa.sa_handler = catch_danger;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ (void) sigaction(SIGDANGER, &sa, NULL);
+#endif /* _AIX */
+ }
+# endif /* not HAVE_DAEMON */
+# else /* SYS_WINNT */
+
+ {
+ if (NoWinService == FALSE) {
+ SERVICE_TABLE_ENTRY dispatchTable[] = {
+ { TEXT("NetworkTimeProtocol"), (LPSERVICE_MAIN_FUNCTION)service_main },
+ { NULL, NULL }
+ };
+
+ /* daemonize */
+ if (!StartServiceCtrlDispatcher(dispatchTable))
+ {
+ msyslog(LOG_ERR, "StartServiceCtrlDispatcher: %m");
+ ExitProcess(2);
+ }
+ }
+ else {
+ service_main(argc, argv);
+ return 0;
+ }
+ }
+# endif /* SYS_WINNT */
+ }
+# endif /* NODETACH */
+# if defined(SYS_WINNT) && !defined(NODETACH)
+ else
+ service_main(argc, argv);
+ return 0; /* must return a value */
+} /* end main */
+
+/*
+ * If this runs as a service under NT, the main thread will block at
+ * StartServiceCtrlDispatcher() and another thread will be started by the
+ * Service Control Dispatcher which will begin execution at the routine
+ * specified in that call (viz. service_main)
+ */
+void
+service_main(
+ DWORD argc,
+ LPTSTR *argv
+ )
+{
+ char *cp;
+ struct recvbuf *rbuflist;
+ struct recvbuf *rbuf;
+
+ if(!debug && NoWinService == FALSE)
+ {
+ /* register our service control handler */
+ sshStatusHandle = RegisterServiceCtrlHandler( TEXT("NetworkTimeProtocol"),
+ (LPHANDLER_FUNCTION)service_ctrl);
+ if(sshStatusHandle == 0)
+ {
+ msyslog(LOG_ERR, "RegisterServiceCtrlHandler failed: %m");
+ return;
+ }
+
+ /* report pending status to Service Control Manager */
+ ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ ssStatus.dwCurrentState = SERVICE_START_PENDING;
+ ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+ ssStatus.dwWin32ExitCode = NO_ERROR;
+ ssStatus.dwServiceSpecificExitCode = 0;
+ ssStatus.dwCheckPoint = 1;
+ ssStatus.dwWaitHint = 5000;
+ if (!SetServiceStatus(sshStatusHandle, &ssStatus))
+ {
+ msyslog(LOG_ERR, "SetServiceStatus: %m");
+ ssStatus.dwCurrentState = SERVICE_STOPPED;
+ SetServiceStatus(sshStatusHandle, &ssStatus);
+ return;
+ }
+
+ } /* debug */
+# endif /* defined(SYS_WINNT) && !defined(NODETACH) */
+#endif /* VMS */
+
+ /*
+ * Logging. This may actually work on the gizmo board. Find a name
+ * to log with by using the basename of argv[0]
+ */
+ cp = strrchr(argv[0], '/');
+ if (cp == 0)
+ cp = argv[0];
+ else
+ cp++;
+
+ debug = 0; /* will be immediately re-initialized 8-( */
+ getstartup(argc, argv); /* startup configuration, catch logfile this time */
+
+#if !defined(VMS)
+
+# ifndef LOG_DAEMON
+ openlog(cp, LOG_PID);
+# else /* LOG_DAEMON */
+
+# ifndef LOG_NTP
+# define LOG_NTP LOG_DAEMON
+# endif
+ openlog(cp, LOG_PID | LOG_NDELAY, LOG_NTP);
+# ifdef DEBUG
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ else
+# endif /* DEBUG */
+ setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
+# endif /* LOG_DAEMON */
+#endif /* !SYS_WINNT && !VMS */
+
+ NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "%s", Version);
+
+#ifdef SYS_WINNT
+ /* GMS 1/18/1997
+ * TODO: lock the process in memory using SetProcessWorkingSetSize() and VirtualLock() functions
+ *
+ process_handle = GetCurrentProcess();
+ if (SetProcessWorkingSetSize(process_handle, 2097152 , 4194304 ) == TRUE) {
+ if (VirtualLock(0 , 4194304) == FALSE)
+ msyslog(LOG_ERR, "VirtualLock() failed: %m");
+ } else {
+ msyslog(LOG_ERR, "SetProcessWorkingSetSize() failed: %m");
+ }
+ */
+#endif /* SYS_WINNT */
+
+#ifdef SCO5_CLOCK
+ /*
+ * SCO OpenServer's system clock offers much more precise timekeeping
+ * on the base CPU than the other CPUs (for multiprocessor systems),
+ * so we must lock to the base CPU.
+ */
+ {
+ int fd = open("/dev/at1", O_RDONLY);
+ if (fd >= 0) {
+ int zero = 0;
+ if (ioctl(fd, ACPU_LOCK, &zero) < 0)
+ msyslog(LOG_ERR, "cannot lock to base CPU: %m\n");
+ close( fd );
+ } /* else ...
+ * If we can't open the device, this probably just isn't
+ * a multiprocessor system, so we're A-OK.
+ */
+ }
+#endif
+
+#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && defined(MCL_FUTURE)
+# ifdef HAVE_SETRLIMIT
+ /*
+ * Set the stack limit to something smaller, so that we don't lock a lot
+ * of unused stack memory.
+ */
+ {
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_STACK, &rl) != -1
+ && (rl.rlim_cur = 20 * 4096) < rl.rlim_max)
+ {
+ if (setrlimit(RLIMIT_STACK, &rl) == -1)
+ {
+ msyslog(LOG_ERR,
+ "Cannot adjust stack limit for mlockall: %m");
+ }
+ }
+ }
+# endif /* HAVE_SETRLIMIT */
+ /*
+ * lock the process into memory
+ */
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0)
+ msyslog(LOG_ERR, "mlockall(): %m");
+#else /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */
+# ifdef HAVE_PLOCK
+# ifdef PROCLOCK
+# ifdef _AIX
+ /*
+ * set the stack limit for AIX for plock().
+ * see get_aix_stack for more info.
+ */
+ if (ulimit(SET_STACKLIM, (get_aix_stack() - 8*4096)) < 0)
+ {
+ msyslog(LOG_ERR,"Cannot adjust stack limit for plock on AIX: %m");
+ }
+# endif /* _AIX */
+ /*
+ * lock the process into memory
+ */
+ if (plock(PROCLOCK) < 0)
+ msyslog(LOG_ERR, "plock(PROCLOCK): %m");
+# else /* not PROCLOCK */
+# ifdef TXTLOCK
+ /*
+ * Lock text into ram
+ */
+ if (plock(TXTLOCK) < 0)
+ msyslog(LOG_ERR, "plock(TXTLOCK) error: %m");
+# else /* not TXTLOCK */
+ msyslog(LOG_ERR, "plock() - don't know what to lock!");
+# endif /* not TXTLOCK */
+# endif /* not PROCLOCK */
+# endif /* HAVE_PLOCK */
+#endif /* not (HAVE_MLOCKALL && MCL_CURRENT && MCL_FUTURE) */
+
+ /*
+ * Set up signals we pay attention to locally.
+ */
+#ifdef SIGDIE1
+ (void) signal_no_reset(SIGDIE1, finish);
+#endif /* SIGDIE1 */
+#ifdef SIGDIE2
+ (void) signal_no_reset(SIGDIE2, finish);
+#endif /* SIGDIE2 */
+#ifdef SIGDIE3
+ (void) signal_no_reset(SIGDIE3, finish);
+#endif /* SIGDIE3 */
+#ifdef SIGDIE4
+ (void) signal_no_reset(SIGDIE4, finish);
+#endif /* SIGDIE4 */
+
+#ifdef SIGBUS
+ (void) signal_no_reset(SIGBUS, finish);
+#endif /* SIGBUS */
+
+#if !defined(SYS_WINNT) && !defined(VMS)
+# ifdef DEBUG
+ (void) signal_no_reset(MOREDEBUGSIG, moredebug);
+ (void) signal_no_reset(LESSDEBUGSIG, lessdebug);
+# else
+ (void) signal_no_reset(MOREDEBUGSIG, no_debug);
+ (void) signal_no_reset(LESSDEBUGSIG, no_debug);
+# endif /* DEBUG */
+#endif /* !SYS_WINNT && !VMS */
+
+ /*
+ * Set up signals we should never pay attention to.
+ */
+#if defined SIGPIPE
+ (void) signal_no_reset(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+
+#if defined SYS_WINNT
+ if (!SetConsoleCtrlHandler(OnConsoleEvent, TRUE)) {
+ msyslog(LOG_ERR, "Can't set console control handler: %m");
+ }
+#endif
+
+ /*
+ * Call the init_ routines to initialize the data structures.
+ */
+#if defined (HAVE_IO_COMPLETION_PORT)
+ init_io_completion_port();
+ init_winnt_time();
+#endif
+ init_auth();
+ init_util();
+ init_restrict();
+ init_mon();
+ init_timer();
+ init_lib();
+ init_random();
+ init_request();
+ init_control();
+ init_peer();
+#ifdef REFCLOCK
+ init_refclock();
+#endif
+ set_process_priority();
+ init_proto(); /* Call at high priority */
+ init_io();
+ init_loopfilter();
+ mon_start(MON_ON); /* monitor on by default now */
+ /* turn off in config if unwanted */
+
+ /*
+ * Get configuration. This (including argument list parsing) is
+ * done in a separate module since this will definitely be different
+ * for the gizmo board. While at it, save the host name for later
+ * along with the length. The crypto needs this.
+ */
+#ifdef DEBUG
+ debug = 0;
+#endif
+ getconfig(argc, argv);
+#ifdef OPENSSL
+ crypto_setup();
+#endif /* OPENSSL */
+ initializing = 0;
+
+#if defined(SYS_WINNT) && !defined(NODETACH)
+# if defined(DEBUG)
+ if(!debug)
+ {
+# endif
+ if (NoWinService == FALSE) {
+ /* report to the service control manager that the service is running */
+ ssStatus.dwCurrentState = SERVICE_RUNNING;
+ ssStatus.dwWin32ExitCode = NO_ERROR;
+ if (!SetServiceStatus(sshStatusHandle, &ssStatus))
+ {
+ msyslog(LOG_ERR, "SetServiceStatus: %m");
+ if (ResolverThreadHandle != NULL)
+ CloseHandle(ResolverThreadHandle);
+ ssStatus.dwCurrentState = SERVICE_STOPPED;
+ SetServiceStatus(sshStatusHandle, &ssStatus);
+ return;
+ }
+ }
+# if defined(DEBUG)
+ }
+# endif
+#endif
+
+#ifdef HAVE_CLOCKCTL
+ /*
+ * Drop super-user privileges and chroot now if the OS supports
+ * non root clock control (only NetBSD for now).
+ */
+ if (user != NULL) {
+ if (isdigit((unsigned char)*user)) {
+ sw_uid = (uid_t)strtoul(user, &endp, 0);
+ if (*endp != '\0')
+ goto getuser;
+ } else {
+getuser:
+ if ((pw = getpwnam(user)) != NULL) {
+ sw_uid = pw->pw_uid;
+ } else {
+ errno = 0;
+ msyslog(LOG_ERR, "Cannot find user `%s'", user);
+ exit (-1);
+ }
+ }
+ }
+ if (group != NULL) {
+ if (isdigit((unsigned char)*group)) {
+ sw_gid = (gid_t)strtoul(group, &endp, 0);
+ if (*endp != '\0')
+ goto getgroup;
+ } else {
+getgroup:
+ if ((gr = getgrnam(group)) != NULL) {
+ sw_gid = pw->pw_gid;
+ } else {
+ errno = 0;
+ msyslog(LOG_ERR, "Cannot find group `%s'", group);
+ exit (-1);
+ }
+ }
+ }
+ if (chrootdir && chroot(chrootdir)) {
+ msyslog(LOG_ERR, "Cannot chroot to `%s': %m", chrootdir);
+ exit (-1);
+ }
+ if (group && setgid(sw_gid)) {
+ msyslog(LOG_ERR, "Cannot setgid() to group `%s': %m", group);
+ exit (-1);
+ }
+ if (group && setegid(sw_gid)) {
+ msyslog(LOG_ERR, "Cannot setegid() to group `%s': %m", group);
+ exit (-1);
+ }
+ if (user && setuid(sw_uid)) {
+ msyslog(LOG_ERR, "Cannot setuid() to user `%s': %m", user);
+ exit (-1);
+ }
+ if (user && seteuid(sw_uid)) {
+ msyslog(LOG_ERR, "Cannot seteuid() to user `%s': %m", user);
+ exit (-1);
+ }
+#endif
+ /*
+ * Report that we're up to any trappers
+ */
+ report_event(EVNT_SYSRESTART, (struct peer *)0);
+
+ /*
+ * Use select() on all on all input fd's for unlimited
+ * time. select() will terminate on SIGALARM or on the
+ * reception of input. Using select() means we can't do
+ * robust signal handling and we get a potential race
+ * between checking for alarms and doing the select().
+ * Mostly harmless, I think.
+ */
+ /* On VMS, I suspect that select() can't be interrupted
+ * by a "signal" either, so I take the easy way out and
+ * have select() time out after one second.
+ * System clock updates really aren't time-critical,
+ * and - lacking a hardware reference clock - I have
+ * yet to learn about anything else that is.
+ */
+#if defined(HAVE_IO_COMPLETION_PORT)
+ WaitHandles[0] = CreateEvent(NULL, FALSE, FALSE, NULL); /* exit reques */
+ WaitHandles[1] = get_timer_handle();
+ WaitHandles[2] = get_io_event();
+
+ for (;;) {
+ DWORD Index = WaitForMultipleObjectsEx(sizeof(WaitHandles)/sizeof(WaitHandles[0]), WaitHandles, FALSE, 1000, TRUE);
+ switch (Index) {
+ case WAIT_OBJECT_0 + 0 : /* exit request */
+ exit(0);
+ break;
+
+ case WAIT_OBJECT_0 + 1 : /* timer */
+ timer();
+ break;
+
+ case WAIT_OBJECT_0 + 2 : /* Io event */
+# ifdef DEBUG
+ if ( debug > 3 )
+ {
+ printf( "IoEvent occurred\n" );
+ }
+# endif
+ break;
+
+ case WAIT_IO_COMPLETION : /* loop */
+ case WAIT_TIMEOUT :
+ break;
+ case WAIT_FAILED:
+ msyslog(LOG_ERR, "ntpdc: WaitForMultipleObjectsEx Failed: Error: %m");
+ break;
+
+ /* For now do nothing if not expected */
+ default:
+ break;
+
+ } /* switch */
+ rbuflist = getrecvbufs(); /* get received buffers */
+
+#else /* normal I/O */
+
+ was_alarmed = 0;
+ rbuflist = (struct recvbuf *)0;
+ for (;;)
+ {
+# if !defined(HAVE_SIGNALED_IO)
+ extern fd_set activefds;
+ extern int maxactivefd;
+
+ fd_set rdfdes;
+ int nfound;
+# elif defined(HAVE_SIGNALED_IO)
+ block_io_and_alarm();
+# endif
+
+ rbuflist = getrecvbufs(); /* get received buffers */
+ if (alarm_flag) /* alarmed? */
+ {
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+
+ if (!was_alarmed && rbuflist == (struct recvbuf *)0)
+ {
+ /*
+ * Nothing to do. Wait for something.
+ */
+# ifndef HAVE_SIGNALED_IO
+ rdfdes = activefds;
+# if defined(VMS) || defined(SYS_VXWORKS)
+ /* make select() wake up after one second */
+ {
+ struct timeval t1;
+
+ t1.tv_sec = 1; t1.tv_usec = 0;
+ nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0,
+ (fd_set *)0, &t1);
+ }
+# else
+ nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0);
+# endif /* VMS */
+ if (nfound > 0)
+ {
+ l_fp ts;
+
+ get_systime(&ts);
+
+ (void)input_handler(&ts);
+ }
+ else if (nfound == -1 && errno != EINTR)
+ msyslog(LOG_ERR, "select() error: %m");
+# ifdef DEBUG
+ else if (debug > 2)
+ msyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound);
+# endif /* DEBUG */
+# else /* HAVE_SIGNALED_IO */
+
+ wait_for_signal();
+# endif /* HAVE_SIGNALED_IO */
+ if (alarm_flag) /* alarmed? */
+ {
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+ rbuflist = getrecvbufs(); /* get received buffers */
+ }
+# ifdef HAVE_SIGNALED_IO
+ unblock_io_and_alarm();
+# endif /* HAVE_SIGNALED_IO */
+
+ /*
+ * Out here, signals are unblocked. Call timer routine
+ * to process expiry.
+ */
+ if (was_alarmed)
+ {
+ timer();
+ was_alarmed = 0;
+ }
+
+#endif /* HAVE_IO_COMPLETION_PORT */
+ /*
+ * Call the data procedure to handle each received
+ * packet.
+ */
+ while (rbuflist != (struct recvbuf *)0)
+ {
+ rbuf = rbuflist;
+ rbuflist = rbuf->next;
+ (rbuf->receiver)(rbuf);
+ freerecvbuf(rbuf);
+ }
+#if defined DEBUG && defined SYS_WINNT
+ if (debug > 4)
+ printf("getrecvbufs: %ld handler interrupts, %ld frames\n",
+ handler_calls, handler_pkts);
+#endif
+
+ /*
+ * Go around again
+ */
+ }
+#ifndef SYS_WINNT
+ exit(1); /* unreachable */
+#endif
+#ifndef SYS_WINNT
+ return 1; /* DEC OSF cc braindamage */
+#endif
+}
+
+
+#ifdef SIGDIE2
+/*
+ * finish - exit gracefully
+ */
+static RETSIGTYPE
+finish(
+ int sig
+ )
+{
+
+ msyslog(LOG_NOTICE, "ntpd exiting on signal %d", sig);
+
+ switch (sig)
+ {
+# ifdef SIGBUS
+ case SIGBUS:
+ printf("\nfinish(SIGBUS)\n");
+ exit(0);
+# endif
+ case 0: /* Should never happen... */
+ return;
+ default:
+ exit(0);
+ }
+}
+#endif /* SIGDIE2 */
+
+
+#ifdef DEBUG
+#ifndef SYS_WINNT
+/*
+ * moredebug - increase debugging verbosity
+ */
+static RETSIGTYPE
+moredebug(
+ int sig
+ )
+{
+ int saved_errno = errno;
+
+ if (debug < 255)
+ {
+ debug++;
+ msyslog(LOG_DEBUG, "debug raised to %d", debug);
+ }
+ errno = saved_errno;
+}
+
+/*
+ * lessdebug - decrease debugging verbosity
+ */
+static RETSIGTYPE
+lessdebug(
+ int sig
+ )
+{
+ int saved_errno = errno;
+
+ if (debug > 0)
+ {
+ debug--;
+ msyslog(LOG_DEBUG, "debug lowered to %d", debug);
+ }
+ errno = saved_errno;
+}
+#endif
+#else /* not DEBUG */
+#ifndef SYS_WINNT/*
+ * no_debug - We don't do the debug here.
+ */
+static RETSIGTYPE
+no_debug(
+ int sig
+ )
+{
+ int saved_errno = errno;
+
+ msyslog(LOG_DEBUG, "ntpd not compiled for debugging (signal %d)", sig);
+ errno = saved_errno;
+}
+#endif /* not SYS_WINNT */
+#endif /* not DEBUG */
+
+#ifdef SYS_WINNT
+/* service_ctrl - control handler for NTP service
+ * signals the service_main routine of start/stop requests
+ * from the control panel or other applications making
+ * win32API calls
+ */
+void
+service_ctrl(
+ DWORD dwCtrlCode
+ )
+{
+ DWORD dwState = SERVICE_RUNNING;
+
+ /* Handle the requested control code */
+ switch(dwCtrlCode)
+ {
+ case SERVICE_CONTROL_PAUSE:
+ /* see no reason to support this */
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ /* see no reason to support this */
+ break;
+
+ case SERVICE_CONTROL_STOP:
+ dwState = SERVICE_STOP_PENDING;
+ /*
+ * Report the status, specifying the checkpoint and waithint,
+ * before setting the termination event.
+ */
+ ssStatus.dwCurrentState = dwState;
+ ssStatus.dwWin32ExitCode = NO_ERROR;
+ ssStatus.dwWaitHint = 3000;
+ if (!SetServiceStatus(sshStatusHandle, &ssStatus))
+ {
+ msyslog(LOG_ERR, "SetServiceStatus: %m");
+ }
+ if (WaitHandles[0] != NULL) {
+ SetEvent(WaitHandles[0]);
+ }
+ return;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ /* Update the service status */
+ break;
+
+ default:
+ /* invalid control code */
+ break;
+
+ }
+
+ ssStatus.dwCurrentState = dwState;
+ ssStatus.dwWin32ExitCode = NO_ERROR;
+ if (!SetServiceStatus(sshStatusHandle, &ssStatus))
+ {
+ msyslog(LOG_ERR, "SetServiceStatus: %m");
+ }
+}
+
+static BOOL WINAPI
+OnConsoleEvent(
+ DWORD dwCtrlType
+ )
+{
+ switch (dwCtrlType) {
+ case CTRL_BREAK_EVENT :
+ if (debug > 0) {
+ debug <<= 1;
+ }
+ else {
+ debug = 1;
+ }
+ if (debug > 8) {
+ debug = 0;
+ }
+ printf("debug level %d\n", debug);
+ break ;
+
+ case CTRL_C_EVENT :
+ case CTRL_CLOSE_EVENT :
+ case CTRL_SHUTDOWN_EVENT :
+ if (WaitHandles[0] != NULL) {
+ SetEvent(WaitHandles[0]);
+ }
+ break;
+
+ default :
+ return FALSE;
+
+
+ }
+ return TRUE;;
+}
+
+
+/*
+ * NT version of exit() - all calls to exit() should be routed to
+ * this function.
+ */
+void
+service_exit(
+ int status
+ )
+{
+ if (!debug) { /* did not become a service, simply exit */
+ /* service mode, need to have the service_main routine
+ * register with the service control manager that the
+ * service has stopped running, before exiting
+ */
+ ssStatus.dwCurrentState = SERVICE_STOPPED;
+ SetServiceStatus(sshStatusHandle, &ssStatus);
+
+ }
+ uninit_io_completion_port();
+ reset_winnt_time();
+
+# if defined _MSC_VER
+ _CrtDumpMemoryLeaks();
+# endif
+#undef exit
+ exit(status);
+}
+
+#endif /* SYS_WINNT */
diff --git a/ntpd/ntpsim.c b/ntpd/ntpsim.c
new file mode 100644
index 0000000..3fbae17
--- /dev/null
+++ b/ntpd/ntpsim.c
@@ -0,0 +1,368 @@
+/*
+ * NTP simulator engine - Harish Nair
+ * University of Delaware, 2001
+ */
+#include "ntpd.h"
+#include "ntpsim.h"
+
+/*
+ * Defines...
+ */
+#define SIM_TIME 86400 /* end simulation time */
+#define NET_DLY .001 /* network delay */
+#define PROC_DLY .001 /* processing delay */
+#define BEEP_DLY 3600 /* beep interval (s) */
+#define SLEW 500e-6 /* correction rate (PPM) */
+
+/*
+ * Function pointers
+ */
+void (*funcPtr[]) (Node *, Event) = {
+ &ndbeep, &ndeclk, &ntptmr, &netpkt
+};
+
+
+/*
+ * ntpsim - initialize global variables and event queue and start
+ */
+int
+ntpsim(
+ int argc,
+ char *argv[]
+ )
+{
+ Event e;
+ double maxtime;
+ struct timeval seed;
+
+ /*
+ * Initialize the global node
+ */
+ ntp_node.time = 0; /* simulation time */
+ ntp_node.sim_time = SIM_TIME; /* end simulation time (-S) */
+ ntp_node.ntp_time = 0; /* client disciplined time */
+ ntp_node.adj = 0; /* remaining time correction */
+ ntp_node.slew = SLEW; /* correction rate (-H) */
+
+ ntp_node.clk_time = 0; /* server time (-O) */
+ ntp_node.ferr = 0; /* frequency error (-T) */
+ ntp_node.fnse = 0; /* random walk noise (-W) */
+ ntp_node.ndly = NET_DLY; /* network delay (-Y) */
+ ntp_node.snse = 0; /* phase noise (-C) */
+ ntp_node.pdly = PROC_DLY; /* processing delay (-Z) */
+ ntp_node.bdly = BEEP_DLY; /* beep interval (-B) */
+
+ ntp_node.events = NULL;
+ ntp_node.rbuflist = NULL;
+
+ /*
+ * Initialize ntp variables
+ */
+ initializing = 1;
+ init_auth();
+ init_util();
+ init_restrict();
+ init_mon();
+ init_timer();
+ init_lib();
+ init_random();
+ init_request();
+ init_control();
+ init_peer();
+ init_proto();
+ init_io();
+ init_loopfilter();
+ mon_start(MON_OFF);
+ getconfig(argc, argv);
+ initializing = 0;
+
+ /*
+ * Watch out here, we want the real time, not the silly stuff.
+ */
+ gettimeofday(&seed, NULL);
+ srand48(seed.tv_usec);
+
+ /*
+ * Push a beep and timer interrupt on the queue
+ */
+ push(event(0, BEEP), &ntp_node.events);
+ push(event(ntp_node.time + 1.0, TIMER), &ntp_node.events);
+
+ /*
+ * Pop the queue until nothing is left or time is exceeded
+ */
+ maxtime = ntp_node.time + ntp_node.sim_time;
+ while (ntp_node.time <= maxtime && ntp_node.events != NULL ) {
+ e = pop(&ntp_node.events);
+ ndeclk(&ntp_node, e);
+ funcPtr[e.function](&ntp_node, e);
+ }
+ return (0);
+}
+
+
+/*
+ * Return an event
+ */
+Event
+event(
+ double t,
+ funcTkn f
+ )
+{
+ Event e;
+
+ e.time = t;
+ e.function = f;
+ return (e);
+}
+
+/*
+ * Create an event queue
+ */
+Queue
+queue(
+ Event e,
+ Queue q
+ )
+{
+ Queue ret;
+
+ if ((ret = (Queue)malloc(sizeof(struct List))) == NULL)
+ abortsim("queue-malloc");
+ ret->event = e;
+ ret->next = q;
+ return (ret);
+}
+
+
+/*
+ * Push an event into the event queue
+ */
+void push(
+ Event e,
+ Queue *qp
+ )
+{
+ Queue *tmp = qp;
+
+ while (*tmp != NULL && ((*tmp)->event.time < e.time))
+ tmp = &((*tmp)->next);
+ *tmp = queue(e, (*tmp));
+}
+
+
+/*
+ * Pop the first event from the event queue
+ */
+Event
+pop(
+ Queue *qp
+ )
+{
+ Event ret;
+ Queue tmp;
+
+ tmp = *qp;
+ if (tmp == NULL)
+ abortsim("pop - empty queue");
+ ret = tmp->event;
+ *qp = tmp->next;
+ free(tmp);
+ return (ret);
+}
+
+
+/*
+ * Update clocks
+ */
+void
+ndeclk(
+ Node *n,
+ Event e
+ )
+{
+ node_clock(n, e.time);
+}
+
+
+/*
+ * Timer interrupt. Eventually, this results in calling the
+ * srvr_rplyi() routine below.
+ */
+void
+ntptmr(
+ Node *n,
+ Event e
+ )
+{
+ struct recvbuf *rbuf;
+
+ timer();
+
+ /*
+ * Process buffers received. They had better be in order by
+ * receive timestamp.
+ */
+ while (n->rbuflist != NULL) {
+ rbuf = n->rbuflist;
+ n->rbuflist = rbuf->next;
+ (rbuf->receiver)(rbuf);
+ free(rbuf);
+ }
+
+ /*
+ * Arm the next timer interrupt.
+ */
+ push(event(e.time + (1 << EVENT_TIMEOUT), TIMER), &n->events);
+}
+
+
+/*
+ * srvr_rply() - send packet
+ */
+int srvr_rply(
+ Node *n,
+ struct sockaddr_storage *dest,
+ struct interface *inter, struct pkt *rpkt
+ )
+{
+ struct pkt xpkt;
+ struct recvbuf rbuf;
+ Event xvnt;
+ double dtemp, etemp;
+
+ /*
+ * Insert packet header values. We make this look like a
+ * stratum-1 server with a GPS clock, but nobody will ever
+ * notice that.
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
+ MODE_SERVER);
+ xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
+ memcpy(&xpkt.refid, "GPS", 4);
+ xpkt.ppoll = rpkt->ppoll;
+ xpkt.precision = rpkt->precision;
+ xpkt.rootdelay = 0;
+ xpkt.rootdispersion = 0;
+
+ /*
+ * Insert the timestamps.
+ */
+ xpkt.org = rpkt->xmt;
+ dtemp = poisson(n->ndly, n->snse); /* client->server delay */
+ DTOLFP(dtemp + n->clk_time, &xpkt.rec);
+ dtemp += poisson(n->pdly, 0); /* server delay */
+ DTOLFP(dtemp + n->clk_time, &xpkt.xmt);
+ xpkt.reftime = xpkt.xmt;
+ dtemp += poisson(n->ndly, n->snse); /* server->client delay */
+
+ /*
+ * Insert the I/O stuff.
+ */
+ rbuf.receiver = receive;
+ get_systime(&rbuf.recv_time);
+ rbuf.recv_length = LEN_PKT_NOMAC;
+ rbuf.recv_pkt = xpkt;
+ memcpy(&rbuf.srcadr, dest, sizeof(struct sockaddr_storage));
+ memcpy(&rbuf.recv_srcadr, dest,
+ sizeof(struct sockaddr_storage));
+ if ((rbuf.dstadr = malloc(sizeof(struct interface))) == NULL)
+ abortsim("server-malloc");
+ memcpy(rbuf.dstadr, inter, sizeof(struct interface));
+ rbuf.next = NULL;
+
+ /*
+ * Very carefully predict the time of arrival for the received
+ * packet.
+ */
+ LFPTOD(&xpkt.org, etemp);
+ etemp += dtemp;
+ xvnt = event(etemp, PACKET);
+ xvnt.rcv_buf = rbuf;
+ push(xvnt, &n->events);
+ return (0);
+}
+
+
+/*
+ * netpkt() - receive packet
+ */
+void
+netpkt(
+ Node *n,
+ Event e
+ )
+{
+ struct recvbuf *rbuf;
+ struct recvbuf *obuf;
+
+ /*
+ * Insert the packet on the receive queue and record the arrival
+ * time.
+ */
+ if ((rbuf = malloc(sizeof(struct recvbuf))) == NULL)
+ abortsim("ntprcv-malloc");
+ memcpy(rbuf, &e.rcv_buf, sizeof(struct recvbuf));
+ rbuf->receiver = receive;
+ DTOLFP(n->ntp_time, &rbuf->recv_time);
+ rbuf->next = NULL;
+ obuf = n->rbuflist;
+
+ /*
+ * In the present incarnation, no more than one buffer can be on
+ * the queue; however, we sniff the queue anyway as a hint for
+ * further development.
+ */
+ if (obuf == NULL) {
+ n->rbuflist = rbuf;
+ } else {
+ while (obuf->next != NULL)
+ obuf = obuf->next;
+ obuf->next = rbuf;
+ }
+}
+
+
+/*
+ * ndbeep() - progress indicator
+ */
+void
+ndbeep(
+ Node *n,
+ Event e
+ )
+{
+ static int first_time = 1;
+ char *dash = "-----------------";
+
+ if(n->bdly > 0) {
+ if (first_time) {
+ printf(
+ "\t%4c T %4c\t%4c T+ERR %3c\t%5cT+ERR+NTP\n", ' ', ' ', ' ', ' ',' ');
+ printf("\t%s\t%s\t%s\n", dash, dash, dash);
+ first_time = 0;
+ push(event(n->bdly, BEEP), &n->events);
+ push(event(n->sim_time, BEEP), &n->events);
+ printf("\t%16.6f\t%16.6f\t%16.6f\n",
+ n->time, n->clk_time, n->ntp_time);
+ return;
+ }
+ printf("\t%16.6f\t%16.6f\t%16.6f\n",
+ n->time, n->clk_time, n->ntp_time);
+ push(event(e.time + n->bdly, BEEP), &n->events);
+ }
+}
+
+
+/*
+ * Abort simulation
+ */
+void
+abortsim(
+ char *errmsg
+ )
+{
+ perror(errmsg);
+ exit(1);
+}
diff --git a/ntpd/refclock_acts.c b/ntpd/refclock_acts.c
new file mode 100644
index 0000000..d26ceed
--- /dev/null
+++ b/ntpd/refclock_acts.c
@@ -0,0 +1,984 @@
+/*
+ * refclock_acts - clock driver for the NIST/PTB Automated Computer Time
+ * Service aka Amalgamated Containerized Trash Service (ACTS)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS))
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_control.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+/* MUST BE AFTER LAST #include <config.h> !!! */
+
+#if defined(CLOCK_ACTS) && defined(CLOCK_PTBACTS)
+# if defined(KEEPPTBACTS)
+# undef CLOCK_ACTS
+# else /* not KEEPPTBACTS */
+# undef CLOCK_PTBACTS
+# endif /* not KEEPPTBACTS */
+#endif /* CLOCK_ACTS && CLOCK_PTBACTS */
+
+/*
+ * This driver supports the NIST Automated Computer Time Service (ACTS).
+ * It periodically dials a prespecified telephone number, receives the
+ * NIST timecode data and calculates the local clock correction. It is
+ * designed primarily for use as a backup when neither a radio clock nor
+ * connectivity to Internet time servers is available. For the best
+ * accuracy, the individual telephone line/modem delay needs to be
+ * calibrated using outside sources.
+ *
+ * The ACTS is located at NIST Boulder, CO, telephone 303 494 4774. A
+ * toll call from a residence telephone in Newark, DE, costs between 14
+ * and 27 cents, depending on time of day, and from a campus telephone
+ * between 3 and 4 cents, although it is not clear what carrier and time
+ * of day discounts apply in this case. The modem dial string will
+ * differ depending on local telephone configuration, etc., and is
+ * specified by the phone command in the configuration file. The
+ * argument to this command is an AT command for a Hayes compatible
+ * modem.
+ *
+ * The accuracy produced by this driver should be in the range of a
+ * millisecond or two, but may need correction due to the delay
+ * characteristics of the individual modem involved. For undetermined
+ * reasons, some modems work with the ACTS echo-delay measurement scheme
+ * and some don't. This driver tries to do the best it can with what it
+ * gets. Initial experiments with a Practical Peripherals 9600SA modem
+ * here in Delaware suggest an accuracy of a millisecond or two can be
+ * achieved without the scheme by using a fudge time1 value of 65.0 ms.
+ * In either case, the dispersion for a single call involving ten
+ * samples is about 1.3 ms.
+ *
+ * The driver can operate in either of three modes, as determined by
+ * the mode parameter in the server configuration command. In mode 0
+ * (automatic) the driver operates continuously at intervals depending
+ * on the prediction error, as measured by the driver, usually in the
+ * order of several hours. In mode 1 (backup) the driver is enabled in
+ * automatic mode only when no other source of synchronization is
+ * available and when more than MAXOUTAGE (3600 s) have elapsed since
+ * last synchronized by other sources. In mode 2 (manual) the driver
+ * operates only when enabled using a fudge flags switch, as described
+ * below.
+ *
+ * For reliable call management, this driver requires a 1200-bps modem
+ * with a Hayes-compatible command set and control over the modem data
+ * terminal ready (DTR) control line. Present restrictions require the
+ * use of a POSIX-compatible programming interface, although other
+ * interfaces may work as well. The modem setup string is hard-coded in
+ * the driver and may require changes for nonstandard modems or special
+ * circumstances.
+ *
+ * Further information can be found in the README.refclock file in the
+ * ntp - Version 3 distribution.
+ *
+ * Fudge Factors
+ *
+ * Ordinarily, the propagation time correction is computed automatically
+ * by ACTS and the driver. When this is not possible or erratic due to
+ * individual modem characteristics, the fudge flag2 switch should be
+ * set to disable the ACTS echo-delay scheme. In any case, the fudge
+ * time1 parameter can be used to adjust the propagation delay as
+ * required.
+ *
+ * The ACTS call interval is determined in one of three ways. In manual
+ * mode a call is initiated by setting fudge flag1 using ntpdc, either
+ * manually or via a cron job. In AUTO mode this flag is set by the peer
+ * timer, which is controlled by the sys_poll variable in response to
+ * measured errors. In backup mode the driver is ordinarily asleep, but
+ * awakes (in auto mode) if all other synchronization sources are lost.
+ * In either auto or backup modes, the call interval increases as long
+ * as the measured errors do not exceed the value of the fudge time2
+ * parameter.
+ *
+ * When the fudge flag1 is set, the ACTS calling program is activated.
+ * This program dials each number listed in the phones command of the
+ * configuration file in turn. If a call attempt fails, the next number
+ * in the list is dialed. The fudge flag1 and counter are reset and the
+ * calling program terminated if (a) a valid clock update has been
+ * determined, (b) no more numbers remain in the list, (c) a device
+ * fault or timeout occurs or (d) fudge flag1 is reset manually using
+ * ntpdc.
+ *
+ * In automatic and backup modes, the driver determines the call
+ * interval using a procedure depending on the measured prediction
+ * error and the fudge time2 parameter. If the error exceeds time2 for a
+ * number of times depending on the current interval, the interval is
+ * decreased, but not less than about 1000 s. If the error is less than
+ * time2 for some number of times, the interval is increased, but not
+ * more than about 18 h. With the default value of zero for fudge time2,
+ * the interval will increase from 1000 s to the 4000-8000-s range, in
+ * which the expected accuracy should be in the 1-2-ms range. Setting
+ * fudge time2 to a large value, like 0.1 s, may result in errors of
+ * that order, but increase the call interval to the maximum. The exact
+ * value for each configuration will depend on the modem and operating
+ * system involved, so some experimentation may be necessary.
+ */
+
+/*
+ * DESCRIPTION OF THE AUTOMATED COMPUTER TELEPHONE SERVICE (ACTS)
+ * (reformatted from ACTS on-line computer help information)
+ *
+ * The following is transmitted (at 1200 baud) following completion of
+ * the telephone connection.
+ *
+ * National Institute of Standards and Technology
+ * Telephone Time Service, Generator 3B
+ * Enter question mark "?" for HELP
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV <OTM>
+ * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:16 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:17 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:18 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:19 50 0 +.1 037.6 UTC(NIST) #
+ * 47999 90-04-18 21:39:20 50 0 +.1 037.6 UTC(NIST) #
+ * etc..etc...etc.......
+ *
+ * UTC = Universal Time Coordinated, the official world time referred to
+ * the zero meridian.
+ *
+ * DST Daylight savings time characters, valid for the continental
+ * U.S., are set as follows:
+ *
+ * 00 We are on standard time (ST).
+ * 01-49 Now on DST, go to ST when your local time is 2:00 am and
+ * the count is 01. The count is decremented daily at 00
+ * (UTC).
+ * 50 We are on DST.
+ * 51-99 Now on ST, go to DST when your local time is 2:00 am and
+ * the count is 51. The count is decremented daily at 00
+ * (UTC).
+ *
+ * The two DST characters provide up to 48 days advance notice of a
+ * change in time. The count remains at 00 or 50 at other times.
+ *
+ * LS Leap second flag is set to "1" to indicate that a leap second is
+ * to be added as 23:59:60 (UTC) on the last day of the current UTC
+ * month. The LS flag will be reset to "0" starting with 23:59:60
+ * (UTC). The flag will remain on for the entire month before the
+ * second is added. Leap seconds are added as needed at the end of
+ * any month. Usually June and/or December are chosen.
+ *
+ * The leap second flag will be set to a "2" to indicate that a
+ * leap second is to be deleted at 23:59:58--00:00:00 on the last
+ * day of the current month. (This latter provision is included per
+ * international recommendation, however it is not likely to be
+ * required in the near future.)
+ *
+ * DUT1 Approximate difference between earth rotation time (UT1) and
+ * UTC, in steps of 0.1 second: DUT1 = UT1 - UTC.
+ *
+ * MJD Modified Julian Date, often used to tag certain scientific data.
+ *
+ * The full time format is sent at 1200 baud, 8 bit, 1 stop, no parity.
+ * The format at 300 Baud is also 8 bit, 1 stop, no parity. At 300 Baud
+ * the MJD and DUT1 values are deleted and the time is transmitted only
+ * on even seconds.
+ *
+ * Maximum on line time will be 56 seconds. If all lines are busy at any
+ * time, the oldest call will be terminated if it has been on line more
+ * than 28 seconds, otherwise, the call that first reaches 28 seconds
+ * will be terminated.
+ *
+ * Current time is valid at the "on-time" marker (OTM), either "*" or
+ * "#". The nominal on-time marker (*) will be transmitted 45 ms early
+ * to account for the 8 ms required to send 1 character at 1200 Baud,
+ * plus an additional 7 ms for delay from NIST to the user, and
+ * approximately 30 ms "scrambler" delay inherent in 1200 Baud modems.
+ * If the caller echoes all characters, NIST will measure the round trip
+ * delay and advance the on-time marker so that the midpoint of the stop
+ * bit arrives at the user on time. The amount of msADV will reflect the
+ * actual required advance in milliseconds and the OTM will be a "#".
+ *
+ * (The NIST system requires 4 or 5 consecutive delay measurements which
+ * are consistent before switching from "*" to "#". If the user has a
+ * 1200 Baud modem with the same internal delay as that used by NIST,
+ * then the "#" OTM should arrive at the user within +-2 ms of the
+ * correct time.
+ *
+ * However, NIST has studied different brands of 1200 Baud modems and
+ * found internal delays from 24 ms to 40 ms and offsets of the "#" OTM
+ * of +-10 ms. For many computer users, +-10 ms accuracy should be more
+ * than adequate since many computer internal clocks can only be set
+ * with granularity of 20 to 50 ms. In any case, the repeatability of
+ * the offset for the "#" OTM should be within +-2 ms, if the dial-up
+ * path is reciprocal and the user doesn't change the brand or model of
+ * modem used.
+ *
+ * This should be true even if the dial-up path on one day is a land-
+ * line of less than 40 ms (one way) and on the next day is a satellite
+ * link of 260 to 300 ms. In the rare event that the path is one way by
+ * satellite and the other way by land line with a round trip
+ * measurement in the range of 90 to 260 ms, the OTM will remain a "*"
+ * indicating 45 ms advance.
+ *
+ * For user comments write:
+ * NIST-ACTS
+ * Time and Frequency Division
+ * Mail Stop 847
+ * 325 Broadway
+ * Boulder, CO 80303
+ *
+ * Software for setting (PC)DOS compatable machines is available on a
+ * 360-kbyte diskette for $35.00 from: NIST Office of Standard Reference
+ * Materials B311-Chemistry Bldg, NIST, Gaithersburg, MD, 20899, (301)
+ * 975-6776
+ *
+ * PTB timecode service (+49 531 512038)
+ * The Physikalisch-Technische Bundesanstalt (Germany)
+ * also supports a modem time service
+ * as the data formats are very similar this driver can also be compiled for
+ * utilizing the PTB time code service.
+ *
+ * Data format
+ * 0000000000111111111122222222223333333333444444444455555555556666666666777777777 7
+ * 0123456789012345678901234567890123456789012345678901234567890123456789012345678 9
+ * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500 *
+ * A B C D EF G H IJ K L M N O P Q R S T U V W XY Z<CR><LF>
+ *
+ * A year
+ * B month
+ * C day
+ * D hour
+ * E : normally
+ * A for DST to ST switch first hour
+ * B for DST to ST switch second hour if not marked in H
+ * F minute
+ * G second
+ * H timezone
+ * I day of week
+ * J week of year
+ * K day of year
+ * L month for next ST/DST changes
+ * M day
+ * N hour
+ * O UTC year
+ * P UTC month
+ * Q UTC day
+ * R UTC hour
+ * S UTC minute
+ * T modified julian day (MJD)
+ * U DUT1
+ * V direction and month if leap second
+ * W signal delay (assumed/measured)
+ * X sequence number for additional text line in Y
+ * Y additional text
+ * Z on time marker (* - assumed delay / # measured delay)
+ * <CR>!<LF> ! is second change !
+ *
+ * This format is also used by the National Physical Laboratory (NPL)'s
+ * TRUETIME service in the UK. In this case the timezone field is
+ * UTC+0 or UTC+1 for standard and daylight saving time. The phone
+ * number for this service (a premium rate number) is 0891 516 333.
+ * It is not clear whether the echo check is implemented.
+ *
+ * For more detail, see http://www.npl.co.uk/npl/cetm/taf/truetime.html.
+ */
+
+/*
+ * Interface definitions
+ */
+#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#ifdef CLOCK_ACTS
+# define REFID "ACTS" /* reference ID */
+# define DESCRIPTION "NIST Automated Computer Time Service" /* WRU */
+# define LENCODE 50 /* length of valid timecode string */
+# define DEVICE "/dev/acts%d" /* device name and unit */
+# define REF_ENTRY refclock_acts
+#else /* not CLOCK_ACTS */
+# define REFID "TPTB" /* reference ID */
+# define DESCRIPTION "PTB Automated Computer Time Service"
+# define LENCODE 78 /* length of valid timecode string */
+# define DEVICE "/dev/ptb%d" /* device name and unit */
+# define REF_ENTRY refclock_ptb
+#endif /* not CLOCK_ACTS */
+#define MODE_AUTO 0 /* automatic mode */
+#define MODE_BACKUP 1 /* backup mode */
+#define MODE_MANUAL 2 /* manual mode */
+
+#define MSGCNT 10 /* we need this many ACTS messages */
+#define SMAX 80 /* max token string length */
+#define ACTS_MINPOLL 10 /* log2 min poll interval (1024 s) */
+#define ACTS_MAXPOLL 18 /* log2 max poll interval (16384 s) */
+#define MAXOUTAGE 3600 /* max before ACTS kicks in (s) */
+
+/*
+ * Modem control strings. These may have to be changed for some modems.
+ *
+ * AT command prefix
+ * B1 initiate call negotiation using Bell 212A
+ * &C1 enable carrier detect
+ * &D2 hang up and return to command mode on DTR transition
+ * E0 modem command echo disabled
+ * l1 set modem speaker volume to low level
+ * M1 speaker enabled untill carrier detect
+ * Q0 return result codes
+ * V1 return result codes as English words
+ */
+#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
+#define MODEM_HANGUP "ATH" /* modem disconnect */
+
+/*
+ * Timeouts
+ */
+#define IDLE 60 /* idle timeout (s) */
+#define WAIT 2 /* wait timeout (s) */
+#define ANSWER 30 /* answer timeout (s) */
+#define CONNECT 10 /* connect timeout (s) */
+#define TIMECODE 15 /* timecode timeout (s) */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct actsunit {
+ int pollcnt; /* poll message counter */
+ int state; /* the first one was Delaware */
+ int run; /* call program run switch */
+ int msgcnt; /* count of ACTS messages received */
+ long redial; /* interval to next automatic call */
+ double msADV; /* millisecond advance of last message */
+};
+
+/*
+ * Function prototypes
+ */
+static int acts_start P((int, struct peer *));
+static void acts_shutdown P((int, struct peer *));
+static void acts_receive P((struct recvbuf *));
+static void acts_poll P((int, struct peer *));
+static void acts_timeout P((struct peer *));
+static void acts_disc P((struct peer *));
+static int acts_write P((struct peer *, const char *));
+
+/*
+ * Transfer vector (conditional structure name)
+ */
+struct refclock REF_ENTRY = {
+ acts_start, /* start up driver */
+ acts_shutdown, /* shut down driver */
+ acts_poll, /* transmit poll message */
+ noentry, /* not used (old acts_control) */
+ noentry, /* not used (old acts_init) */
+ noentry, /* not used (old acts_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * acts_start - open the devices and initialize data for processing
+ */
+
+static int
+acts_start (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ int dtr = TIOCM_DTR;
+
+ /*
+ * Open serial port. Use ACTS line discipline, if available. It
+ * pumps a timestamp into the data stream at every on-time
+ * character '*' found. Note: the port must have modem control
+ * or deep pockets for the phone bill. HP-UX 9.03 users should
+ * have very deep pockets.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS)))
+ return (0);
+ if (ioctl(fd, TIOCMBIS, (char *)&dtr) < 0) {
+ msyslog(LOG_ERR, "clock %s ACTS no modem control",
+ ntoa(&peer->srcadr));
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct actsunit *)
+ emalloc(sizeof(struct actsunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct actsunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = acts_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->minpoll = ACTS_MINPOLL;
+ peer->maxpoll = ACTS_MAXPOLL;
+ peer->sstclktype = CTL_SST_TS_TELEPHONE;
+
+ /*
+ * Initialize modem and kill DTR. We skedaddle if this comes
+ * bum.
+ */
+ if (!acts_write(peer, MODEM_SETUP)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Set up the driver timeout
+ */
+ peer->nextdate = current_time + WAIT;
+ return (1);
+}
+
+
+/*
+ * acts_shutdown - shut down the clock
+ */
+static void
+acts_shutdown (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * acts_receive - receive data from the serial interface
+ */
+static void
+acts_receive (
+ struct recvbuf *rbufp
+ )
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char str[SMAX];
+ int i;
+ char hangup = '%'; /* ACTS hangup */
+ int day; /* day of the month */
+ int month; /* month of the year */
+ u_long mjd; /* Modified Julian Day */
+ double dut1; /* DUT adjustment */
+ double msADV; /* ACTS transmit advance (ms) */
+ char flag; /* calibration flag */
+#ifndef CLOCK_PTBACTS
+ char utc[10]; /* this is NIST and you're not */
+ u_int dst; /* daylight/standard time indicator */
+ u_int leap; /* leap-second indicator */
+#else
+ char leapdir; /* leap direction */
+ u_int leapmonth; /* month of leap */
+#endif
+ /*
+ * Initialize pointers and read the timecode and timestamp. If
+ * the OK modem status code, leave it where folks can find it.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &pp->lastrec);
+ if (pp->lencode == 0) {
+ if (strcmp(pp->a_lastcode, "OK") == 0)
+ pp->lencode = 2;
+ return;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("acts: state %d timecode %d %*s\n", up->state,
+ pp->lencode, pp->lencode, pp->a_lastcode);
+#endif
+
+ switch (up->state) {
+
+ case 0:
+
+ /*
+ * State 0. We are not expecting anything. Probably
+ * modem disconnect noise. Go back to sleep.
+ */
+ return;
+
+ case 1:
+
+ /*
+ * State 1. We are waiting for the call to be answered.
+ * All we care about here is CONNECT as the first token
+ * in the string. If the modem signals BUSY, ERROR, NO
+ * ANSWER, NO CARRIER or NO DIALTONE, we immediately
+ * hang up the phone. If CONNECT doesn't happen after
+ * ANSWER seconds, hang up the phone. If everything is
+ * okay, start the connect timeout and slide into state
+ * 2.
+ */
+ if( strcmp(pp->a_lastcode, " ") == 0) {
+ acts_disc(peer);
+ return;
+ }
+ if( strcmp(sys_phone[0],"DIRECT") != 0 ) {
+ (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX);
+ if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
+ 0 || strcmp(str, "NO") == 0) {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s ACTS modem status %s",
+ ntoa(&peer->srcadr), pp->a_lastcode);
+ acts_disc(peer);
+ } else if (strcmp(str, "CONNECT") == 0) {
+ peer->nextdate = current_time + CONNECT;
+ up->msgcnt = 0;
+ up->state++;
+ }
+ } else {
+ (void) strncpy(str,"CONNECT",7);
+ peer->nextdate = current_time + CONNECT;
+ up->msgcnt = 0;
+ up->state++;
+ }
+ return;
+
+ case 2:
+
+ /*
+ * State 2. The call has been answered and we are
+ * waiting for the first ACTS message. If this doesn't
+ * happen within the timecode timeout, hang up the
+ * phone. We probably got a wrong number or ACTS is
+ * down.
+ */
+ peer->nextdate = current_time + TIMECODE;
+ up->state++;
+ }
+
+ /*
+ * Real yucky things here. Ignore everything except timecode
+ * messages, as determined by the message length. We told the
+ * terminal routines to end the line with '*' and the line
+ * discipline to strike a timestamp on that character. However,
+ * when the ACTS echo-delay scheme works, the '*' eventually
+ * becomes a '#'. In this case the message is ended by the <CR>
+ * that comes about 200 ms after the '#' and the '#' cannot be
+ * echoed at the proper time. But, this may not be a lose, since
+ * we already have good data from prior messages and only need
+ * the millisecond advance calculated by ACTS. So, if the
+ * message is long enough and has an on-time character at the
+ * right place, we consider the message (but not neccesarily the
+ * timestmap) to be valid.
+ */
+ if (pp->lencode != LENCODE)
+ return;
+
+#ifndef CLOCK_PTBACTS
+ /*
+ * We apparently have a valid timecode message, so dismember it
+ * with sscan(). This routine does a good job in spotting syntax
+ * errors without becoming overly pedantic.
+ *
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV OTM
+ * 47222 88-03-02 21:39:15 83 0 +.3 045.0 UTC(NBS) *
+ */
+ if (sscanf(pp->a_lastcode,
+ "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %s %c",
+ &mjd, &pp->year, &month, &day, &pp->hour, &pp->minute,
+ &pp->second, &dst, &leap, &dut1, &msADV, utc, &flag) != 13) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+#else
+ /*
+ * Data format
+ * 0000000000111111111122222222223333333333444444444455555555556666666666777777777 7
+ * 0123456789012345678901234567890123456789012345678901234567890123456789012345678 9
+ * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500 *
+ */
+ if (sscanf(pp->a_lastcode,
+ "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
+ &pp->second, &pp->year, &month, &day, &pp->hour, &pp->minute, &mjd, &dut1, &leapdir, &leapmonth, &msADV, &flag) != 12) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+#endif
+ /*
+ * Some modems can't be trusted (the Practical Peripherals
+ * 9600SA comes to mind) and, even if they manage to unstick
+ * ACTS, the millisecond advance is wrong, so we use CLK_FLAG2
+ * to disable echoes, if neccessary.
+ */
+ if ((flag == '*' || flag == '#') && !(pp->sloppyclockflag &
+ CLK_FLAG2))
+ (void)write(pp->io.fd, &flag, 1);
+
+ /*
+ * The ACTS timecode format croaks in 2000. Life is short.
+ * Would only the timecode mavens resist the urge to express months
+ * of the year and days of the month in favor of days of the year.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Depending on the driver, at this point we have a two-digit year
+ * or a four-digit year. Make sure we have a four-digit year.
+ */
+ if ( pp->year < YEAR_PIVOT ) pp->year += 100; /* Y2KFixes */
+ if ( pp->year < YEAR_BREAK ) pp->year += 1900; /* Y2KFixes */
+ if ( !isleap_4(pp->year) ) { /* Y2KFixes */
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+#ifndef CLOCK_PTBACTS
+ if (leap == 1)
+ pp->leap = LEAP_ADDSECOND;
+ else if (pp->leap == 2)
+ pp->leap = LEAP_DELSECOND;
+#else
+ if (leapmonth == month) {
+ if (leapdir == '+')
+ pp->leap = LEAP_ADDSECOND;
+ else if (leapdir == '-')
+ pp->leap = LEAP_DELSECOND;
+ }
+#endif
+
+ /*
+ * Colossal hack here. We process each sample in a trimmed-mean
+ * filter and determine the reference clock offset and
+ * dispersion. The fudge time1 value is added to each sample as
+ * received. If we collect MSGCNT samples before the '#' on-time
+ * character, we use the results of the filter as is. If the '#'
+ * is found before that, the adjusted msADV is used to correct
+ * the propagation delay.
+ */
+ up->msgcnt++;
+ if (flag == '#') {
+ pp->offset += (msADV - up->msADV) * 1000 * 1e-6;
+ } else {
+ up->msADV = msADV;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } else if (up->msgcnt < MSGCNT)
+ return;
+ }
+
+ /*
+ * We have a filtered sample offset ready for peer processing.
+ * We use lastrec as both the reference time and receive time in
+ * order to avoid being cute, like setting the reference time
+ * later than the receive time, which may cause a paranoid
+ * protocol module to chuck out the data. Finaly, we unhook the
+ * timeout, arm for the next call, fold the tent and go home.
+ * The little dance with the '%' character is an undocumented
+ * ACTS feature that hangs up the phone real quick without
+ * waiting for carrier loss or long-space disconnect, but we do
+ * these clumsy things anyway.
+ */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ up->pollcnt = 0;
+ (void)write(pp->io.fd, &hangup, 1);
+ up->state = 0;
+ acts_disc(peer);
+}
+
+
+/*
+ * acts_poll - called by the transmit routine
+ */
+static void
+acts_poll (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * If the driver is running, we set the enable flag (fudge
+ * flag1), which causes the driver timeout routine to initiate a
+ * call to ACTS. If not, the enable flag can be set using
+ * ntpdc. If this is the sustem peer, then follow the system
+ * poll interval.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+
+ if (up->run) {
+ pp->sloppyclockflag |= CLK_FLAG1;
+ if (peer == sys_peer)
+ peer->hpoll = sys_poll;
+ else
+ peer->hpoll = peer->minpoll;
+ }
+ acts_timeout (peer);
+ return;
+}
+
+
+/*
+ * acts_timeout - called by the timer interrupt
+ */
+static void
+acts_timeout (
+ struct peer *peer
+ )
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * If a timeout occurs in other than state 0, the call has
+ * failed. If in state 0, we just see if there is other work to
+ * do.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->state) {
+ acts_disc(peer);
+ return;
+ }
+ switch (peer->ttl) {
+
+ /*
+ * In manual mode the ACTS calling program is activated
+ * by the ntpdc program using the enable flag (fudge
+ * flag1), either manually or by a cron job.
+ */
+ case MODE_MANUAL:
+ up->run = 0;
+ break;
+
+ /*
+ * In automatic mode the ACTS calling program runs
+ * continuously at intervals determined by the sys_poll
+ * variable.
+ */
+ case MODE_AUTO:
+ if (!up->run)
+ pp->sloppyclockflag |= CLK_FLAG1;
+ up->run = 1;
+ break;
+
+ /*
+ * In backup mode the ACTS calling program is disabled,
+ * unless no system peer has been selected for MAXOUTAGE
+ * (3600 s). Once enabled, it runs until some other NTP
+ * peer shows up.
+ */
+ case MODE_BACKUP:
+ if (!up->run && sys_peer == 0) {
+ if (current_time - last_time > MAXOUTAGE) {
+ up->run = 1;
+ peer->hpoll = peer->minpoll;
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s ACTS backup started ",
+ ntoa(&peer->srcadr));
+ }
+ } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) {
+ peer->hpoll = peer->minpoll;
+ up->run = 0;
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s ACTS backup stopped",
+ ntoa(&peer->srcadr));
+ }
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "clock %s ACTS invalid mode", ntoa(&peer->srcadr));
+ }
+
+ /*
+ * The fudge flag1 is used as an enable/disable; if set either
+ * by the code or via ntpdc, the ACTS calling program is
+ * started; if reset, the phones stop ringing.
+ */
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ up->pollcnt = 0;
+ peer->nextdate = current_time + IDLE;
+ return;
+ }
+
+ /*
+ * Initiate a call to the ACTS service. If we wind up here in
+ * other than state 0, a successful call could not be completed
+ * within minpoll seconds. We advance to the next modem dial
+ * string. If none are left, we log a notice and clear the
+ * enable flag. For future enhancement: call the site RP and
+ * leave an obscene message in his voicemail.
+ */
+ if (sys_phone[up->pollcnt][0] == '\0') {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s ACTS calling program terminated",
+ ntoa(&peer->srcadr));
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+#ifdef DEBUG
+ if (debug)
+ printf("acts: calling program terminated\n");
+#endif
+ up->pollcnt = 0;
+ peer->nextdate = current_time + IDLE;
+ return;
+ }
+
+ /*
+ * Raise DTR, call ACTS and start the answer timeout. We think
+ * it strange if the OK status has not been received from the
+ * modem, but plow ahead anyway.
+ */
+ if (strcmp(pp->a_lastcode, "OK") != 0)
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "clock %s ACTS no modem status",
+ ntoa(&peer->srcadr));
+ (void)ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
+ (void)acts_write(peer, sys_phone[up->pollcnt]);
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "clock %s ACTS calling %s\n",
+ ntoa(&peer->srcadr), sys_phone[up->pollcnt]);
+ up->state = 1;
+ up->pollcnt++;
+ pp->polls++;
+ peer->nextdate = current_time + ANSWER;
+ return;
+}
+
+
+/*
+ * acts_disc - disconnect the call and wait for the ruckus to cool
+ */
+static void
+acts_disc (
+ struct peer *peer
+ )
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * We should never get here other than in state 0, unless a call
+ * has timed out. We drop DTR, which will reliably get the modem
+ * off the air, even while ACTS is hammering away full tilt.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ (void)ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
+ if (up->state > 0) {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "clock %s ACTS call failed %d",
+ ntoa(&peer->srcadr), up->state);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: call failed %d\n", up->state);
+#endif
+ up->state = 0;
+ }
+ peer->nextdate = current_time + WAIT;
+}
+
+
+/*
+ * acts_write - write a message to the serial port
+ */
+static int
+acts_write (
+ struct peer *peer,
+ const char *str
+ )
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int len;
+ int code;
+ char cr = '\r';
+
+ /*
+ * Not much to do here, other than send the message, handle
+ * debug and report faults.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ len = strlen(str);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: state %d send %d %s\n", up->state, len,
+ str);
+#endif
+ code = write(pp->io.fd, str, (unsigned)len) == len;
+ code &= write(pp->io.fd, &cr, 1) == 1;
+ if (!code)
+ refclock_report(peer, CEVNT_FAULT);
+ return (code);
+}
+
+#else
+int refclock_acts_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_arbiter.c b/ntpd/refclock_arbiter.c
new file mode 100644
index 0000000..cf5f92f
--- /dev/null
+++ b/ntpd/refclock_arbiter.c
@@ -0,0 +1,429 @@
+/*
+ * refclock_arbiter - clock driver for Arbiter 1088A/B Satellite
+ * Controlled Clock
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ARBITER)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Arbiter 1088A/B Satellite Controlled Clock.
+ * The claimed accuracy of this clock is 100 ns relative to the PPS
+ * output when receiving four or more satellites.
+ *
+ * The receiver should be configured before starting the NTP daemon, in
+ * order to establish reliable position and operating conditions. It
+ * does not initiate surveying or hold mode. For use with NTP, the
+ * daylight savings time feature should be disables (D0 command) and the
+ * broadcast mode set to operate in UTC (BU command).
+ *
+ * The timecode format supported by this driver is selected by the poll
+ * sequence "B5", which initiates a line in the following format to be
+ * repeated once per second until turned off by the "B0" poll sequence.
+ *
+ * Format B5 (24 ASCII printing characters):
+ *
+ * <cr><lf>i yy ddd hh:mm:ss.000bbb
+ *
+ * on-time = <cr>
+ * i = synchronization flag (' ' = locked, '?' = unlocked)
+ * yy = year of century
+ * ddd = day of year
+ * hh:mm:ss = hours, minutes, seconds
+ * .000 = fraction of second (not used)
+ * bbb = tailing spaces for fill
+ *
+ * The alarm condition is indicated by a '?' at i, which indicates the
+ * receiver is not synchronized. In normal operation, a line consisting
+ * of the timecode followed by the time quality character (TQ) followed
+ * by the receiver status string (SR) is written to the clockstats file.
+ * The time quality character is encoded in IEEE P1344 standard:
+ *
+ * Format TQ (IEEE P1344 estimated worst-case time quality)
+ *
+ * 0 clock locked, maximum accuracy
+ * F clock failure, time not reliable
+ * 4 clock unlocked, accuracy < 1 us
+ * 5 clock unlocked, accuracy < 10 us
+ * 6 clock unlocked, accuracy < 100 us
+ * 7 clock unlocked, accuracy < 1 ms
+ * 8 clock unlocked, accuracy < 10 ms
+ * 9 clock unlocked, accuracy < 100 ms
+ * A clock unlocked, accuracy < 1 s
+ * B clock unlocked, accuracy < 10 s
+ *
+ * The status string is encoded as follows:
+ *
+ * Format SR (25 ASCII printing characters)
+ *
+ * V=vv S=ss T=t P=pdop E=ee
+ *
+ * vv = satellites visible
+ * ss = relative signal strength
+ * t = satellites tracked
+ * pdop = position dilution of precision (meters)
+ * ee = hardware errors
+ *
+ * If flag4 is set, an additional line consisting of the receiver
+ * latitude (LA), longitude (LO) and elevation (LH) (meters) is written
+ * to this file. If channel B is enabled for deviation mode and connected
+ * to a 1-PPS signal, the last two numbers on the line are the deviation
+ * and standard deviation averaged over the last 15 seconds.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS " /* reference ID */
+#define DESCRIPTION "Arbiter 1088A/B GPS Receiver" /* WRU */
+
+#define LENARB 24 /* format B5 timecode length */
+#define MAXSTA 30 /* max length of status string */
+#define MAXPOS 70 /* max length of position string */
+
+/*
+ * ARB unit control structure
+ */
+struct arbunit {
+ l_fp laststamp; /* last receive timestamp */
+ int tcswitch; /* timecode switch/counter */
+ char qualchar; /* IEEE P1344 quality (TQ command) */
+ char status[MAXSTA]; /* receiver status (SR command) */
+ char latlon[MAXPOS]; /* receiver position (lat/lon/alt) */
+};
+
+/*
+ * Function prototypes
+ */
+static int arb_start P((int, struct peer *));
+static void arb_shutdown P((int, struct peer *));
+static void arb_receive P((struct recvbuf *));
+static void arb_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_arbiter = {
+ arb_start, /* start up driver */
+ arb_shutdown, /* shut down driver */
+ arb_poll, /* transmit poll message */
+ noentry, /* not used (old arb_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old arb_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * arb_start - open the devices and initialize data for processing
+ */
+static int
+arb_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct arbunit *)emalloc(sizeof(struct arbunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct arbunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = arb_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ write(pp->io.fd, "B0", 2);
+ return (1);
+}
+
+
+/*
+ * arb_shutdown - shut down the clock
+ */
+static void
+arb_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct arbunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * arb_receive - receive data from the serial interface
+ */
+static void
+arb_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int temp;
+ u_char syncchar; /* synchronization indicator */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct arbunit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained. The program first
+ * sends a TQ and expects the echo followed by the time quality
+ * character. It then sends a B5 starting the timecode broadcast
+ * and expects the echo followed some time later by the on-time
+ * character <cr> and then the <lf> beginning the timecode
+ * itself. Finally, at the <cr> beginning the next timecode at
+ * the next second, the program sends a B0 shutting down the
+ * timecode broadcast.
+ *
+ * If flag4 is set, the program snatches the latitude, longitude
+ * and elevation and writes it to the clockstats file.
+ */
+ if (temp == 0)
+ return;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ if (temp < 3)
+ return;
+ if (up->tcswitch == 0) {
+
+ /*
+ * Collect statistics. If nothing is recogized, just
+ * ignore; sometimes the clock doesn't stop spewing
+ * timecodes for awhile after the B0 commant.
+ */
+ if (!strncmp(pp->a_lastcode, "TQ", 2)) {
+ up->qualchar = pp->a_lastcode[2];
+ write(pp->io.fd, "SR", 2);
+ } else if (!strncmp(pp->a_lastcode, "SR", 2)) {
+ strcpy(up->status, pp->a_lastcode + 2);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ write(pp->io.fd, "LA", 2);
+ else {
+ write(pp->io.fd, "B5", 2);
+ up->tcswitch++;
+ }
+ } else if (!strncmp(pp->a_lastcode, "LA", 2)) {
+ strcpy(up->latlon, pp->a_lastcode + 2);
+ write(pp->io.fd, "LO", 2);
+ } else if (!strncmp(pp->a_lastcode, "LO", 2)) {
+ strcat(up->latlon, " ");
+ strcat(up->latlon, pp->a_lastcode + 2);
+ write(pp->io.fd, "LH", 2);
+ } else if (!strncmp(pp->a_lastcode, "LH", 2)) {
+ strcat(up->latlon, " ");
+ strcat(up->latlon, pp->a_lastcode + 2);
+ write(pp->io.fd, "DB", 2);
+ } else if (!strncmp(pp->a_lastcode, "DB", 2)) {
+ strcat(up->latlon, " ");
+ strcat(up->latlon, pp->a_lastcode + 2);
+ record_clock_stats(&peer->srcadr, up->latlon);
+ write(pp->io.fd, "B5", 2);
+ up->tcswitch++;
+ }
+ return;
+ }
+ pp->lencode = temp;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has valid length, but not in
+ * proper format, we declare bad format and exit. If the
+ * timecode has invalid length, which sometimes occurs when the
+ * B0 amputates the broadcast, we just quietly steal away. Note
+ * that the time quality character and receiver status string is
+ * tacked on the end for clockstats display.
+ */
+ if (pp->lencode == LENARB) {
+ /*
+ * Timecode format B5: "i yy ddd hh:mm:ss.000 "
+ */
+ pp->a_lastcode[LENARB - 2] = up->qualchar;
+ strcat(pp->a_lastcode, up->status);
+ syncchar = ' ';
+ if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d",
+ &syncchar, &pp->year, &pp->day, &pp->hour,
+ &pp->minute, &pp->second) != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ write(pp->io.fd, "B0", 2);
+ return;
+ }
+ } else {
+ write(pp->io.fd, "B0", 2);
+ return;
+ }
+ up->tcswitch++;
+
+ /*
+ * We decode the clock dispersion from the time quality
+ * character.
+ */
+ switch (up->qualchar) {
+
+ case '0': /* locked, max accuracy */
+ pp->disp = 1e-7;
+ break;
+
+ case '4': /* unlock accuracy < 1 us */
+ pp->disp = 1e-6;
+ break;
+
+ case '5': /* unlock accuracy < 10 us */
+ pp->disp = 1e-5;
+ break;
+
+ case '6': /* unlock accuracy < 100 us */
+ pp->disp = 1e-4;
+ break;
+
+ case '7': /* unlock accuracy < 1 ms */
+ pp->disp = .001;
+ break;
+
+ case '8': /* unlock accuracy < 10 ms */
+ pp->disp = .01;
+ break;
+
+ case '9': /* unlock accuracy < 100 ms */
+ pp->disp = .1;
+ break;
+
+ case 'A': /* unlock accuracy < 1 s */
+ pp->disp = 1;
+ break;
+
+ case 'B': /* unlock accuracy < 10 s */
+ pp->disp = 10;
+ break;
+
+ case 'F': /* clock failure */
+ pp->disp = MAXDISPERSE;
+ refclock_report(peer, CEVNT_FAULT);
+ write(pp->io.fd, "B0", 2);
+ return;
+
+ default:
+ pp->disp = MAXDISPERSE;
+ refclock_report(peer, CEVNT_BADREPLY);
+ write(pp->io.fd, "B0", 2);
+ return;
+ }
+ if (syncchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+#ifdef DEBUG
+ if (debug)
+ printf("arbiter: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+ if (up->tcswitch >= NSTAGE)
+ write(pp->io.fd, "B0", 2);
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+}
+
+
+/*
+ * arb_poll - called by the transmit procedure
+ */
+static void
+arb_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arbunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The Arbiter clock responds to a "B5"
+ * by returning a timecode in the format specified above.
+ * Transmission occurs once per second, unless turned off by a
+ * "B0". Note there is no checking on state, since this may not
+ * be the only customer reading the clock. Only one customer
+ * need poll the clock; all others just listen in. If nothing is
+ * heard from the clock for two polls, declare a timeout and
+ * keep going.
+ */
+ pp = peer->procptr;
+ up = (struct arbunit *)pp->unitptr;
+ up->tcswitch = 0;
+ if (write(pp->io.fd, "TQ", 2) != 2) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else
+ pp->polls++;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+}
+
+#else
+int refclock_arbiter_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_arc.c b/ntpd/refclock_arc.c
new file mode 100644
index 0000000..f556da6
--- /dev/null
+++ b/ntpd/refclock_arc.c
@@ -0,0 +1,1529 @@
+/*
+ * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
+static const char arc_version[] = { "V1.3 2003/02/21" };
+
+/* define PRE_NTP420 for compatibility to previous versions of NTP (at least
+ to 4.1.0 */
+#undef PRE_NTP420
+
+#ifndef ARCRON_NOT_KEEN
+#define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
+#endif
+
+#ifndef ARCRON_NOT_MULTIPLE_SAMPLES
+#define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */
+#endif
+
+#ifndef ARCRON_NOT_LEAPSECOND_KEEN
+#ifndef ARCRON_LEAPSECOND_KEEN
+#undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */
+#endif
+#endif
+
+/*
+Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
+Modifications by Damon Hart-Davis, <d@hd.org>, 1997.
+Modifications by Paul Alfille, <palfille@partners.org>, 2003.
+Modifications by Christopher Price, <cprice@cs-home.com>, 2003.
+
+
+THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND. USE AT
+YOUR OWN RISK.
+
+Orginally developed and used with ntp3-5.85 by Derek Mulcahy.
+
+Built against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
+
+This code may be freely copied and used and incorporated in other
+systems providing the disclaimer and notice of authorship are
+reproduced.
+
+-------------------------------------------------------------------------------
+
+Christopher's notes:
+
+MAJOR CHANGES SINCE V1.2
+========================
+ 1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk>
+ 2001-02-17 comp.protocols.time.ntp
+
+ 2) Added WWVB support via clock mode command, localtime/UTC time configured
+ via flag1=(0=UTC, 1=localtime)
+
+ 3) Added ignore resync request via flag2=(0=resync, 1=ignore resync)
+
+ 4) Added simplified conversion from localtime to UTC with dst/bst translation
+
+ 5) Added average signal quality poll
+
+ 6) Fixed a badformat error when no code is available due to stripping
+ \n & \r's
+
+ 7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll
+ routine
+
+ 8) Lots of code cleanup, including standardized DEBUG macros and removal
+ of unused code
+
+-------------------------------------------------------------------------------
+
+Author's original note:
+
+I enclose my ntp driver for the Galleon Systems Arc MSF receiver.
+
+It works (after a fashion) on both Solaris-1 and Solaris-2.
+
+I am currently using ntp3-5.85. I have been running the code for
+about 7 months without any problems. Even coped with the change to BST!
+
+I had to do some funky things to read from the clock because it uses the
+power from the receive lines to drive the transmit lines. This makes the
+code look a bit stupid but it works. I also had to put in some delays to
+allow for the turnaround time from receive to transmit. These delays
+are between characters when requesting a time stamp so that shouldn't affect
+the results too drastically.
+
+...
+
+The bottom line is that it works but could easily be improved. You are
+free to do what you will with the code. I haven't been able to determine
+how good the clock is. I think that this requires a known good clock
+to compare it against.
+
+-------------------------------------------------------------------------------
+
+Damon's notes for adjustments:
+
+MAJOR CHANGES SINCE V1.0
+========================
+ 1) Removal of pollcnt variable that made the clock go permanently
+ off-line once two time polls failed to gain responses.
+
+ 2) Avoiding (at least on Solaris-2) terminal becoming the controlling
+ terminal of the process when we do a low-level open().
+
+ 3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being
+ defined) to try to resync quickly after a potential leap-second
+ insertion or deletion.
+
+ 4) Code significantly slimmer at run-time than V1.0.
+
+
+GENERAL
+=======
+
+ 1) The C preprocessor symbol to have the clock built has been changed
+ from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the
+ possiblity of clashes with other symbols in the future.
+
+ 2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons:
+
+ a) The ARC documentation claims the internal clock is (only)
+ accurate to about 20ms relative to Rugby (plus there must be
+ noticable drift and delay in the ms range due to transmission
+ delays and changing atmospheric effects). This clock is not
+ designed for ms accuracy as NTP has spoilt us all to expect.
+
+ b) The clock oscillator looks like a simple uncompensated quartz
+ crystal of the sort used in digital watches (ie 32768Hz) which
+ can have large temperature coefficients and drifts; it is not
+ clear if this oscillator is properly disciplined to the MSF
+ transmission, but as the default is to resync only once per
+ *day*, we can imagine that it is not, and is free-running. We
+ can minimise drift by resyncing more often (at the cost of
+ reduced battery life), but drift/wander may still be
+ significant.
+
+ c) Note that the bit time of 3.3ms adds to the potential error in
+ the the clock timestamp, since the bit clock of the serial link
+ may effectively be free-running with respect to the host clock
+ and the MSF clock. Actually, the error is probably 1/16th of
+ the above, since the input data is probably sampled at at least
+ 16x the bit rate.
+
+ By keeping the clock marked as not very precise, it will have a
+ fairly large dispersion, and thus will tend to be used as a
+ `backup' time source and sanity checker, which this clock is
+ probably ideal for. For an isolated network without other time
+ sources, this clock can probably be expected to provide *much*
+ better than 1s accuracy, which will be fine.
+
+ By default, PRECISION is set to -4, but experience, especially at a
+ particular geographic location with a particular clock, may allow
+ this to be altered to -5. (Note that skews of +/- 10ms are to be
+ expected from the clock from time-to-time.) This improvement of
+ reported precision can be instigated by setting flag3 to 1, though
+ the PRECISION will revert to the normal value while the clock
+ signal quality is unknown whatever the flag3 setting.
+
+ IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE
+ ANY RESIDUAL SKEW, eg:
+
+ server 127.127.27.0 # ARCRON MSF radio clock unit 0.
+ # Fudge timestamps by about 20ms.
+ fudge 127.127.27.0 time1 0.020
+
+ You will need to observe your system's behaviour, assuming you have
+ some other NTP source to compare it with, to work out what the
+ fudge factor should be. For my Sun SS1 running SunOS 4.1.3_U1 with
+ my MSF clock with my distance from the MSF transmitter, +20ms
+ seemed about right, after some observation.
+
+ 3) REFID has been made "MSFa" to reflect the MSF time source and the
+ ARCRON receiver.
+
+ 4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before
+ forcing a resync since the last attempt. This is picked to give a
+ little less than an hour between resyncs and to try to avoid
+ clashing with any regular event at a regular time-past-the-hour
+ which might cause systematic errors.
+
+ The INITIAL_RESYNC_DELAY is to avoid bothering the clock and
+ running down its batteries unnecesarily if ntpd is going to crash
+ or be killed or reconfigured quickly. If ARCRON_KEEN is defined
+ then this period is long enough for (with normal polling rates)
+ enough time samples to have been taken to allow ntpd to sync to
+ the clock before the interruption for the clock to resync to MSF.
+ This avoids ntpd syncing to another peer first and then
+ almost immediately hopping to the MSF clock.
+
+ The RETRY_RESYNC_TIME is used before rescheduling a resync after a
+ resync failed to reveal a statisfatory signal quality (too low or
+ unknown).
+
+ 5) The clock seems quite jittery, so I have increased the
+ median-filter size from the typical (previous) value of 3. I
+ discard up to half the results in the filter. It looks like maybe
+ 1 sample in 10 or so (maybe less) is a spike, so allow the median
+ filter to discard at least 10% of its entries or 1 entry, whichever
+ is greater.
+
+ 6) Sleeping *before* each character sent to the unit to allow required
+ inter-character time but without introducting jitter and delay in
+ handling the response if possible.
+
+ 7) If the flag ARCRON_KEEN is defined, take time samples whenever
+ possible, even while resyncing, etc. We rely, in this case, on the
+ clock always giving us a reasonable time or else telling us in the
+ status byte at the end of the timestamp that it failed to sync to
+ MSF---thus we should never end up syncing to completely the wrong
+ time.
+
+ 8) If the flag ARCRON_OWN_FILTER is defined, use own versions of
+ refclock median-filter routines to get round small bug in 3-5.90
+ code which does not return the median offset. XXX Removed this
+ bit due NTP Version 4 upgrade - dlm.
+
+ 9) We would appear to have a year-2000 problem with this clock since
+ it returns only the two least-significant digits of the year. But
+ ntpd ignores the year and uses the local-system year instead, so
+ this is in fact not a problem. Nevertheless, we attempt to do a
+ sensible thing with the dates, wrapping them into a 100-year
+ window.
+
+ 10)Logs stats information that can be used by Derek's Tcl/Tk utility
+ to show the status of the clock.
+
+ 11)The clock documentation insists that the number of bits per
+ character to be sent to the clock, and sent by it, is 11, including
+ one start bit and two stop bits. The data format is either 7+even
+ or 8+none.
+
+
+TO-DO LIST
+==========
+
+ * Eliminate use of scanf(), and maybe sprintf().
+
+ * Allow user setting of resync interval to trade battery life for
+ accuracy; maybe could be done via fudge factor or unit number.
+
+ * Possibly note the time since the last resync of the MSF clock to
+ MSF as the age of the last reference timestamp, ie trust the
+ clock's oscillator not very much...
+
+ * Add very slow auto-adjustment up to a value of +/- time2 to correct
+ for long-term errors in the clock value (time2 defaults to 0 so the
+ correction would be disabled by default).
+
+ * Consider trying to use the tty_clk/ppsclock support.
+
+ * Possibly use average or maximum signal quality reported during
+ resync, rather than just the last one, which may be atypical.
+
+*/
+
+
+/* Notes for HKW Elektronik GmBH Radio clock driver */
+/* Author Lyndon David, Sentinet Ltd, Feb 1997 */
+/* These notes seem also to apply usefully to the ARCRON clock. */
+
+/* The HKW clock module is a radio receiver tuned into the Rugby */
+/* MSF time signal tranmitted on 60 kHz. The clock module connects */
+/* to the computer via a serial line and transmits the time encoded */
+/* in 15 bytes at 300 baud 7 bits two stop bits even parity */
+
+/* Clock communications, from the datasheet */
+/* All characters sent to the clock are echoed back to the controlling */
+/* device. */
+/* Transmit time/date information */
+/* syntax ASCII o<cr> */
+/* Character o may be replaced if neccesary by a character whose code */
+/* contains the lowest four bits f(hex) eg */
+/* syntax binary: xxxx1111 00001101 */
+
+/* DHD note:
+You have to wait for character echo + 10ms before sending next character.
+*/
+
+/* The clock replies to this command with a sequence of 15 characters */
+/* which contain the complete time and a final <cr> making 16 characters */
+/* in total. */
+/* The RC computer clock will not reply immediately to this command because */
+/* the start bit edge of the first reply character marks the beginning of */
+/* the second. So the RC Computer Clock will reply to this command at the */
+/* start of the next second */
+/* The characters have the following meaning */
+/* 1. hours tens */
+/* 2. hours units */
+/* 3. minutes tens */
+/* 4. minutes units */
+/* 5. seconds tens */
+/* 6. seconds units */
+/* 7. day of week 1-monday 7-sunday */
+/* 8. day of month tens */
+/* 9. day of month units */
+/* 10. month tens */
+/* 11. month units */
+/* 12. year tens */
+/* 13. year units */
+/* 14. BST/UTC status */
+/* bit 7 parity */
+/* bit 6 always 0 */
+/* bit 5 always 1 */
+/* bit 4 always 1 */
+/* bit 3 always 0 */
+/* bit 2 =1 if UTC is in effect, complementary to the BST bit */
+/* bit 1 =1 if BST is in effect, according to the BST bit */
+/* bit 0 BST/UTC change impending bit=1 in case of change impending */
+/* 15. status */
+/* bit 7 parity */
+/* bit 6 always 0 */
+/* bit 5 always 1 */
+/* bit 4 always 1 */
+/* bit 3 =1 if low battery is detected */
+/* bit 2 =1 if the very last reception attempt failed and a valid */
+/* time information already exists (bit0=1) */
+/* =0 if the last reception attempt was successful */
+/* bit 1 =1 if at least one reception since 2:30 am was successful */
+/* =0 if no reception attempt since 2:30 am was successful */
+/* bit 0 =1 if the RC Computer Clock contains valid time information */
+/* This bit is zero after reset and one after the first */
+/* successful reception attempt */
+
+/* DHD note:
+Also note g<cr> command which confirms that a resync is in progress, and
+if so what signal quality (0--5) is available.
+Also note h<cr> command which starts a resync to MSF signal.
+*/
+
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+
+/*
+ * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/arc%d" /* Device name and unit. */
+#define SPEED B300 /* UART speed (300 baud) */
+#define PRECISION (-4) /* Precision (~63 ms). */
+#define HIGHPRECISION (-5) /* If things are going well... */
+#define REFID "MSFa" /* Reference ID. */
+#define REFID_MSF "MSF" /* Reference ID. */
+#define REFID_DCF77 "DCF" /* Reference ID. */
+#define REFID_WWVB "WWVB" /* Reference ID. */
+#define DESCRIPTION "ARCRON MSF/DCF/WWVB Receiver"
+
+#ifdef PRE_NTP420
+#define MODE ttlmax
+#else
+#define MODE ttl
+#endif
+
+#define LENARC 16 /* Format `o' timecode length. */
+
+#define BITSPERCHAR 11 /* Bits per character. */
+#define BITTIME 0x0DA740E /* Time for 1 bit at 300bps. */
+#define CHARTIME10 0x8888888 /* Time for 10-bit char at 300bps. */
+#define CHARTIME11 0x962FC96 /* Time for 11-bit char at 300bps. */
+#define CHARTIME /* Time for char at 300bps. */ \
+( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
+ (BITSPERCHAR * BITTIME) ) )
+
+ /* Allow for UART to accept char half-way through final stop bit. */
+#define INITIALOFFSET (u_int32)(-BITTIME/2)
+
+ /*
+ charoffsets[x] is the time after the start of the second that byte
+ x (with the first byte being byte 1) is received by the UART,
+ assuming that the initial edge of the start bit of the first byte
+ is on-time. The values are represented as the fractional part of
+ an l_fp.
+
+ We store enough values to have the offset of each byte including
+ the trailing \r, on the assumption that the bytes follow one
+ another without gaps.
+ */
+ static const u_int32 charoffsets[LENARC+1] = {
+#if BITSPERCHAR == 11 /* Usual case. */
+ /* Offsets computed as accurately as possible... */
+ 0,
+ INITIALOFFSET + 0x0962fc96, /* 1 chars, 11 bits */
+ INITIALOFFSET + 0x12c5f92c, /* 2 chars, 22 bits */
+ INITIALOFFSET + 0x1c28f5c3, /* 3 chars, 33 bits */
+ INITIALOFFSET + 0x258bf259, /* 4 chars, 44 bits */
+ INITIALOFFSET + 0x2eeeeeef, /* 5 chars, 55 bits */
+ INITIALOFFSET + 0x3851eb85, /* 6 chars, 66 bits */
+ INITIALOFFSET + 0x41b4e81b, /* 7 chars, 77 bits */
+ INITIALOFFSET + 0x4b17e4b1, /* 8 chars, 88 bits */
+ INITIALOFFSET + 0x547ae148, /* 9 chars, 99 bits */
+ INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
+ INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
+ INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
+ INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
+ INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
+ INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
+ INITIALOFFSET + 0x962fc963 /* 16 chars, 176 bits */
+#else
+ /* Offsets computed with a small rounding error... */
+ 0,
+ INITIALOFFSET + 1 * CHARTIME,
+ INITIALOFFSET + 2 * CHARTIME,
+ INITIALOFFSET + 3 * CHARTIME,
+ INITIALOFFSET + 4 * CHARTIME,
+ INITIALOFFSET + 5 * CHARTIME,
+ INITIALOFFSET + 6 * CHARTIME,
+ INITIALOFFSET + 7 * CHARTIME,
+ INITIALOFFSET + 8 * CHARTIME,
+ INITIALOFFSET + 9 * CHARTIME,
+ INITIALOFFSET + 10 * CHARTIME,
+ INITIALOFFSET + 11 * CHARTIME,
+ INITIALOFFSET + 12 * CHARTIME,
+ INITIALOFFSET + 13 * CHARTIME,
+ INITIALOFFSET + 14 * CHARTIME,
+ INITIALOFFSET + 15 * CHARTIME,
+ INITIALOFFSET + 16 * CHARTIME
+#endif
+ };
+
+#define DEFAULT_RESYNC_TIME (57*60) /* Gap between resync attempts (s). */
+#define RETRY_RESYNC_TIME (27*60) /* Gap to emergency resync attempt. */
+#ifdef ARCRON_KEEN
+#define INITIAL_RESYNC_DELAY 500 /* Delay before first resync. */
+#else
+#define INITIAL_RESYNC_DELAY 50 /* Delay before first resync. */
+#endif
+
+ static const int moff[12] =
+{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+/* Flags for a raw open() of the clock serial device. */
+#ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
+#define OPEN_FLAGS (O_RDWR | O_NOCTTY)
+#else /* Oh well, it may not matter... */
+#define OPEN_FLAGS (O_RDWR)
+#endif
+
+
+/* Length of queue of command bytes to be sent. */
+#define CMDQUEUELEN 4 /* Enough for two cmds + each \r. */
+/* Queue tick time; interval in seconds between chars taken off queue. */
+/* Must be >= 2 to allow o\r response to come back uninterrupted. */
+#define QUEUETICK 2 /* Allow o\r reply to finish. */
+
+/*
+ * ARC unit control structure
+ */
+struct arcunit {
+ l_fp lastrec; /* Time tag for the receive time (system). */
+ int status; /* Clock status. */
+
+ int quality; /* Quality of reception 0--5 for unit. */
+ /* We may also use the values -1 or 6 internally. */
+ u_long quality_stamp; /* Next time to reset quality average. */
+
+ u_long next_resync; /* Next resync time (s) compared to current_time. */
+ int resyncing; /* Resync in progress if true. */
+
+ /* In the outgoing queue, cmdqueue[0] is next to be sent. */
+ char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
+
+ u_long saved_flags; /* Saved fudge flags. */
+};
+
+#ifdef ARCRON_LEAPSECOND_KEEN
+/* The flag `possible_leap' is set non-zero when any MSF unit
+ thinks a leap-second may have happened.
+
+ Set whenever we receive a valid time sample in the first hour of
+ the first day of the first/seventh months.
+
+ Outside the special hour this value is unconditionally set
+ to zero by the receive routine.
+
+ On finding itself in this timeslot, as long as the value is
+ non-negative, the receive routine sets it to a positive value to
+ indicate a resync to MSF should be performed.
+
+ In the poll routine, if this value is positive and we are not
+ already resyncing (eg from a sync that started just before
+ midnight), start resyncing and set this value negative to
+ indicate that a leap-triggered resync has been started. Having
+ set this negative prevents the receive routine setting it
+ positive and thus prevents multiple resyncs during the witching
+ hour.
+ */
+static int possible_leap = 0; /* No resync required by default. */
+#endif
+
+#if 0
+static void dummy_event_handler P((struct peer *));
+static void arc_event_handler P((struct peer *));
+#endif /* 0 */
+
+#define QUALITY_UNKNOWN -1 /* Indicates unknown clock quality. */
+#define MIN_CLOCK_QUALITY 0 /* Min quality clock will return. */
+#define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
+#define MAX_CLOCK_QUALITY 5 /* Max quality clock will return. */
+
+/*
+ * Function prototypes
+ */
+static int arc_start P((int, struct peer *));
+static void arc_shutdown P((int, struct peer *));
+static void arc_receive P((struct recvbuf *));
+static void arc_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_arc = {
+ arc_start, /* start up driver */
+ arc_shutdown, /* shut down driver */
+ arc_poll, /* transmit poll message */
+ noentry, /* not used (old arc_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old arc_buginfo) */
+ NOFLAGS /* not used */
+};
+
+/* Queue us up for the next tick. */
+#define ENQUEUE(up) \
+ do { \
+ peer->nextaction = current_time + QUEUETICK; \
+ } while(0)
+
+/* Placeholder event handler---does nothing safely---soaks up loose tick. */
+static void
+dummy_event_handler(
+ struct peer *peer
+ )
+{
+#ifdef DEBUG
+ if(debug) { printf("arc: dummy_event_handler() called.\n"); }
+#endif
+}
+
+/*
+Normal event handler.
+
+Take first character off queue and send to clock if not a null.
+
+Shift characters down and put a null on the end.
+
+We assume that there is no parallelism so no race condition, but even
+if there is nothing bad will happen except that we might send some bad
+data to the clock once in a while.
+*/
+static void
+arc_event_handler(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp = peer->procptr;
+ register struct arcunit *up = (struct arcunit *)pp->unitptr;
+ int i;
+ char c;
+#ifdef DEBUG
+ if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
+#endif
+
+ c = up->cmdqueue[0]; /* Next char to be sent. */
+ /* Shift down characters, shifting trailing \0 in at end. */
+ for(i = 0; i < CMDQUEUELEN; ++i)
+ { up->cmdqueue[i] = up->cmdqueue[i+1]; }
+
+ /* Don't send '\0' characters. */
+ if(c != '\0') {
+ if(write(pp->io.fd, &c, 1) != 1) {
+ msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
+ }
+#ifdef DEBUG
+ else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
+#endif
+ }
+
+ ENQUEUE(up);
+}
+
+/*
+ * arc_start - open the devices and initialize data for processing
+ */
+static int
+arc_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+#ifdef HAVE_TERMIOS
+ struct termios arg;
+#endif
+
+ msyslog(LOG_NOTICE, "ARCRON: %s: opening unit %d", arc_version, unit);
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: %s: attempt to open unit %d.\n", arc_version, unit);
+ }
+#endif
+
+ /* Prevent a ridiculous device number causing overflow of device[]. */
+ if((unit < 0) || (unit > 255)) { return(0); }
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED, LDISC_CLK)))
+ return(0);
+#ifdef DEBUG
+ if(debug) { printf("arc: unit %d using open().\n", unit); }
+#endif
+ fd = open(device, OPEN_FLAGS);
+ if(fd < 0) {
+#ifdef DEBUG
+ if(debug) { printf("arc: failed [open()] to open %s.\n", device); }
+#endif
+ return(0);
+ }
+
+ fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */
+#ifdef DEBUG
+ if(debug)
+ { printf("arc: opened RS232 port with file descriptor %d.\n", fd); }
+#endif
+
+#ifdef HAVE_TERMIOS
+
+ arg.c_iflag = IGNBRK | ISTRIP;
+ arg.c_oflag = 0;
+ arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
+ arg.c_lflag = 0;
+ arg.c_cc[VMIN] = 1;
+ arg.c_cc[VTIME] = 0;
+
+ tcsetattr(fd, TCSANOW, &arg);
+
+#else
+
+ msyslog(LOG_ERR, "ARCRON: termios not supported in this driver");
+ (void)close(fd);
+
+ return 0;
+
+#endif
+
+ up = (struct arcunit *) emalloc(sizeof(struct arcunit));
+ if(!up) { (void) close(fd); return(0); }
+ /* Set structure to all zeros... */
+ memset((char *)up, 0, sizeof(struct arcunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = arc_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if(!io_addclock(&pp->io)) { (void) close(fd); free(up); return(0); }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ peer->stratum = 2; /* Default to stratum 2 not 0. */
+ pp->clockdesc = DESCRIPTION;
+ if (peer->MODE > 3) {
+ msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE);
+ return 0;
+ }
+#ifdef DEBUG
+ if(debug) { printf("arc: mode = %d.\n", peer->MODE); }
+#endif
+ switch (peer->MODE) {
+ case 1:
+ memcpy((char *)&pp->refid, REFID_MSF, 4);
+ break;
+ case 2:
+ memcpy((char *)&pp->refid, REFID_DCF77, 4);
+ break;
+ case 3:
+ memcpy((char *)&pp->refid, REFID_WWVB, 4);
+ break;
+ default:
+ memcpy((char *)&pp->refid, REFID, 4);
+ break;
+ }
+ /* Spread out resyncs so that they should remain separated. */
+ up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
+
+#if 0 /* Not needed because of zeroing of arcunit structure... */
+ up->resyncing = 0; /* Not resyncing yet. */
+ up->saved_flags = 0; /* Default is all flags off. */
+ /* Clear send buffer out... */
+ {
+ int i;
+ for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
+ }
+#endif
+
+#ifdef ARCRON_KEEN
+ up->quality = QUALITY_UNKNOWN; /* Trust the clock immediately. */
+#else
+ up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
+#endif
+
+ peer->action = arc_event_handler;
+
+ ENQUEUE(up);
+
+ return(1);
+}
+
+
+/*
+ * arc_shutdown - shut down the clock
+ */
+static void
+arc_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+
+ peer->action = dummy_event_handler;
+
+ pp = peer->procptr;
+ up = (struct arcunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+/*
+Compute space left in output buffer.
+*/
+static int
+space_left(
+ register struct arcunit *up
+ )
+{
+ int spaceleft;
+
+ /* Compute space left in buffer after any pending output. */
+ for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
+ { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
+ return(spaceleft);
+}
+
+/*
+Send command by copying into command buffer as far forward as possible,
+after any pending output.
+
+Indicate an error by returning 0 if there is not space for the command.
+*/
+static int
+send_slow(
+ register struct arcunit *up,
+ int fd,
+ const char *s
+ )
+{
+ int sl = strlen(s);
+ int spaceleft = space_left(up);
+
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
+#endif
+ if(spaceleft < sl) { /* Should not normally happen... */
+#ifdef DEBUG
+ msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
+ sl, spaceleft);
+#endif
+ return(0); /* FAILED! */
+ }
+
+ /* Copy in the command to be sent. */
+ while(*s) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
+
+ return(1);
+}
+
+
+/* Macro indicating action we will take for different quality values. */
+#define quality_action(q) \
+(((q) == QUALITY_UNKNOWN) ? "UNKNOWN, will use clock anyway" : \
+ (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
+ "OK, will use clock"))
+
+ /*
+ * arc_receive - receive data from the serial interface
+ */
+ static void
+arc_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char c;
+ int i, n, wday, month, flags, status;
+ int arc_last_offset;
+ static int quality_average = 0;
+ static int quality_sum = 0;
+ static int quality_polls = 0;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct arcunit *)pp->unitptr;
+
+
+ /*
+ If the command buffer is empty, and we are resyncing, insert a
+ g\r quality request into it to poll for signal quality again.
+ */
+ if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
+#endif
+ send_slow(up, pp->io.fd, "g\r");
+ }
+
+ /*
+ The `arc_last_offset' is the offset in lastcode[] of the last byte
+ received, and which we assume actually received the input
+ timestamp.
+
+ (When we get round to using tty_clk and it is available, we
+ assume that we will receive the whole timecode with the
+ trailing \r, and that that \r will be timestamped. But this
+ assumption also works if receive the characters one-by-one.)
+ */
+ arc_last_offset = pp->lencode+rbufp->recv_length - 1;
+
+ /*
+ We catch a timestamp iff:
+
+ * The command code is `o' for a timestamp.
+
+ * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
+ exactly char in the buffer (the command code) so that we
+ only sample the first character of the timecode as our
+ `on-time' character.
+
+ * The first character in the buffer is not the echoed `\r'
+ from the `o` command (so if we are to timestamp an `\r' it
+ must not be first in the receive buffer with lencode==1.
+ (Even if we had other characters following it, we probably
+ would have a premature timestamp on the '\r'.)
+
+ * We have received at least one character (I cannot imagine
+ how it could be otherwise, but anyway...).
+ */
+ c = rbufp->recv_buffer[0];
+ if((pp->a_lastcode[0] == 'o') &&
+#ifndef ARCRON_MULTIPLE_SAMPLES
+ (pp->lencode == 1) &&
+#endif
+ ((pp->lencode != 1) || (c != '\r')) &&
+ (arc_last_offset >= 1)) {
+ /* Note that the timestamp should be corrected if >1 char rcvd. */
+ l_fp timestamp;
+ timestamp = rbufp->recv_time;
+#ifdef DEBUG
+ if(debug) { /* Show \r as `R', other non-printing char as `?'. */
+ printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
+ ((c == '\r') ? 'R' : (isgraph((int)c) ? c : '?')),
+ rbufp->recv_length);
+ }
+#endif
+
+ /*
+ Now correct timestamp by offset of last byte received---we
+ subtract from the receive time the delay implied by the
+ extra characters received.
+
+ Reject the input if the resulting code is too long, but
+ allow for the trailing \r, normally not used but a good
+ handle for tty_clk or somesuch kernel timestamper.
+ */
+ if(arc_last_offset > LENARC) {
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: input code too long (%d cf %d); rejected.\n",
+ arc_last_offset, LENARC);
+ }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
+#ifdef DEBUG
+ if(debug > 1) {
+ printf(
+ "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
+ ((rbufp->recv_length > 1) ? "*** " : ""),
+ rbufp->recv_length,
+ arc_last_offset,
+ mfptoms((unsigned long)0,
+ charoffsets[arc_last_offset],
+ 1));
+ }
+#endif
+
+#ifdef ARCRON_MULTIPLE_SAMPLES
+ /*
+ If taking multiple samples, capture the current adjusted
+ sample iff:
+
+ * No timestamp has yet been captured (it is zero), OR
+
+ * This adjusted timestamp is earlier than the one already
+ captured, on the grounds that this one suffered less
+ delay in being delivered to us and is more accurate.
+
+ */
+ if(L_ISZERO(&(up->lastrec)) ||
+ L_ISGEQ(&(up->lastrec), &timestamp))
+#endif
+ {
+#ifdef DEBUG
+ if(debug > 1) {
+ printf("arc: system timestamp captured.\n");
+#ifdef ARCRON_MULTIPLE_SAMPLES
+ if(!L_ISZERO(&(up->lastrec))) {
+ l_fp diff;
+ diff = up->lastrec;
+ L_SUB(&diff, &timestamp);
+ printf("arc: adjusted timestamp by -%sms.\n",
+ mfptoms(diff.l_i, diff.l_f, 3));
+ }
+#endif
+ }
+#endif
+ up->lastrec = timestamp;
+ }
+
+ }
+
+ /* Just in case we still have lots of rubbish in the buffer... */
+ /* ...and to avoid the same timestamp being reused by mistake, */
+ /* eg on receipt of the \r coming in on its own after the */
+ /* timecode. */
+ if(pp->lencode >= LENARC) {
+#ifdef DEBUG
+ if(debug && (rbufp->recv_buffer[0] != '\r'))
+ { printf("arc: rubbish in pp->a_lastcode[].\n"); }
+#endif
+ pp->lencode = 0;
+ return;
+ }
+
+ /* Append input to code buffer, avoiding overflow. */
+ for(i = 0; i < rbufp->recv_length; i++) {
+ if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
+ c = rbufp->recv_buffer[i];
+
+ /* Drop trailing '\r's and drop `h' command echo totally. */
+ if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
+
+ /*
+ If we've just put an `o' in the lastcode[0], clear the
+ timestamp in anticipation of a timecode arriving soon.
+
+ We would expect to get to process this before any of the
+ timecode arrives.
+ */
+ if((c == 'o') && (pp->lencode == 1)) {
+ L_CLR(&(up->lastrec));
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: clearing timestamp.\n"); }
+#endif
+ }
+ }
+ if (pp->lencode == 0) return;
+
+ /* Handle a quality message. */
+ if(pp->a_lastcode[0] == 'g') {
+ int r, q;
+
+ if(pp->lencode < 3) { return; } /* Need more data... */
+ r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
+ q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
+ if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
+ ((r & 0x70) != 0x30)) {
+ /* Badly formatted response. */
+#ifdef DEBUG
+ if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
+#endif
+ return;
+ }
+ if(r == '3') { /* Only use quality value whilst sync in progress. */
+ if (up->quality_stamp < current_time) {
+ struct calendar cal;
+ l_fp new_stamp;
+
+ get_systime (&new_stamp);
+ caljulian (new_stamp.l_ui, &cal);
+ up->quality_stamp =
+ current_time + 60 - cal.second + 5;
+ quality_sum = 0;
+ quality_polls = 0;
+ }
+ quality_sum += (q & 0xf);
+ quality_polls++;
+ quality_average = (quality_sum / quality_polls);
+#ifdef DEBUG
+ if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
+#endif
+ } else if( /* (r == '2') && */ up->resyncing) {
+ up->quality = quality_average;
+#ifdef DEBUG
+ if(debug)
+ {
+ printf("arc: sync finished, signal quality %d: %s\n",
+ up->quality,
+ quality_action(up->quality));
+ }
+#endif
+ msyslog(LOG_NOTICE,
+ "ARCRON: sync finished, signal quality %d: %s",
+ up->quality,
+ quality_action(up->quality));
+ up->resyncing = 0; /* Resync is over. */
+ quality_average = 0;
+ quality_sum = 0;
+ quality_polls = 0;
+
+#ifdef ARCRON_KEEN
+ /* Clock quality dubious; resync earlier than usual. */
+ if((up->quality == QUALITY_UNKNOWN) ||
+ (up->quality < MIN_CLOCK_QUALITY_OK))
+ { up->next_resync = current_time + RETRY_RESYNC_TIME; }
+#endif
+ }
+ pp->lencode = 0;
+ return;
+ }
+
+ /* Stop now if this is not a timecode message. */
+ if(pp->a_lastcode[0] != 'o') {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /* If we don't have enough data, wait for more... */
+ if(pp->lencode < LENARC) { return; }
+
+
+ /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
+#ifdef DEBUG
+ if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
+#endif
+
+ /* But check that we actually captured a system timestamp on it. */
+ if(L_ISZERO(&(up->lastrec))) {
+#ifdef DEBUG
+ if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ /*
+ Append a mark of the clock's received signal quality for the
+ benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
+ quality value to `6' for his s/w) and terminate the string for
+ sure. This should not go off the buffer end.
+ */
+ pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
+ '6' : ('0' + up->quality));
+ pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
+
+#ifdef PRE_NTP420
+ /* We don't use the micro-/milli- second part... */
+ pp->usec = 0;
+ pp->msec = 0;
+#else
+ /* We don't use the nano-second part... */
+ pp->nsec = 0;
+#endif
+ n = sscanf(pp->a_lastcode, "o%2d%2d%2d%1d%2d%2d%2d%1d%1d",
+ &pp->hour, &pp->minute, &pp->second,
+ &wday, &pp->day, &month, &pp->year, &flags, &status);
+
+ /* Validate format and numbers. */
+ if(n != 9) {
+#ifdef DEBUG
+ /* Would expect to have caught major problems already... */
+ if(debug) { printf("arc: badly formatted data.\n"); }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ /*
+ Validate received values at least enough to prevent internal
+ array-bounds problems, etc.
+ */
+ if((pp->hour < 0) || (pp->hour > 23) ||
+ (pp->minute < 0) || (pp->minute > 59) ||
+ (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
+ (wday < 1) || (wday > 7) ||
+ (pp->day < 1) || (pp->day > 31) ||
+ (month < 1) || (month > 12) ||
+ (pp->year < 0) || (pp->year > 99)) {
+ /* Data out of range. */
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+
+ if(peer->MODE == 0) { /* compatiblity to original version */
+ int bst = flags;
+ /* Check that BST/UTC bits are the complement of one another. */
+ if(!(bst & 2) == !(bst & 4)) {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ }
+ if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
+
+ /* Year-2000 alert! */
+ /* Attempt to wrap 2-digit date into sensible window. */
+ if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* Y2KFixes */
+ pp->year += 1900; /* use full four-digit year */ /* Y2KFixes */
+ /*
+ Attempt to do the right thing by screaming that the code will
+ soon break when we get to the end of its useful life. What a
+ hero I am... PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
+ */
+ if(pp->year >= YEAR_PIVOT+2000-2 ) { /* Y2KFixes */
+ /*This should get attention B^> */
+ msyslog(LOG_NOTICE,
+ "ARCRON: fix me! EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
+ }
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
+ n,
+ pp->hour, pp->minute, pp->second,
+ pp->day, month, pp->year, flags, status);
+ }
+#endif
+
+ /*
+ The status value tested for is not strictly supported by the
+ clock spec since the value of bit 2 (0x4) is claimed to be
+ undefined for MSF, yet does seem to indicate if the last resync
+ was successful or not.
+ */
+ pp->leap = LEAP_NOWARNING;
+ status &= 0x7;
+ if(status == 0x3) {
+ if(status != up->status)
+ { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
+ } else {
+ if(status != up->status) {
+ msyslog(LOG_NOTICE, "ARCRON: signal lost");
+ pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
+ up->status = status;
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+ up->status = status;
+
+ if (peer->MODE == 0) { /* compatiblity to original version */
+ int bst = flags;
+
+ pp->day += moff[month - 1];
+
+ if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
+
+ /* Convert to UTC if required */
+ if(bst & 2) {
+ pp->hour--;
+ if (pp->hour < 0) {
+ pp->hour = 23;
+ pp->day--;
+ /* If we try to wrap round the year
+ * (BST on 1st Jan), reject.*/
+ if(pp->day < 0) {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ }
+ }
+ }
+
+ if(peer->MODE > 0) {
+ if(pp->sloppyclockflag & CLK_FLAG1) {
+ struct tm local;
+ struct tm *gmtp;
+ time_t unixtime;
+
+ /*
+ * Convert to GMT for sites that distribute localtime.
+ * This means we have to do Y2K conversion on the
+ * 2-digit year; otherwise, we get the time wrong.
+ */
+
+ local.tm_year = pp->year-1900;
+ local.tm_mon = month-1;
+ local.tm_mday = pp->day;
+ local.tm_hour = pp->hour;
+ local.tm_min = pp->minute;
+ local.tm_sec = pp->second;
+ switch (peer->MODE) {
+ case 1:
+ local.tm_isdst = (flags & 2);
+ break;
+ case 2:
+ local.tm_isdst = (flags & 2);
+ break;
+ case 3:
+ switch (flags & 3) {
+ case 0: /* It is unclear exactly when the
+ Arcron changes from DST->ST and
+ ST->DST. Testing has shown this
+ to be irregular. For the time
+ being, let the OS decide. */
+ local.tm_isdst = 0;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 00 (0)\n");
+#endif
+ break;
+ case 1: /* dst->st time */
+ local.tm_isdst = -1;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 01 (1)\n");
+#endif
+ break;
+ case 2: /* st->dst time */
+ local.tm_isdst = -1;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 10 (2)\n");
+#endif
+ break;
+ case 3: /* dst time */
+ local.tm_isdst = 1;
+#ifdef DEBUG
+ if (debug)
+ printf ("arc: DST = 11 (3)\n");
+#endif
+ break;
+ }
+ break;
+ default:
+ msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
+ peer->MODE);
+ return;
+ break;
+ }
+ unixtime = mktime (&local);
+ if ((gmtp = gmtime (&unixtime)) == NULL)
+ {
+ pp->lencode = 0;
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+ pp->year = gmtp->tm_year+1900;
+ month = gmtp->tm_mon+1;
+ pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
+ /* pp->day = gmtp->tm_yday; */
+ pp->hour = gmtp->tm_hour;
+ pp->minute = gmtp->tm_min;
+ pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+ if (debug)
+ {
+ printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
+ pp->second);
+ }
+#endif
+ } else
+ {
+ /*
+ * For more rational sites distributing UTC
+ */
+ pp->day = ymd2yd(pp->year,month,pp->day);
+ }
+ }
+
+ if (peer->MODE == 0) { /* compatiblity to original version */
+ /* If clock signal quality is
+ * unknown, revert to default PRECISION...*/
+ if(up->quality == QUALITY_UNKNOWN) {
+ peer->precision = PRECISION;
+ } else { /* ...else improve precision if flag3 is set... */
+ peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+ HIGHPRECISION : PRECISION);
+ }
+ } else {
+ if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
+ peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+ HIGHPRECISION : PRECISION);
+ } else if (up->quality == QUALITY_UNKNOWN) {
+ peer->precision = PRECISION;
+ } else {
+ peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+ HIGHPRECISION : PRECISION);
+ }
+ }
+
+ /* Notice and log any change (eg from initial defaults) for flags. */
+ if(up->saved_flags != pp->sloppyclockflag) {
+#ifdef DEBUG
+ msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
+ ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
+ ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
+ ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
+ ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
+ /* Note effects of flags changing... */
+ if(debug) {
+ printf("arc: PRECISION = %d.\n", peer->precision);
+ }
+#endif
+ up->saved_flags = pp->sloppyclockflag;
+ }
+
+ /* Note time of last believable timestamp. */
+ pp->lastrec = up->lastrec;
+
+#ifdef ARCRON_LEAPSECOND_KEEN
+ /* Find out if a leap-second might just have happened...
+ (ie is this the first hour of the first day of Jan or Jul?)
+ */
+ if((pp->hour == 0) &&
+ (pp->day == 1) &&
+ ((month == 1) || (month == 7))) {
+ if(possible_leap >= 0) {
+ /* A leap may have happened, and no resync has started yet...*/
+ possible_leap = 1;
+ }
+ } else {
+ /* Definitely not leap-second territory... */
+ possible_leap = 0;
+ }
+#endif
+
+ if (!refclock_process(pp)) {
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+}
+
+
+/* request_time() sends a time request to the clock with given peer. */
+/* This automatically reports a fault if necessary. */
+/* No data should be sent after this until arc_poll() returns. */
+static void request_time P((int, struct peer *));
+static void
+request_time(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp = peer->procptr;
+ register struct arcunit *up = (struct arcunit *)pp->unitptr;
+#ifdef DEBUG
+ if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
+#endif
+ if (!send_slow(up, pp->io.fd, "o\r")) {
+#ifdef DEBUG
+ if (debug) {
+ printf("arc: unit %d: problem sending", unit);
+ }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ pp->polls++;
+}
+
+/*
+ * arc_poll - called by the transmit procedure
+ */
+static void
+arc_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+ int resync_needed; /* Should we start a resync? */
+
+ pp = peer->procptr;
+ up = (struct arcunit *)pp->unitptr;
+#if 0
+ pp->lencode = 0;
+ memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
+#endif
+
+#if 0
+ /* Flush input. */
+ tcflush(pp->io.fd, TCIFLUSH);
+#endif
+
+ /* Resync if our next scheduled resync time is here or has passed. */
+ resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
+ (up->next_resync <= current_time) );
+
+#ifdef ARCRON_LEAPSECOND_KEEN
+ /*
+ Try to catch a potential leap-second insertion or deletion quickly.
+
+ In addition to the normal NTP fun of clocks that don't report
+ leap-seconds spooking their hosts, this clock does not even
+ sample the radio sugnal the whole time, so may miss a
+ leap-second insertion or deletion for up to a whole sample
+ time.
+
+ To try to minimise this effect, if in the first few minutes of
+ the day immediately following a leap-second-insertion point
+ (ie in the first hour of the first day of the first and sixth
+ months), and if the last resync was in the previous day, and a
+ resync is not already in progress, resync the clock
+ immediately.
+
+ */
+ if((possible_leap > 0) && /* Must be 00:XX 01/0{1,7}/XXXX. */
+ (!up->resyncing)) { /* No resync in progress yet. */
+ resync_needed = 1;
+ possible_leap = -1; /* Prevent multiple resyncs. */
+ msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
+ }
+#endif
+
+ /* Do a resync if required... */
+ if(resync_needed) {
+ /* First, reset quality value to `unknown' so we can detect */
+ /* when a quality message has been responded to by this */
+ /* being set to some other value. */
+ up->quality = QUALITY_UNKNOWN;
+
+ /* Note that we are resyncing... */
+ up->resyncing = 1;
+
+ /* Now actually send the resync command and an immediate poll. */
+#ifdef DEBUG
+ if(debug) { printf("arc: sending resync command (h\\r).\n"); }
+#endif
+ msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
+ send_slow(up, pp->io.fd, "h\r");
+
+ /* Schedule our next resync... */
+ up->next_resync = current_time + DEFAULT_RESYNC_TIME;
+
+ /* Drop through to request time if appropriate. */
+ }
+
+ /* If clock quality is too poor to trust, indicate a fault. */
+ /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
+ /* we'll cross our fingers and just hope that the thing */
+ /* synced so quickly we did not catch it---we'll */
+ /* double-check the clock is OK elsewhere. */
+ if(
+#ifdef ARCRON_KEEN
+ (up->quality != QUALITY_UNKNOWN) &&
+#else
+ (up->quality == QUALITY_UNKNOWN) ||
+#endif
+ (up->quality < MIN_CLOCK_QUALITY_OK)) {
+#ifdef DEBUG
+ if(debug) {
+ printf("arc: clock quality %d too poor.\n", up->quality);
+ }
+#endif
+ pp->lencode = 0;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ /* This is the normal case: request a timestamp. */
+ request_time(unit, peer);
+}
+
+#else
+int refclock_arc_bs;
+#endif
diff --git a/ntpd/refclock_as2201.c b/ntpd/refclock_as2201.c
new file mode 100644
index 0000000..f04d417b
--- /dev/null
+++ b/ntpd/refclock_as2201.c
@@ -0,0 +1,388 @@
+/*
+ * refclock_as2201 - clock driver for the Austron 2201A GPS
+ * Timing Receiver
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_AS2201)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Austron 2200A/2201A GPS Receiver with
+ * Buffered RS-232-C Interface Module. Note that the original 2200/2201
+ * receivers will not work reliably with this driver, since the older
+ * design cannot accept input commands at any reasonable data rate.
+ *
+ * The program sends a "*toc\r" to the radio and expects a response of
+ * the form "yy:ddd:hh:mm:ss.mmm\r" where yy = year of century, ddd =
+ * day of year, hh:mm:ss = second of day and mmm = millisecond of
+ * second. Then, it sends statistics commands to the radio and expects
+ * a multi-line reply showing the corresponding statistics or other
+ * selected data. Statistics commands are sent in order as determined by
+ * a vector of commands; these might have to be changed with different
+ * radio options. If flag4 of the fudge configuration command is set to
+ * 1, the statistics data are written to the clockstats file for later
+ * processing.
+ *
+ * In order for this code to work, the radio must be placed in non-
+ * interactive mode using the "off" command and with a single <cr>
+ * response using the "term cr" command. The setting of the "echo"
+ * and "df" commands does not matter. The radio should select UTC
+ * timescale using the "ts utc" command.
+ *
+ * There are two modes of operation for this driver. The first with
+ * default configuration is used with stock kernels and serial-line
+ * drivers and works with almost any machine. In this mode the driver
+ * assumes the radio captures a timestamp upon receipt of the "*" that
+ * begins the driver query. Accuracies in this mode are in the order of
+ * a millisecond or two and the receiver can be connected to only one
+ * host.
+ *
+ * The second mode of operation can be used for SunOS kernels that have
+ * been modified with the ppsclock streams module included in this
+ * distribution. The mode is enabled if flag3 of the fudge configuration
+ * command has been set to 1. In this mode a precise timestamp is
+ * available using a gadget box and 1-pps signal from the receiver. This
+ * improves the accuracy to the order of a few tens of microseconds. In
+ * addition, the serial output and 1-pps signal can be bussed to more
+ * than one hosts, but only one of them should be connected to the
+ * radio input data line.
+ */
+
+/*
+ * GPS Definitions
+ */
+#define SMAX 200 /* statistics buffer length */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "Austron 2201A GPS Receiver" /* WRU */
+
+#define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm timecode lngth */
+
+/*
+ * AS2201 unit control structure.
+ */
+struct as2201unit {
+ char *lastptr; /* statistics buffer pointer */
+ char stats[SMAX]; /* statistics buffer */
+ int linect; /* count of lines remaining */
+ int index; /* current statistics command */
+};
+
+/*
+ * Radio commands to extract statitistics
+ *
+ * A command consists of an ASCII string terminated by a <cr> (\r). The
+ * command list consist of a sequence of commands terminated by a null
+ * string ("\0"). One command from the list is sent immediately
+ * following each received timecode (*toc\r command) and the ASCII
+ * strings received from the radio are saved along with the timecode in
+ * the clockstats file. Subsequent commands are sent at each timecode,
+ * with the last one in the list followed by the first one. The data
+ * received from the radio consist of ASCII strings, each terminated by
+ * a <cr> (\r) character. The number of strings for each command is
+ * specified as the first line of output as an ASCII-encode number. Note
+ * that the ETF command requires the Input Buffer Module and the LORAN
+ * commands require the LORAN Assist Module. However, if these modules
+ * are not installed, the radio and this driver will continue to operate
+ * successfuly, but no data will be captured for these commands.
+ */
+static char stat_command[][30] = {
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "ID;OPT;VER\r", /* model; options; software version */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "POS;PPS;PPSOFF\r", /* position, pps source, offsets */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "UTC\r", /* UTC leap info */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "OSC;ET;TEMP\r", /* osc type; tune volts; oven temp */
+ "\0" /* end of table */
+};
+
+/*
+ * Function prototypes
+ */
+static int as2201_start P((int, struct peer *));
+static void as2201_shutdown P((int, struct peer *));
+static void as2201_receive P((struct recvbuf *));
+static void as2201_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_as2201 = {
+ as2201_start, /* start up driver */
+ as2201_shutdown, /* shut down driver */
+ as2201_poll, /* transmit poll message */
+ noentry, /* not used (old as2201_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old as2201_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * as2201_start - open the devices and initialize data for processing
+ */
+static int
+as2201_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct as2201unit *)
+ emalloc(sizeof(struct as2201unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct as2201unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = as2201_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->lastptr = up->stats;
+ up->index = 0;
+ return (1);
+}
+
+
+/*
+ * as2201_shutdown - shut down the clock
+ */
+static void
+as2201_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * as2201__receive - receive data from the serial interface
+ */
+static void
+as2201_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: timecode %d %d %s\n",
+ up->linect, pp->lencode, pp->a_lastcode);
+#endif
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * If linect is greater than zero, we must be in the middle of a
+ * statistics operation, so simply tack the received data at the
+ * end of the statistics string. If not, we could either have
+ * just received the timecode itself or a decimal number
+ * indicating the number of following lines of the statistics
+ * reply. In the former case, write the accumulated statistics
+ * data to the clockstats file and continue onward to process
+ * the timecode; in the later case, save the number of lines and
+ * quietly return.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ pp->lastrec = trtmp;
+ if (up->linect > 0) {
+ up->linect--;
+ if ((int)(up->lastptr - up->stats + pp->lencode) > SMAX - 2)
+ return;
+ *up->lastptr++ = ' ';
+ (void)strcpy(up->lastptr, pp->a_lastcode);
+ up->lastptr += pp->lencode;
+ return;
+ } else {
+ if (pp->lencode == 1) {
+ up->linect = atoi(pp->a_lastcode);
+ return;
+ } else {
+ record_clock_stats(&peer->srcadr, up->stats);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: stat %s\n", up->stats);
+#endif
+ }
+ }
+ up->lastptr = up->stats;
+ *up->lastptr = '\0';
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENTOC) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "yy:ddd:hh:mm:ss.mmm"
+ */
+ if (sscanf(pp->a_lastcode, "%2d:%3d:%2d:%2d:%2d.%3ld", &pp->year,
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec)
+ != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->nsec *= 1000000;
+
+ /*
+ * Test for synchronization (this is a temporary crock).
+ */
+ if (pp->a_lastcode[2] != ':')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * If CLK_FLAG4 is set, initialize the statistics buffer and
+ * send the next command. If not, simply write the timecode to
+ * the clockstats file.
+ */
+ (void)strcpy(up->lastptr, pp->a_lastcode);
+ up->lastptr += pp->lencode;
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ *up->lastptr++ = ' ';
+ (void)strcpy(up->lastptr, stat_command[up->index]);
+ up->lastptr += strlen(stat_command[up->index]);
+ up->lastptr--;
+ *up->lastptr = '\0';
+ (void)write(pp->io.fd, stat_command[up->index],
+ strlen(stat_command[up->index]));
+ up->index++;
+ if (*stat_command[up->index] == '\0')
+ up->index = 0;
+ }
+}
+
+
+/*
+ * as2201_poll - called by the transmit procedure
+ *
+ * We go to great pains to avoid changing state here, since there may be
+ * more than one eavesdropper receiving the same timecode.
+ */
+static void
+as2201_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ /*
+ * Send a "\r*toc\r" to get things going. We go to great pains
+ * to avoid changing state, since there may be more than one
+ * eavesdropper watching the radio.
+ */
+ pp = peer->procptr;
+ if (write(pp->io.fd, "\r*toc\r", 6) != 6) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else {
+ pp->polls++;
+ if (!(pp->sloppyclockflag & CLK_FLAG2))
+ get_systime(&pp->lastrec);
+ }
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ peer->burst = NSTAGE;
+}
+
+#else
+int refclock_as2201_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_atom.c b/ntpd/refclock_atom.c
new file mode 100644
index 0000000..51153ae
--- /dev/null
+++ b/ntpd/refclock_atom.c
@@ -0,0 +1,504 @@
+
+/*
+ * refclock_atom - clock driver for 1-pps signals
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#if defined(REFCLOCK) && defined(CLOCK_ATOM)
+
+#ifdef HAVE_PPSAPI
+# ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+# else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# endif
+# endif
+#endif /* HAVE_PPSAPI */
+
+/*
+ * This driver furnishes an interface for pulse-per-second (PPS) signals
+ * produced by a cesium clock, timing receiver or related equipment. It
+ * can be used to remove accumulated jitter and retime a secondary
+ * server when synchronized to a primary server over a congested, wide-
+ * area network and before redistributing the time to local clients.
+ *
+ * Before this driver becomes active, the local clock must be set to
+ * within +-500 ms by another means, such as a radio clock or NTP
+ * itself. There are two ways to connect the PPS signal, normally at TTL
+ * levels, to the computer. One is to shift to EIA levels and connect to
+ * pin 8 (DCD) of a serial port. This requires a level converter and
+ * may require a one-shot flipflop to lengthen the pulse. The other is
+ * to connect the PPS signal directly to pin 10 (ACK) of a PC paralell
+ * port. These methods are architecture dependent.
+ *
+ * Both methods require a modified device driver and kernel interface
+ * compatible with the Pulse-per-Second API for Unix-like Operating
+ * Systems, Version 1.0, RFC-2783 (PPSAPI). Implementations are
+ * available for FreeBSD, Linux, SunOS, Solaris and Alpha. However, at
+ * present only the Alpha implementation provides the full generality of
+ * the API with multiple PPS drivers and multiple handles per driver.
+ *
+ * In many configurations a single port is used for the radio timecode
+ * and PPS signal. In order to provide for this configuration and others
+ * involving dedicated multiple serial/parallel ports, the driver first
+ * attempts to open the device /dev/pps%d, where %d is the unit number.
+ * If this fails, the driver attempts to open the device specified by
+ * the pps configuration command. If a port is to be shared, the pps
+ * command must be placed before the radio device(s) and the radio
+ * device(s) must be placed before the PPS driver(s) in the
+ * configuration file.
+ *
+ * This driver normally uses the PLL/FLL clock discipline implemented in
+ * the ntpd code. If kernel support is available, the kernel PLL/FLL
+ * clock discipline is used instead. The default configuration is not to
+ * use the kernel PPS discipline, if present. The kernel PPS discipline
+ * can be enabled using the pps command.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic. The fudge
+ * time1 parameter can be used to compensate for miscellaneous device
+ * driver and OS delays.
+ */
+/*
+ * Interface definitions
+ */
+#ifdef HAVE_PPSAPI
+#define DEVICE "/dev/pps%d" /* device name and unit */
+#endif /* HAVE_PPSAPI */
+
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "PPS\0" /* reference ID */
+#define DESCRIPTION "PPS Clock Discipline" /* WRU */
+#define NANOSECOND 1000000000 /* one second (ns) */
+#define RANGEGATE 500000 /* range gate (ns) */
+#define ASTAGE 8 /* filter stages */
+
+static struct peer *pps_peer; /* atom driver for PPS sources */
+
+#ifdef HAVE_PPSAPI
+/*
+ * PPS unit control structure
+ */
+struct ppsunit {
+ struct timespec ts; /* last timestamp */
+ int fddev; /* pps device descriptor */
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t handle; /* pps handlebars */
+};
+#endif /* HAVE_PPSAPI */
+
+/*
+ * Function prototypes
+ */
+static int atom_start P((int, struct peer *));
+static void atom_poll P((int, struct peer *));
+#ifdef HAVE_PPSAPI
+static void atom_shutdown P((int, struct peer *));
+static void atom_control P((int, struct refclockstat *, struct
+ refclockstat *, struct peer *));
+static int atom_pps P((struct peer *));
+static int atom_ppsapi P((struct peer *, int, int));
+#endif /* HAVE_PPSAPI */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_atom = {
+ atom_start, /* start up driver */
+#ifdef HAVE_PPSAPI
+ atom_shutdown, /* shut down driver */
+#else
+ noentry, /* shut down driver */
+#endif /* HAVE_PPSAPI */
+ atom_poll, /* transmit poll message */
+#ifdef HAVE_PPSAPI
+ atom_control, /* fudge control */
+#else
+ noentry, /* fudge control */
+#endif /* HAVE_PPSAPI */
+ noentry, /* initialize driver */
+ noentry, /* not used (old atom_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * atom_start - initialize data for processing
+ */
+static int
+atom_start(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+#ifdef HAVE_PPSAPI
+ register struct ppsunit *up;
+ char device[80];
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ pps_peer = peer;
+ pp = peer->procptr;
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ pp->stratum = STRATUM_UNSPEC;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->burst = ASTAGE;
+#ifdef HAVE_PPSAPI
+ up = emalloc(sizeof(struct ppsunit));
+ memset(up, 0, sizeof(struct ppsunit));
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Open PPS device. If this fails and some driver has already
+ * opened the associated radio device, fdpps has the file
+ * descriptor for it.
+ */
+ sprintf(device, DEVICE, unit);
+ up->fddev = open(device, O_RDWR, 0777);
+ if (up->fddev <= 0 && fdpps > 0) {
+ strcpy(device, pps_device);
+ up->fddev = fdpps;
+ }
+ if (up->fddev <= 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Light off the PPSAPI interface. If this PPS device is shared
+ * with the radio device, take the default options from the pps
+ * command. This is for legacy purposes.
+ */
+ if (time_pps_create(up->fddev, &up->handle) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: time_pps_create failed: %m");
+ return (0);
+ }
+ return (atom_ppsapi(peer, 0, 0));
+#else /* HAVE_PPSAPI */
+ return (1);
+#endif /* HAVE_PPSAPI */
+}
+
+
+#ifdef HAVE_PPSAPI
+/*
+ * atom_control - fudge control
+ */
+static void
+atom_control(
+ int unit, /* unit (not used */
+ struct refclockstat *in, /* input parameters (not uded) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ atom_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
+ pp->sloppyclockflag & CLK_FLAG3);
+}
+
+
+/*
+ * Initialize PPSAPI
+ */
+int
+atom_ppsapi(
+ struct peer *peer, /* peer structure pointer */
+ int enb_clear, /* clear enable */
+ int enb_hardpps /* hardpps enable */
+ )
+{
+ struct refclockproc *pp;
+ register struct ppsunit *up;
+ int capability;
+
+ pp = peer->procptr;
+ up = (struct ppsunit *)pp->unitptr;
+ if (time_pps_getcap(up->handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&up->pps_params, 0, sizeof(pps_params_t));
+ if (enb_clear)
+ up->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ up->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!up->pps_params.mode) {
+ msyslog(LOG_ERR,
+ "refclock_atom: invalid capture edge %d",
+ enb_clear);
+ return (0);
+ }
+ up->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (enb_hardpps) {
+ if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
+ up->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_atom: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+#if DEBUG
+ if (debug) {
+ time_pps_getparams(up->handle, &up->pps_params);
+ printf(
+ "refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x kern %d\n",
+ up->fddev, capability, up->pps_params.api_version,
+ up->pps_params.mode, enb_hardpps);
+ }
+#endif
+ return (1);
+}
+
+
+/*
+ * atom_shutdown - shut down the clock
+ */
+static void
+atom_shutdown(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ register struct ppsunit *up;
+
+ pp = peer->procptr;
+ up = (struct ppsunit *)pp->unitptr;
+ if (up->fddev > 0)
+ close(up->fddev);
+ if (up->handle != 0)
+ time_pps_destroy(up->handle);
+ if (pps_peer == peer)
+ pps_peer = 0;
+ free(up);
+}
+
+
+/*
+ * atom_pps - receive data from the PPSAPI interface
+ *
+ * This routine is called once per second when the PPSAPI interface is
+ * present. It snatches the PPS timestamp from the kernel and saves the
+ * sign-extended fraction in a circular buffer for processing at the
+ * next poll event.
+ */
+static int
+atom_pps(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ register struct ppsunit *up;
+ struct refclockproc *pp;
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+
+ /*
+ * Convert the timespec nanoseconds field to signed double and
+ * save in the median filter. for billboards. No harm is done if
+ * previous data are overwritten. If the discipline comes bum or
+ * the data grow stale, just forget it. A range gate rejects new
+ * samples if less than a jiggle time from the next second.
+ */
+ pp = peer->procptr;
+ up = (struct ppsunit *)pp->unitptr;
+ if (up->handle == 0)
+ return (-1);
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
+ &timeout) < 0)
+ return (-1);
+ if (up->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ up->pps_info.assert_sequence)
+ return (1);
+ ts = up->pps_info.assert_timestamp;
+ } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ up->pps_info.clear_sequence)
+ return (1);
+ ts = up->pps_info.clear_timestamp;
+ } else {
+ return (-1);
+ }
+ if (!((ts.tv_sec == up->ts.tv_sec && ts.tv_nsec -
+ up->ts.tv_nsec > NANOSECOND - RANGEGATE) ||
+ (ts.tv_sec - up->ts.tv_sec == 1 && ts.tv_nsec -
+ up->ts.tv_nsec < RANGEGATE))) {
+ up->ts = ts;
+ return (1);
+ }
+ up->ts = ts;
+ pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ if (dtemp >= FRAC)
+ pp->lastrec.l_ui++;
+ pp->lastrec.l_uf = (u_int32)dtemp;
+ if (ts.tv_nsec > NANOSECOND / 2)
+ ts.tv_nsec -= NANOSECOND;
+ dtemp = -(double)ts.tv_nsec / NANOSECOND;
+ SAMPLE(dtemp + pp->fudgetime1);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("atom_pps %f %f\n", dtemp, pp->fudgetime1);
+#endif
+ return (0);
+}
+#endif /* HAVE_PPSAPI */
+
+
+/*
+ * pps_sample - receive PPS data from some other clock driver
+ *
+ * This routine is called once per second when the external clock driver
+ * processes PPS information. It processes the PPS timestamp and saves
+ * the sign-extended fraction in a circular buffer for processing at the
+ * next poll event. This works only for a single PPS device.
+ */
+int
+pps_sample(
+ l_fp *offset /* PPS offset */
+ )
+{
+ register struct peer *peer;
+ struct refclockproc *pp;
+ l_fp lftmp;
+ double doffset;
+
+ peer = pps_peer;
+ if (peer == 0) /* nobody home */
+ return (1);
+ pp = peer->procptr;
+
+ /*
+ * Convert the timeval to l_fp and save for billboards. Sign-
+ * extend the fraction and stash in the buffer. No harm is done
+ * if previous data are overwritten. If the discipline comes bum
+ * or the data grow stale, just forget it.
+ */
+ pp->lastrec = *offset;
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ LFPTOD(&lftmp, doffset);
+ SAMPLE(-doffset + pp->fudgetime1);
+ return (0);
+}
+
+/*
+ * atom_poll - called by the transmit procedure
+ *
+ * This routine is called once per second when in burst mode to save PPS
+ * sample offsets in the median filter. At the end of the burst period
+ * the samples are processed as a heap and the clock filter updated.
+ */
+static void
+atom_poll(
+ int unit, /* unit number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+#ifdef HAVE_PPSAPI
+ int err;
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * Accumulate samples in the median filter. If a noise sample,
+ * return with no prejudice; if a protocol error, get mean;
+ * otherwise, cool. At the end of each poll interval, do a
+ * little bookeeping and process the surviving samples.
+ */
+ pp = peer->procptr;
+ pp->polls++;
+#ifdef HAVE_PPSAPI
+ err = atom_pps(peer);
+ if (err < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * Valid time is returned only if the prefer peer has survived
+ * the intersection algorithm and within clock_max of local time
+ * and not too long ago. This ensures the PPS time is within
+ * +-0.5 s of the local time and the seconds numbering is
+ * unambiguous. Note that the leap bits are set no-warning on
+ * the first valid update and the stratum is set at the prefer
+ * peer, unless overriden by a fudge command.
+ */
+ if (peer->burst > 0)
+ return;
+ peer->leap = LEAP_NOTINSYNC;
+ if (pp->codeproc == pp->coderecv) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ peer->burst = ASTAGE;
+ return;
+
+ } else if (sys_prefer == NULL) {
+ pp->codeproc = pp->coderecv;
+ peer->burst = ASTAGE;
+ return;
+
+ } else if (fabs(sys_prefer->offset) > clock_max) {
+ pp->codeproc = pp->coderecv;
+ peer->burst = ASTAGE;
+ return;
+ }
+ pp->leap = LEAP_NOWARNING;
+ if (pp->stratum >= STRATUM_UNSPEC)
+ peer->stratum = sys_prefer->stratum;
+ else
+ peer->stratum = pp->stratum;
+ if (peer->stratum == STRATUM_REFCLOCK || peer->stratum ==
+ STRATUM_UNSPEC)
+ peer->refid = pp->refid;
+ else
+ peer->refid = addr2refid(&sys_prefer->srcadr);
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ peer->burst = ASTAGE;
+}
+#else
+int refclock_atom_bs;
+int
+pps_sample(
+ l_fp *offset /* PPS offset */
+ )
+{
+ return 1;
+}
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_bancomm.c b/ntpd/refclock_bancomm.c
new file mode 100644
index 0000000..a63be44
--- /dev/null
+++ b/ntpd/refclock_bancomm.c
@@ -0,0 +1,433 @@
+/* refclock_bancomm.c - clock driver for the Datum/Bancomm bc635VME
+ * Time and Frequency Processor. It requires the BANCOMM bc635VME/
+ * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x
+ * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc
+ * IIi-cEngine running Solaris 2.6.
+ *
+ * Author(s): Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
+ * Ottawa, Canada
+ *
+ * Date: July 1999
+ *
+ * Note(s): The refclock type has been defined as 16.
+ *
+ * This program has been modelled after the Bancomm driver
+ * originally written by R. Schmidt of Time Service, U.S.
+ * Naval Observatory for a HP-UX machine. Since the original
+ * authors no longer plan to maintain this code, all
+ * references to the HP-UX vme2 driver subsystem bave been
+ * removed. Functions vme_report_event(), vme_receive(),
+ * vme_control() and vme_buginfo() have been deleted because
+ * they are no longer being used.
+ *
+ * The time on the bc635 TFP must be set to GMT due to the
+ * fact that NTP makes use of GMT for all its calculations.
+ *
+ * Installation of the Datum/Bancomm driver creates the
+ * device file /dev/btfp0
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_BANC)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+
+/* STUFF BY RES */
+struct btfp_time /* Structure for reading 5 time words */
+ /* in one ioctl(2) operation. */
+{
+ unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/
+};
+
+/* SunOS5 ioctl commands definitions.*/
+#define BTFPIOC ( 'b'<< 8 )
+#define IOCIO( l, n ) ( BTFPIOC | n )
+#define IOCIOR( l, n, s ) ( BTFPIOC | n )
+#define IOCIORN( l, n, s ) ( BTFPIOC | n )
+#define IOCIOWN( l, n, s ) ( BTFPIOC | n )
+
+/***** Simple ioctl commands *****/
+#define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */
+#define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/
+#define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/
+
+/***** Compound ioctl commands *****/
+
+/* Read all 5 time words in one call. */
+#define READTIME IOCIORN(b, 32, sizeof( struct btfp_time ))
+#define VMEFD "/dev/btfp0"
+
+struct vmedate { /* structure returned by get_vmetime.c */
+ unsigned short year;
+ unsigned short day;
+ unsigned short hr;
+ unsigned short mn;
+ unsigned short sec;
+ unsigned long frac;
+ unsigned short status;
+};
+
+/* END OF STUFF FROM RES */
+
+/*
+ * VME interface parameters.
+ */
+#define VMEPRECISION (-21) /* precision assumed (1 us) */
+#define USNOREFID "BTFP" /* or whatever */
+#define VMEREFID "BTFP" /* reference id */
+#define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */
+#define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */
+/* clock type 16 is used here */
+#define GMT 0 /* hour offset from Greenwich */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time(s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * VME unit control structure.
+ * Changes made to vmeunit structure. Most members are now available in the
+ * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
+ */
+struct vmeunit {
+ struct vmedate vmedata; /* data returned from vme read */
+ u_long lasttime; /* last time clock heard from */
+};
+
+/*
+ * Function prototypes
+ */
+static void vme_init (void);
+static int vme_start (int, struct peer *);
+static void vme_shutdown (int, struct peer *);
+static void vme_receive (struct recvbuf *);
+static void vme_poll (int unit, struct peer *);
+struct vmedate *get_datumtime(struct vmedate *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_bancomm = {
+ vme_start, /* start up driver */
+ vme_shutdown, /* shut down driver */
+ vme_poll, /* transmit poll message */
+ noentry, /* not used (old vme_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old vme_buginfo) */
+ NOFLAGS /* not used */
+};
+
+int fd_vme; /* file descriptor for ioctls */
+int regvalue;
+
+
+/*
+ * vme_start - open the VME device and initialize data for processing
+ */
+static int
+vme_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct vmeunit *vme;
+ struct refclockproc *pp;
+ int dummy;
+ char vmedev[20];
+
+ /*
+ * Open VME device
+ */
+#ifdef DEBUG
+
+ printf("Opening DATUM VME DEVICE \n");
+#endif
+ if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) {
+ msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
+ return (0);
+ }
+ else { /* Release capture lockout in case it was set from before. */
+ if( ioctl( fd_vme, RUNLOCK, &dummy ) )
+ msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
+
+ regvalue = 0; /* More esoteric stuff to do... */
+ if( ioctl( fd_vme, WCR0, &regvalue ) )
+ msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
+ }
+
+ /*
+ * Allocate unit structure
+ */
+ vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
+ bzero((char *)vme, sizeof(struct vmeunit));
+
+
+ /*
+ * Set up the structures
+ */
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t) vme;
+ pp->timestarted = current_time;
+
+ pp->io.clock_recv = vme_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd_vme;
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success. Note that root delay and root dispersion are
+ * always zero for this clock.
+ */
+ peer->precision = VMEPRECISION;
+ memcpy(&pp->refid, USNOREFID,4);
+ return (1);
+}
+
+
+/*
+ * vme_shutdown - shut down a VME clock
+ */
+static void
+vme_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct vmeunit *vme;
+ struct refclockproc *pp;
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ pp = peer->procptr;
+ vme = (struct vmeunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ pp->unitptr = NULL;
+ free(vme);
+}
+
+
+/*
+ * vme_receive - receive data from the VME device.
+ *
+ * Note: This interface would be interrupt-driven. We don't use that
+ * now, but include a dummy routine for possible future adventures.
+ */
+static void
+vme_receive(
+ struct recvbuf *rbufp
+ )
+{
+}
+
+
+/*
+ * vme_poll - called by the transmit procedure
+ */
+static void
+vme_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct vmedate *tptr;
+ struct vmeunit *vme;
+ struct refclockproc *pp;
+ time_t tloc;
+ struct tm *tadr;
+
+ pp = peer->procptr;
+ vme = (struct vmeunit *)pp->unitptr; /* Here is the structure */
+
+ tptr = &vme->vmedata;
+ if ((tptr = get_datumtime(tptr)) == NULL ) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+ vme->lasttime = current_time;
+
+ /*
+ * Get VME time and convert to timestamp format.
+ * The year must come from the system clock.
+ */
+
+ time(&tloc);
+ tadr = gmtime(&tloc);
+ tptr->year = (unsigned short)(tadr->tm_year + 1900);
+
+
+ sprintf(pp->a_lastcode,
+ "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
+ tptr->day,
+ tptr->hr,
+ tptr->mn,
+ tptr->sec,
+ tptr->frac,
+ tptr->status);
+
+ pp->lencode = (u_short) strlen(pp->a_lastcode);
+
+ pp->day = tptr->day;
+ pp->hour = tptr->hr;
+ pp->minute = tptr->mn;
+ pp->second = tptr->sec;
+ pp->usec = tptr->frac;
+
+#ifdef DEBUG
+ if (debug)
+ printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
+ pp->day, pp->hour, pp->minute, pp->second,
+ pp->usec, tptr->status);
+#endif
+ if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Now, compute the reference time value. Use the heavy
+ * machinery for the seconds and the millisecond field for the
+ * fraction when present. If an error in conversion to internal
+ * format is found, the program declares bad data and exits.
+ * Note that this code does not yet know how to do the years and
+ * relies on the clock-calendar chip for sanity.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+}
+
+struct vmedate *
+get_datumtime(struct vmedate *time_vme)
+{
+ unsigned short status;
+ char cbuf[7];
+ struct btfp_time vts;
+
+ if ( time_vme == (struct vmedate *)NULL) {
+ time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
+ }
+
+ if( ioctl(fd_vme, READTIME, &vts))
+ msyslog(LOG_ERR, "get_datumtime error: %m");
+
+ /* if you want to actually check the validity of these registers, do a
+ define of CHECK above this. I didn't find it necessary. - RES
+ */
+
+#ifdef CHECK
+
+ /* Get day */
+ sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
+ ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
+
+ if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) )
+ time_vme->day = (unsigned short)atoi(cbuf);
+ else
+ time_vme->day = (unsigned short) 0;
+
+ /* Get hour */
+ sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
+
+ if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
+ time_vme->hr = (unsigned short)atoi(cbuf);
+ else
+ time_vme->hr = (unsigned short) 0;
+
+ /* Get minutes */
+ sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
+ if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
+ time_vme->mn = (unsigned short)atoi(cbuf);
+ else
+ time_vme->mn = (unsigned short) 0;
+
+ /* Get seconds */
+ sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
+
+ if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
+ time_vme->sec = (unsigned short)atoi(cbuf);
+ else
+ time_vme->sec = (unsigned short) 0;
+
+ /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can
+ use the TVTOTSF function later on...*/
+
+ sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
+ vts.btfp_time[ 4 ]>>8);
+
+ if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2])
+ && isdigit(cbuf[3]) && isdigit(cbuf[4]) && isdigit(cbuf[5]))
+ time_vme->frac = (u_long) atoi(cbuf);
+ else
+ time_vme->frac = (u_long) 0;
+#else
+
+ /* DONT CHECK just trust the card */
+
+ /* Get day */
+ sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
+ ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
+ time_vme->day = (unsigned short)atoi(cbuf);
+
+ /* Get hour */
+ sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
+
+ time_vme->hr = (unsigned short)atoi(cbuf);
+
+ /* Get minutes */
+ sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
+ time_vme->mn = (unsigned short)atoi(cbuf);
+
+ /* Get seconds */
+ sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
+ time_vme->sec = (unsigned short)atoi(cbuf);
+
+ /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can
+ use the TVTOTSF function later on...*/
+
+ sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
+ vts.btfp_time[ 4 ]>>8);
+
+ time_vme->frac = (u_long) atoi(cbuf);
+
+#endif /* CHECK */
+
+ /* Get status bit */
+ status = (vts.btfp_time[0] & 0x0010) >>4;
+ time_vme->status = status; /* Status=0 if locked to ref. */
+ /* Status=1 if flywheeling */
+ if (status) { /* lost lock ? */
+ return ((void *)NULL);
+ }
+ else
+ return (time_vme);
+}
+
+#else
+int refclock_bancomm_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_chronolog.c b/ntpd/refclock_chronolog.c
new file mode 100644
index 0000000..a1d131e
--- /dev/null
+++ b/ntpd/refclock_chronolog.c
@@ -0,0 +1,341 @@
+/*
+ * refclock_chronolog - clock driver for Chronolog K-series WWVB receiver.
+ */
+
+/*
+ * Must interpolate back to local time. Very annoying.
+ */
+#define GET_LOCALTIME
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_CHRONOLOG)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Chronolog K-series WWVB receiver.
+ *
+ * Input format:
+ *
+ * Y YY/MM/DD<cr><lf>
+ * Z hh:mm:ss<cr><lf>
+ *
+ * YY/MM/DD -- what you'd expect. This arrives a few seconds before the
+ * timestamp.
+ * hh:mm:ss -- what you'd expect. We take time on the <cr>.
+ *
+ * Our Chronolog writes time out at 2400 bps 8/N/1, but it can be configured
+ * otherwise. The clock seems to appear every 60 seconds, which doesn't make
+ * for good statistics collection.
+ *
+ * The original source of this module was the WWVB module.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/chronolog%d" /* device name and unit */
+#define SPEED232 B2400 /* uart speed (2400 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "chronolog" /* reference ID */
+#define DESCRIPTION "Chrono-log K" /* WRU */
+
+#define MONLIN 15 /* number of monitoring lines */
+
+/*
+ * Chrono-log unit control structure
+ */
+struct chronolog_unit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ int year; /* Y2K-adjusted year */
+ int day; /* day-of-month */
+ int month; /* month-of-year */
+};
+
+/*
+ * Function prototypes
+ */
+static int chronolog_start P((int, struct peer *));
+static void chronolog_shutdown P((int, struct peer *));
+static void chronolog_receive P((struct recvbuf *));
+static void chronolog_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_chronolog = {
+ chronolog_start, /* start up driver */
+ chronolog_shutdown, /* shut down driver */
+ chronolog_poll, /* poll the driver -- a nice fabrication */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * chronolog_start - open the devices and initialize data for processing
+ */
+static int
+chronolog_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct chronolog_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Don't bother with CLK line discipline, since
+ * it's not available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+#ifdef DEBUG
+ if (debug)
+ printf ("starting Chronolog with device %s\n",device);
+#endif
+ if (!(fd = refclock_open(device, SPEED232, 0)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct chronolog_unit *)
+ emalloc(sizeof(struct chronolog_unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct chronolog_unit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = chronolog_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * chronolog_shutdown - shut down the clock
+ */
+static void
+chronolog_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct chronolog_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct chronolog_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * chronolog_receive - receive data from the serial interface
+ */
+static void
+chronolog_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct chronolog_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int hours; /* hour-of-day */
+ int minutes; /* minutes-past-the-hour */
+ int seconds; /* seconds */
+ int temp; /* int temp */
+ int got_good; /* got a good time flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chronolog_unit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+
+#ifdef DEBUG
+ if (debug)
+ printf("chronolog: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business. Check the timecode format and decode
+ * its contents. This code uses the first character to see whether
+ * we're looking at a date or a time. We store data data across
+ * calls since it is transmitted a few seconds ahead of the
+ * timestamp.
+ */
+ got_good=0;
+ if (sscanf(pp->a_lastcode, "Y %d/%d/%d", &up->year,&up->month,&up->day))
+ {
+ /*
+ * Y2K convert the 2-digit year
+ */
+ up->year = up->year >= 69 ? up->year : up->year + 100;
+ return;
+ }
+ if (sscanf(pp->a_lastcode,"Z %02d:%02d:%02d",
+ &hours,&minutes,&seconds) == 3)
+ {
+#ifdef GET_LOCALTIME
+ struct tm local;
+ struct tm *gmtp;
+ time_t unixtime;
+ int adjyear;
+ int adjmon;
+
+ /*
+ * Convert to GMT for sites that distribute localtime. This
+ * means we have to do Y2K conversion on the 2-digit year;
+ * otherwise, we get the time wrong.
+ */
+
+ local.tm_year = up->year;
+ local.tm_mon = up->month-1;
+ local.tm_mday = up->day;
+ local.tm_hour = hours;
+ local.tm_min = minutes;
+ local.tm_sec = seconds;
+ local.tm_isdst = -1;
+
+ unixtime = mktime (&local);
+ if ((gmtp = gmtime (&unixtime)) == NULL)
+ {
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+ adjyear = gmtp->tm_year+1900;
+ adjmon = gmtp->tm_mon+1;
+ pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
+ pp->hour = gmtp->tm_hour;
+ pp->minute = gmtp->tm_min;
+ pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+ if (debug)
+ printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
+ pp->second);
+#endif
+
+#else
+ /*
+ * For more rational sites distributing UTC
+ */
+ pp->day = ymd2yd(year+1900,month,day);
+ pp->hour = hours;
+ pp->minute = minutes;
+ pp->second = seconds;
+
+#endif
+ got_good=1;
+ }
+
+ if (!got_good)
+ return;
+
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ up->lasthour = pp->hour;
+}
+
+
+/*
+ * chronolog_poll - called by the transmit procedure
+ */
+static void
+chronolog_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ /*
+ * Time to poll the clock. The Chrono-log clock is supposed to
+ * respond to a 'T' by returning a timecode in the format(s)
+ * specified above. Ours does (can?) not, but this seems to be
+ * an installation-specific problem. This code is dyked out,
+ * but may be re-enabled if anyone ever finds a Chrono-log that
+ * actually listens to this command.
+ */
+#if 0
+ register struct chronolog_unit *up;
+ struct refclockproc *pp;
+ char pollchar;
+
+ pp = peer->procptr;
+ up = (struct chronolog_unit *)pp->unitptr;
+ if (peer->burst == 0 && peer->reach == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ if (up->linect > 0)
+ pollchar = 'R';
+ else
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+#endif
+}
+
+#else
+int refclock_chronolog_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_chu.c b/ntpd/refclock_chu.c
new file mode 100644
index 0000000..e0c79e2
--- /dev/null
+++ b/ntpd/refclock_chu.c
@@ -0,0 +1,1687 @@
+/*
+ * refclock_chu - clock driver for Canadian CHU time/frequency station
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_CHU)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifdef HAVE_AUDIO
+#include "audio.h"
+#endif /* HAVE_AUDIO */
+
+#define ICOM 1 /* undefine to suppress ICOM code */
+
+#ifdef ICOM
+#include "icom.h"
+#endif /* ICOM */
+
+/*
+ * Audio CHU demodulator/decoder
+ *
+ * This driver synchronizes the computer time using data encoded in
+ * radio transmissions from Canadian time/frequency station CHU in
+ * Ottawa, Ontario. Transmissions are made continuously on 3330 kHz,
+ * 7335 kHz and 14670 kHz in upper sideband, compatible AM mode. An
+ * ordinary shortwave receiver can be tuned manually to one of these
+ * frequencies or, in the case of ICOM receivers, the receiver can be
+ * tuned automatically using this program as propagation conditions
+ * change throughout the day and night.
+ *
+ * The driver receives, demodulates and decodes the radio signals when
+ * connected to the audio codec of a suported workstation hardware and
+ * operating system. These include Solaris, SunOS, FreeBSD, NetBSD and
+ * Linux. In this implementation, only one audio driver and codec can be
+ * supported on a single machine.
+ *
+ * The driver can be compiled to use a Bell 103 compatible modem or
+ * modem chip to receive the radio signal and demodulate the data.
+ * Alternatively, the driver can be compiled to use the audio codec of
+ * the Sun workstation or another with compatible audio drivers. In the
+ * latter case, the driver implements the modem using DSP routines, so
+ * the radio can be connected directly to either the microphone on line
+ * input port. In either case, the driver decodes the data using a
+ * maximum likelihood technique which exploits the considerable degree
+ * of redundancy available to maximize accuracy and minimize errors.
+ *
+ * The CHU time broadcast includes an audio signal compatible with the
+ * Bell 103 modem standard (mark = 2225 Hz, space = 2025 Hz). It consist
+ * of nine, ten-character bursts transmitted at 300 bps and beginning
+ * each second from second 31 to second 39 of the minute. Each character
+ * consists of eight data bits plus one start bit and two stop bits to
+ * encode two hex digits. The burst data consist of five characters (ten
+ * hex digits) followed by a repeat of these characters. In format A,
+ * the characters are repeated in the same polarity; in format B, the
+ * characters are repeated in the opposite polarity.
+ *
+ * Format A bursts are sent at seconds 32 through 39 of the minute in
+ * hex digits
+ *
+ * 6dddhhmmss6dddhhmmss
+ *
+ * The first ten digits encode a frame marker (6) followed by the day
+ * (ddd), hour (hh in UTC), minute (mm) and the second (ss). Since
+ * format A bursts are sent during the third decade of seconds the tens
+ * digit of ss is always 3. The driver uses this to determine correct
+ * burst synchronization. These digits are then repeated with the same
+ * polarity.
+ *
+ * Format B bursts are sent at second 31 of the minute in hex digits
+ *
+ * xdyyyyttaaxdyyyyttaa
+ *
+ * The first ten digits encode a code (x described below) followed by
+ * the DUT1 (d in deciseconds), Gregorian year (yyyy), difference TAI -
+ * UTC (tt) and daylight time indicator (aa) peculiar to Canada. These
+ * digits are then repeated with inverted polarity.
+ *
+ * The x is coded
+ *
+ * 1 Sign of DUT (0 = +)
+ * 2 Leap second warning. One second will be added.
+ * 4 Leap second warning. One second will be subtracted.
+ * 8 Even parity bit for this nibble.
+ *
+ * By design, the last stop bit of the last character in the burst
+ * coincides with 0.5 second. Since characters have 11 bits and are
+ * transmitted at 300 bps, the last stop bit of the first character
+ * coincides with 0.5 - 10 * 11/300 = 0.133 second. Depending on the
+ * UART, character interrupts can vary somewhere between the beginning
+ * of bit 9 and end of bit 11. These eccentricities can be corrected
+ * along with the radio propagation delay using fudge time 1.
+ *
+ * Debugging aids
+ *
+ * The timecode format used for debugging and data recording includes
+ * data helpful in diagnosing problems with the radio signal and serial
+ * connections. With debugging enabled (-d on the ntpd command line),
+ * the driver produces one line for each burst in two formats
+ * corresponding to format A and B. Following is format A:
+ *
+ * n b f s m code
+ *
+ * where n is the number of characters in the burst (0-11), b the burst
+ * distance (0-40), f the field alignment (-1, 0, 1), s the
+ * synchronization distance (0-16), m the burst number (2-9) and code
+ * the burst characters as received. Note that the hex digits in each
+ * character are reversed, so the burst
+ *
+ * 10 38 0 16 9 06851292930685129293
+ *
+ * is interpreted as containing 11 characters with burst distance 38,
+ * field alignment 0, synchronization distance 16 and burst number 9.
+ * The nibble-swapped timecode shows day 58, hour 21, minute 29 and
+ * second 39.
+ *
+ * When the audio driver is compiled, format A is preceded by
+ * the current gain (0-255) and relative signal level (0-9999). The
+ * receiver folume control should be set so that the gain is somewhere
+ * near the middle of the range 0-255, which results in a signal level
+ * near 1000.
+ *
+ * Following is format B:
+ *
+ * n b s code
+ *
+ * where n is the number of characters in the burst (0-11), b the burst
+ * distance (0-40), s the synchronization distance (0-40) and code the
+ * burst characters as received. Note that the hex digits in each
+ * character are reversed and the last ten digits inverted, so the burst
+ *
+ * 11 40 1091891300ef6e76ecff
+ *
+ * is interpreted as containing 11 characters with burst distance 40.
+ * The nibble-swapped timecode shows DUT1 +0.1 second, year 1998 and TAI
+ * - UTC 31 seconds.
+ *
+ * In addition to the above, the reference timecode is updated and
+ * written to the clockstats file and debug score after the last burst
+ * received in the minute. The format is
+ *
+ * qq yyyy ddd hh:mm:ss nn dd tt
+ *
+ * where qq are the error flags, as described below, yyyy is the year,
+ * ddd the day, hh:mm:ss the time of day, nn the number of format A
+ * bursts received during the previous minute, dd the decoding distance
+ * and tt the number of timestamps. The error flags are cleared after
+ * every update.
+ *
+ * Fudge factors
+ *
+ * For accuracies better than the low millisceconds, fudge time1 can be
+ * set to the radio propagation delay from CHU to the receiver. This can
+ * be done conviently using the minimuf program.
+ *
+ * Fudge flag4 causes the dubugging output described above to be
+ * recorded in the clockstats file. When the audio driver is compiled,
+ * fudge flag2 selects the audio input port, where 0 is the mike port
+ * (default) and 1 is the line-in port. It does not seem useful to
+ * select the compact disc player port. Fudge flag3 enables audio
+ * monitoring of the input signal. For this purpose, the monitor gain is
+ * set to a default value.
+ *
+ * The audio codec code is normally compiled in the driver if the
+ * architecture supports it (HAVE_AUDIO defined), but is used only if
+ * the link /dev/chu_audio is defined and valid. The serial port code is
+ * always compiled in the driver, but is used only if the autdio codec
+ * is not available and the link /dev/chu%d is defined and valid.
+ *
+ * The ICOM code is normally compiled in the driver if selected (ICOM
+ * defined), but is used only if the link /dev/icom%d is defined and
+ * valid and the mode keyword on the server configuration command
+ * specifies a nonzero mode (ICOM ID select code). The C-IV speed is
+ * 9600 bps if the high order 0x80 bit of the mode is zero and 1200 bps
+ * if one. The C-IV trace is turned on if the debug level is greater
+ * than one.
+ */
+/*
+ * Interface definitions
+ */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "CHU" /* reference ID */
+#define DEVICE "/dev/chu%d" /* device name and unit */
+#define SPEED232 B300 /* UART speed (300 baud) */
+#ifdef ICOM
+#define TUNE .001 /* offset for narrow filter (kHz) */
+#define DWELL 5 /* minutes in a probe cycle */
+#define NCHAN 3 /* number of channels */
+#define ISTAGE 3 /* number of integrator stages */
+#endif /* ICOM */
+
+#ifdef HAVE_AUDIO
+/*
+ * Audio demodulator definitions
+ */
+#define SECOND 8000 /* nominal sample rate (Hz) */
+#define BAUD 300 /* modulation rate (bps) */
+#define OFFSET 128 /* companded sample offset */
+#define SIZE 256 /* decompanding table size */
+#define MAXSIG 6000. /* maximum signal level */
+#define MAXCLP 100 /* max clips above reference per s */
+#define LIMIT 1000. /* soft limiter threshold */
+#define AGAIN 6. /* baseband gain */
+#define LAG 10 /* discriminator lag */
+#define DEVICE_AUDIO "/dev/chu_audio" /* device name */
+#define DESCRIPTION "CHU Audio/Modem Receiver" /* WRU */
+#define AUDIO_BUFSIZ 240 /* audio buffer size (30 ms) */
+#else
+#define DESCRIPTION "CHU Modem Receiver" /* WRU */
+#endif /* HAVE_AUDIO */
+
+/*
+ * Decoder definitions
+ */
+#define CHAR (11. / 300.) /* character time (s) */
+#define FUDGE .185 /* offset to first stop bit (s) */
+#define BURST 11 /* max characters per burst */
+#define MINCHAR 9 /* min characters per burst */
+#define MINDIST 28 /* min burst distance (of 40) */
+#define MINBURST 4 /* min bursts in minute */
+#define MINSYNC 8 /* min sync distance (of 16) */
+#define MINSTAMP 20 /* min timestamps (of 60) */
+#define METRIC 50. /* min channel metric */
+#define PANIC 1440 /* panic timeout (m) */
+#define HOLD 30 /* reach hold (m) */
+
+/*
+ * Hex extension codes (>= 16)
+ */
+#define HEX_MISS 16 /* miss _ */
+#define HEX_SOFT 17 /* soft error * */
+#define HEX_HARD 18 /* hard error = */
+
+/*
+ * Status bits (status)
+ */
+#define RUNT 0x0001 /* runt burst */
+#define NOISE 0x0002 /* noise burst */
+#define BFRAME 0x0004 /* invalid format B frame sync */
+#define BFORMAT 0x0008 /* invalid format B data */
+#define AFRAME 0x0010 /* invalid format A frame sync */
+#define AFORMAT 0x0020 /* invalid format A data */
+#define DECODE 0x0040 /* invalid data decode */
+#define STAMP 0x0080 /* too few timestamps */
+#define AVALID 0x0100 /* valid A frame */
+#define BVALID 0x0200 /* valid B frame */
+#define INSYNC 0x0400 /* clock synchronized */
+
+/*
+ * Alarm status bits (alarm)
+ *
+ * These alarms are set at the end of a minute in which at least one
+ * burst was received. SYNERR is raised if the AFRAME or BFRAME status
+ * bits are set during the minute, FMTERR is raised if the AFORMAT or
+ * BFORMAT status bits are set, DECERR is raised if the DECODE status
+ * bit is set and TSPERR is raised if the STAMP status bit is set.
+ */
+#define SYNERR 0x01 /* frame sync error */
+#define FMTERR 0x02 /* data format error */
+#define DECERR 0x04 /* data decoding error */
+#define TSPERR 0x08 /* insufficient data */
+
+#ifdef HAVE_AUDIO
+/*
+ * Maximum likelihood UART structure. There are eight of these
+ * corresponding to the number of phases.
+ */
+struct surv {
+ double shift[12]; /* mark register */
+ double es_max, es_min; /* max/min envelope signals */
+ double dist; /* sample distance */
+ int uart; /* decoded character */
+};
+#endif /* HAVE_AUDIO */
+
+#ifdef ICOM
+/*
+ * CHU station structure. There are three of these corresponding to the
+ * three frequencies.
+ */
+struct xmtr {
+ double integ[ISTAGE]; /* circular integrator */
+ double metric; /* integrator sum */
+ int iptr; /* integrator pointer */
+ int probe; /* dwells since last probe */
+};
+#endif /* ICOM */
+
+/*
+ * CHU unit control structure
+ */
+struct chuunit {
+ u_char decode[20][16]; /* maximum likelihood decoding matrix */
+ l_fp cstamp[BURST]; /* character timestamps */
+ l_fp tstamp[MAXSTAGE]; /* timestamp samples */
+ l_fp timestamp; /* current buffer timestamp */
+ l_fp laststamp; /* last buffer timestamp */
+ l_fp charstamp; /* character time as a l_fp */
+ int errflg; /* error flags */
+ int status; /* status bits */
+ char ident[5]; /* station ID and channel */
+#ifdef ICOM
+ int fd_icom; /* ICOM file descriptor */
+ int chan; /* data channel */
+ int achan; /* active channel */
+ int dwell; /* dwell cycle */
+ struct xmtr xmtr[NCHAN]; /* station metric */
+#endif /* ICOM */
+
+ /*
+ * Character burst variables
+ */
+ int cbuf[BURST]; /* character buffer */
+ int ntstamp; /* number of timestamp samples */
+ int ndx; /* buffer start index */
+ int prevsec; /* previous burst second */
+ int burdist; /* burst distance */
+ int syndist; /* sync distance */
+ int burstcnt; /* format A bursts this minute */
+
+ /*
+ * Format particulars
+ */
+ int leap; /* leap/dut code */
+ int dut; /* UTC1 correction */
+ int tai; /* TAI - UTC correction */
+ int dst; /* Canadian DST code */
+
+#ifdef HAVE_AUDIO
+ /*
+ * Audio codec variables
+ */
+ int fd_audio; /* audio port file descriptor */
+ double comp[SIZE]; /* decompanding table */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int mongain; /* codec monitor gain */
+ int clipcnt; /* sample clip count */
+ int seccnt; /* second interval counter */
+
+ /*
+ * Modem variables
+ */
+ l_fp tick; /* audio sample increment */
+ double bpf[9]; /* IIR bandpass filter */
+ double disc[LAG]; /* discriminator shift register */
+ double lpf[27]; /* FIR lowpass filter */
+ double monitor; /* audio monitor */
+ double maxsignal; /* signal level */
+ int discptr; /* discriminator pointer */
+
+ /*
+ * Maximum likelihood UART variables
+ */
+ double baud; /* baud interval */
+ struct surv surv[8]; /* UART survivor structures */
+ int decptr; /* decode pointer */
+ int dbrk; /* holdoff counter */
+#endif /* HAVE_AUDIO */
+};
+
+/*
+ * Function prototypes
+ */
+static int chu_start P((int, struct peer *));
+static void chu_shutdown P((int, struct peer *));
+static void chu_receive P((struct recvbuf *));
+static void chu_poll P((int, struct peer *));
+
+/*
+ * More function prototypes
+ */
+static void chu_decode P((struct peer *, int));
+static void chu_burst P((struct peer *));
+static void chu_clear P((struct peer *));
+static void chu_a P((struct peer *, int));
+static void chu_b P((struct peer *, int));
+static int chu_dist P((int, int));
+static double chu_major P((struct peer *));
+#ifdef HAVE_AUDIO
+static void chu_uart P((struct surv *, double));
+static void chu_rf P((struct peer *, double));
+static void chu_gain P((struct peer *));
+static void chu_audio_receive P((struct recvbuf *rbufp));
+#endif /* HAVE_AUDIO */
+#ifdef ICOM
+static int chu_newchan P((struct peer *, double));
+#endif /* ICOM */
+static void chu_serial_receive P((struct recvbuf *rbufp));
+
+/*
+ * Global variables
+ */
+static char hexchar[] = "0123456789abcdef_*=";
+
+#ifdef ICOM
+/*
+ * Note the tuned frequencies are 1 kHz higher than the carrier. CHU
+ * transmits on USB with carrier so we can use AM and the narrow SSB
+ * filter.
+ */
+static double qsy[NCHAN] = {3.330, 7.335, 14.670}; /* freq (MHz) */
+#endif /* ICOM */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_chu = {
+ chu_start, /* start up driver */
+ chu_shutdown, /* shut down driver */
+ chu_poll, /* transmit poll message */
+ noentry, /* not used (old chu_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old chu_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * chu_start - open the devices and initialize data for processing
+ */
+static int
+chu_start(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ char device[20]; /* device name */
+ int fd; /* file descriptor */
+#ifdef ICOM
+ int temp;
+#endif /* ICOM */
+#ifdef HAVE_AUDIO
+ int fd_audio; /* audio port file descriptor */
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device.
+ */
+ fd_audio = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
+#ifdef DEBUG
+ if (fd_audio > 0 && debug)
+ audio_show();
+#endif
+
+ /*
+ * Open serial port in raw mode.
+ */
+ if (fd_audio > 0) {
+ fd = fd_audio;
+ } else {
+ sprintf(device, DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_RAW);
+ }
+#else /* HAVE_AUDIO */
+
+ /*
+ * Open serial port in raw mode.
+ */
+ sprintf(device, DEVICE, unit);
+ fd = refclock_open(device, SPEED232, LDISC_RAW);
+#endif /* HAVE_AUDIO */
+ if (fd <= 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct chuunit *)
+ emalloc(sizeof(struct chuunit)))) {
+ close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct chuunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = chu_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ strcpy(up->ident, "CHU");
+ memcpy(&peer->refid, up->ident, 4);
+ DTOLFP(CHAR, &up->charstamp);
+#ifdef HAVE_AUDIO
+
+ /*
+ * The companded samples are encoded sign-magnitude. The table
+ * contains all the 256 values in the interest of speed. We do
+ * this even if the audio codec is not available. C'est la lazy.
+ */
+ up->fd_audio = fd_audio;
+ up->gain = 127;
+ up->comp[0] = up->comp[OFFSET] = 0.;
+ up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
+ up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
+ step = 2.;
+ for (i = 3; i < OFFSET; i++) {
+ up->comp[i] = up->comp[i - 1] + step;
+ up->comp[OFFSET + i] = -up->comp[i];
+ if (i % 16 == 0)
+ step *= 2.;
+ }
+ DTOLFP(1. / SECOND, &up->tick);
+#endif /* HAVE_AUDIO */
+#ifdef ICOM
+ temp = 0;
+#ifdef DEBUG
+ if (debug > 1)
+ temp = P_TRACE;
+#endif
+ if (peer->ttl > 0) {
+ if (peer->ttl & 0x80)
+ up->fd_icom = icom_init("/dev/icom", B1200,
+ temp);
+ else
+ up->fd_icom = icom_init("/dev/icom", B9600,
+ temp);
+ }
+ if (up->fd_icom > 0) {
+ if (chu_newchan(peer, 0) != 0) {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "icom: radio not found");
+ up->errflg = CEVNT_FAULT;
+ close(up->fd_icom);
+ up->fd_icom = 0;
+ } else {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "icom: autotune enabled");
+ }
+ }
+#endif /* ICOM */
+ return (1);
+}
+
+
+/*
+ * chu_shutdown - shut down the clock
+ */
+static void
+chu_shutdown(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ if (up == NULL)
+ return;
+
+ io_closeclock(&pp->io);
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ close(up->fd_icom);
+#endif /* ICOM */
+ free(up);
+}
+
+
+/*
+ * chu_receive - receive data from the audio or serial device
+ */
+static void
+chu_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+#ifdef HAVE_AUDIO
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * If the audio codec is warmed up, the buffer contains codec
+ * samples which need to be demodulated and decoded into CHU
+ * characters using the software UART. Otherwise, the buffer
+ * contains CHU characters from the serial port, so the software
+ * UART is bypassed. In this case the CPU will probably run a
+ * few degrees cooler.
+ */
+ if (up->fd_audio > 0)
+ chu_audio_receive(rbufp);
+ else
+ chu_serial_receive(rbufp);
+#else
+ chu_serial_receive(rbufp);
+#endif /* HAVE_AUDIO */
+}
+
+
+#ifdef HAVE_AUDIO
+/*
+ * chu_audio_receive - receive data from the audio device
+ */
+static void
+chu_audio_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ int bufcnt; /* buffer counter */
+ l_fp ltemp; /* l_fp temp */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Main loop - read until there ain't no more. Note codec
+ * samples are bit-inverted.
+ */
+ DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
+ L_SUB(&rbufp->recv_time, &ltemp);
+ up->timestamp = rbufp->recv_time;
+ dpt = rbufp->recv_buffer;
+ for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
+ sample = up->comp[~*dpt++ & 0xff];
+
+ /*
+ * Clip noise spikes greater than MAXSIG. If no clips,
+ * increase the gain a tad; if the clips are too high,
+ * decrease a tad.
+ */
+ if (sample > MAXSIG) {
+ sample = MAXSIG;
+ up->clipcnt++;
+ } else if (sample < -MAXSIG) {
+ sample = -MAXSIG;
+ up->clipcnt++;
+ }
+ chu_rf(peer, sample);
+ L_ADD(&up->timestamp, &up->tick);
+
+ /*
+ * Once each second ride gain.
+ */
+ up->seccnt = (up->seccnt + 1) % SECOND;
+ if (up->seccnt == 0) {
+ pp->second = (pp->second + 1) % 60;
+ chu_gain(peer);
+ }
+ }
+
+ /*
+ * Set the input port and monitor gain for the next buffer.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = 2;
+ else
+ up->port = 1;
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ up->mongain = MONGAIN;
+ else
+ up->mongain = 0;
+}
+
+
+/*
+ * chu_rf - filter and demodulate the FSK signal
+ *
+ * This routine implements a 300-baud Bell 103 modem with mark 2225 Hz
+ * and space 2025 Hz. It uses a bandpass filter followed by a soft
+ * limiter, FM discriminator and lowpass filter. A maximum likelihood
+ * decoder samples the baseband signal at eight times the baud rate and
+ * detects the start bit of each character.
+ *
+ * The filters are built for speed, which explains the rather clumsy
+ * code. Hopefully, the compiler will efficiently implement the move-
+ * and-muiltiply-and-add operations.
+ */
+static void
+chu_rf(
+ struct peer *peer, /* peer structure pointer */
+ double sample /* analog sample */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+ struct surv *sp;
+
+ /*
+ * Local variables
+ */
+ double signal; /* bandpass signal */
+ double limit; /* limiter signal */
+ double disc; /* discriminator signal */
+ double lpf; /* lowpass signal */
+ double span; /* UART signal span */
+ double dist; /* UART signal distance */
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Bandpass filter. 4th-order elliptic, 500-Hz bandpass centered
+ * at 2125 Hz. Passband ripple 0.3 dB, stopband ripple 50 dB.
+ */
+ signal = (up->bpf[8] = up->bpf[7]) * 5.844676e-01;
+ signal += (up->bpf[7] = up->bpf[6]) * 4.884860e-01;
+ signal += (up->bpf[6] = up->bpf[5]) * 2.704384e+00;
+ signal += (up->bpf[5] = up->bpf[4]) * 1.645032e+00;
+ signal += (up->bpf[4] = up->bpf[3]) * 4.644557e+00;
+ signal += (up->bpf[3] = up->bpf[2]) * 1.879165e+00;
+ signal += (up->bpf[2] = up->bpf[1]) * 3.522634e+00;
+ signal += (up->bpf[1] = up->bpf[0]) * 7.315738e-01;
+ up->bpf[0] = sample - signal;
+ signal = up->bpf[0] * 6.176213e-03
+ + up->bpf[1] * 3.156599e-03
+ + up->bpf[2] * 7.567487e-03
+ + up->bpf[3] * 4.344580e-03
+ + up->bpf[4] * 1.190128e-02
+ + up->bpf[5] * 4.344580e-03
+ + up->bpf[6] * 7.567487e-03
+ + up->bpf[7] * 3.156599e-03
+ + up->bpf[8] * 6.176213e-03;
+
+ up->monitor = signal / 4.; /* note monitor after filter */
+
+ /*
+ * Soft limiter/discriminator. The 11-sample discriminator lag
+ * interval corresponds to three cycles of 2125 Hz, which
+ * requires the sample frequency to be 2125 * 11 / 3 = 7791.7
+ * Hz. The discriminator output varies +-0.5 interval for input
+ * frequency 2025-2225 Hz. However, we don't get to sample at
+ * this frequency, so the discriminator output is biased. Life
+ * at 8000 Hz sucks.
+ */
+ limit = signal;
+ if (limit > LIMIT)
+ limit = LIMIT;
+ else if (limit < -LIMIT)
+ limit = -LIMIT;
+ disc = up->disc[up->discptr] * -limit;
+ up->disc[up->discptr] = limit;
+ up->discptr = (up->discptr + 1 ) % LAG;
+ if (disc >= 0)
+ disc = SQRT(disc);
+ else
+ disc = -SQRT(-disc);
+
+ /*
+ * Lowpass filter. Raised cosine, Ts = 1 / 300, beta = 0.1.
+ */
+ lpf = (up->lpf[26] = up->lpf[25]) * 2.538771e-02;
+ lpf += (up->lpf[25] = up->lpf[24]) * 1.084671e-01;
+ lpf += (up->lpf[24] = up->lpf[23]) * 2.003159e-01;
+ lpf += (up->lpf[23] = up->lpf[22]) * 2.985303e-01;
+ lpf += (up->lpf[22] = up->lpf[21]) * 4.003697e-01;
+ lpf += (up->lpf[21] = up->lpf[20]) * 5.028552e-01;
+ lpf += (up->lpf[20] = up->lpf[19]) * 6.028795e-01;
+ lpf += (up->lpf[19] = up->lpf[18]) * 6.973249e-01;
+ lpf += (up->lpf[18] = up->lpf[17]) * 7.831828e-01;
+ lpf += (up->lpf[17] = up->lpf[16]) * 8.576717e-01;
+ lpf += (up->lpf[16] = up->lpf[15]) * 9.183463e-01;
+ lpf += (up->lpf[15] = up->lpf[14]) * 9.631951e-01;
+ lpf += (up->lpf[14] = up->lpf[13]) * 9.907208e-01;
+ lpf += (up->lpf[13] = up->lpf[12]) * 1.000000e+00;
+ lpf += (up->lpf[12] = up->lpf[11]) * 9.907208e-01;
+ lpf += (up->lpf[11] = up->lpf[10]) * 9.631951e-01;
+ lpf += (up->lpf[10] = up->lpf[9]) * 9.183463e-01;
+ lpf += (up->lpf[9] = up->lpf[8]) * 8.576717e-01;
+ lpf += (up->lpf[8] = up->lpf[7]) * 7.831828e-01;
+ lpf += (up->lpf[7] = up->lpf[6]) * 6.973249e-01;
+ lpf += (up->lpf[6] = up->lpf[5]) * 6.028795e-01;
+ lpf += (up->lpf[5] = up->lpf[4]) * 5.028552e-01;
+ lpf += (up->lpf[4] = up->lpf[3]) * 4.003697e-01;
+ lpf += (up->lpf[3] = up->lpf[2]) * 2.985303e-01;
+ lpf += (up->lpf[2] = up->lpf[1]) * 2.003159e-01;
+ lpf += (up->lpf[1] = up->lpf[0]) * 1.084671e-01;
+ lpf += up->lpf[0] = disc * 2.538771e-02;
+
+ /*
+ * Maximum likelihood decoder. The UART updates each of the
+ * eight survivors and determines the span, slice level and
+ * tentative decoded character. Valid 11-bit characters are
+ * framed so that bit 1 and bit 11 (stop bits) are mark and bit
+ * 2 (start bit) is space. When a valid character is found, the
+ * survivor with maximum distance determines the final decoded
+ * character.
+ */
+ up->baud += 1. / SECOND;
+ if (up->baud > 1. / (BAUD * 8.)) {
+ up->baud -= 1. / (BAUD * 8.);
+ sp = &up->surv[up->decptr];
+ span = sp->es_max - sp->es_min;
+ up->maxsignal += (span - up->maxsignal) / 80.;
+ if (up->dbrk > 0) {
+ up->dbrk--;
+ } else if ((sp->uart & 0x403) == 0x401 && span > 1000.)
+ {
+ dist = 0;
+ j = 0;
+ for (i = 0; i < 8; i++) {
+ if (up->surv[i].dist > dist) {
+ dist = up->surv[i].dist;
+ j = i;
+ }
+ }
+ chu_decode(peer, (up->surv[j].uart >> 2) &
+ 0xff);
+ up->dbrk = 80;
+ }
+ up->decptr = (up->decptr + 1) % 8;
+ chu_uart(sp, -lpf * AGAIN);
+ }
+}
+
+
+/*
+ * chu_uart - maximum likelihood UART
+ *
+ * This routine updates a shift register holding the last 11 envelope
+ * samples. It then computes the slice level and span over these samples
+ * and determines the tentative data bits and distance. The calling
+ * program selects over the last eight survivors the one with maximum
+ * distance to determine the decoded character.
+ */
+static void
+chu_uart(
+ struct surv *sp, /* survivor structure pointer */
+ double sample /* baseband signal */
+ )
+{
+ double es_max, es_min; /* max/min envelope */
+ double slice; /* slice level */
+ double dist; /* distance */
+ double dtemp;
+ int i;
+
+ /*
+ * Save the sample and shift right. At the same time, measure
+ * the maximum and minimum over all eleven samples.
+ */
+ es_max = -1e6;
+ es_min = 1e6;
+ sp->shift[0] = sample;
+ for (i = 11; i > 0; i--) {
+ sp->shift[i] = sp->shift[i - 1];
+ if (sp->shift[i] > es_max)
+ es_max = sp->shift[i];
+ if (sp->shift[i] < es_min)
+ es_min = sp->shift[i];
+ }
+
+ /*
+ * Determine the slice level midway beteen the maximum and
+ * minimum and the span as the maximum less the minimum. Compute
+ * the distance on the assumption the first and last bits must
+ * be mark, the second space and the rest either mark or space.
+ */
+ slice = (es_max + es_min) / 2.;
+ dist = 0;
+ sp->uart = 0;
+ for (i = 1; i < 12; i++) {
+ sp->uart <<= 1;
+ dtemp = sp->shift[i];
+ if (dtemp > slice)
+ sp->uart |= 0x1;
+ if (i == 1 || i == 11) {
+ dist += dtemp - es_min;
+ } else if (i == 10) {
+ dist += es_max - dtemp;
+ } else {
+ if (dtemp > slice)
+ dist += dtemp - es_min;
+ else
+ dist += es_max - dtemp;
+ }
+ }
+ sp->es_max = es_max;
+ sp->es_min = es_min;
+ sp->dist = dist / (11 * (es_max - es_min));
+}
+#endif /* HAVE_AUDIO */
+
+
+/*
+ * chu_serial_receive - receive data from the serial device
+ */
+static void
+chu_serial_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ u_char *dpt; /* receive buffer pointer */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ up->timestamp = rbufp->recv_time;
+ dpt = (u_char *)&rbufp->recv_space;
+ chu_decode(peer, *dpt);
+}
+
+
+/*
+ * chu_decode - decode the character data
+ */
+static void
+chu_decode(
+ struct peer *peer, /* peer structure pointer */
+ int hexhex /* data character */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ l_fp tstmp; /* timestamp temp */
+ double dtemp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * If the interval since the last character is greater than the
+ * longest burst, process the last burst and start a new one. If
+ * the interval is less than this but greater than two
+ * characters, consider this a noise burst and reject it.
+ */
+ tstmp = up->timestamp;
+ if (L_ISZERO(&up->laststamp))
+ up->laststamp = up->timestamp;
+ L_SUB(&tstmp, &up->laststamp);
+ up->laststamp = up->timestamp;
+ LFPTOD(&tstmp, dtemp);
+ if (dtemp > BURST * CHAR) {
+ chu_burst(peer);
+ up->ndx = 0;
+ } else if (dtemp > 2.5 * CHAR) {
+ up->ndx = 0;
+ }
+
+ /*
+ * Append the character to the current burst and append the
+ * timestamp to the timestamp list.
+ */
+ if (up->ndx < BURST) {
+ up->cbuf[up->ndx] = hexhex & 0xff;
+ up->cstamp[up->ndx] = up->timestamp;
+ up->ndx++;
+
+ }
+}
+
+
+/*
+ * chu_burst - search for valid burst format
+ */
+static void
+chu_burst(
+ struct peer *peer
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+
+ int i;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Correlate a block of five characters with the next block of
+ * five characters. The burst distance is defined as the number
+ * of bits that match in the two blocks for format A and that
+ * match the inverse for format B.
+ */
+ if (up->ndx < MINCHAR) {
+ up->status |= RUNT;
+ return;
+ }
+ up->burdist = 0;
+ for (i = 0; i < 5 && i < up->ndx - 5; i++)
+ up->burdist += chu_dist(up->cbuf[i], up->cbuf[i + 5]);
+
+ /*
+ * If the burst distance is at least MINDIST, this must be a
+ * format A burst; if the value is not greater than -MINDIST, it
+ * must be a format B burst. If the B burst is perfect, we
+ * believe it; otherwise, it is a noise burst and of no use to
+ * anybody.
+ */
+ if (up->burdist >= MINDIST) {
+ chu_a(peer, up->ndx);
+ } else if (up->burdist <= -MINDIST) {
+ chu_b(peer, up->ndx);
+ } else {
+ up->status |= NOISE;
+ return;
+ }
+
+ /*
+ * If this is a valid burst, wait a guard time of ten seconds to
+ * allow for more bursts, then arm the poll update routine to
+ * process the minute. Don't do this if this is called from the
+ * timer interrupt routine.
+ */
+ if (peer->outdate != current_time)
+ peer->nextdate = current_time + 10;
+}
+
+
+/*
+ * chu_b - decode format B burst
+ */
+static void
+chu_b(
+ struct peer *peer,
+ int nchar
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ u_char code[11]; /* decoded timecode */
+ char tbuf[80]; /* trace buffer */
+ l_fp offset; /* timestamp offset */
+ int i;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * In a format B burst, a character is considered valid only if
+ * the first occurrence matches the last occurrence. The burst
+ * is considered valid only if all characters are valid; that
+ * is, only if the distance is 40. Note that once a valid frame
+ * has been found errors are ignored.
+ */
+ sprintf(tbuf, "chuB %04x %2d %2d ", up->status, nchar,
+ -up->burdist);
+ for (i = 0; i < nchar; i++)
+ sprintf(&tbuf[strlen(tbuf)], "%02x", up->cbuf[i]);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ if (up->burdist > -40) {
+ up->status |= BFRAME;
+ return;
+ }
+ up->status |= BVALID;
+
+ /*
+ * Convert the burst data to internal format. If this succeeds,
+ * save the timestamps for later.
+ */
+ for (i = 0; i < 5; i++) {
+ code[2 * i] = hexchar[up->cbuf[i] & 0xf];
+ code[2 * i + 1] = hexchar[(up->cbuf[i] >>
+ 4) & 0xf];
+ }
+ if (sscanf((char *)code, "%1x%1d%4d%2d%2x", &up->leap, &up->dut,
+ &pp->year, &up->tai, &up->dst) != 5) {
+ up->status |= BFORMAT;
+ return;
+ }
+ if (up->leap & 0x8)
+ up->dut = -up->dut;
+ offset.l_ui = 31;
+ offset.l_f = 0;
+ for (i = 0; i < nchar && i < 10; i++) {
+ up->tstamp[up->ntstamp] = up->cstamp[i];
+ L_SUB(&up->tstamp[up->ntstamp], &offset);
+ L_ADD(&offset, &up->charstamp);
+ if (up->ntstamp < MAXSTAGE)
+ up->ntstamp++;
+ }
+}
+
+
+/*
+ * chu_a - decode format A burst
+ */
+static void
+chu_a(
+ struct peer *peer,
+ int nchar
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ char tbuf[80]; /* trace buffer */
+ l_fp offset; /* timestamp offset */
+ int val; /* distance */
+ int temp;
+ int i, j, k;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Determine correct burst phase. There are three cases
+ * corresponding to in-phase, one character early or one
+ * character late. These cases are distinguished by the position
+ * of the framing digits x6 at positions 0 and 5 and x3 at
+ * positions 4 and 9. The correct phase is when the distance
+ * relative to the framing digits is maximum. The burst is valid
+ * only if the maximum distance is at least MINSYNC.
+ */
+ up->syndist = k = 0;
+ val = -16;
+ for (i = -1; i < 2; i++) {
+ temp = up->cbuf[i + 4] & 0xf;
+ if (i >= 0)
+ temp |= (up->cbuf[i] & 0xf) << 4;
+ val = chu_dist(temp, 0x63);
+ temp = (up->cbuf[i + 5] & 0xf) << 4;
+ if (i + 9 < nchar)
+ temp |= up->cbuf[i + 9] & 0xf;
+ val += chu_dist(temp, 0x63);
+ if (val > up->syndist) {
+ up->syndist = val;
+ k = i;
+ }
+ }
+ temp = (up->cbuf[k + 4] >> 4) & 0xf;
+ if (temp > 9 || k + 9 >= nchar || temp != ((up->cbuf[k + 9] >>
+ 4) & 0xf))
+ temp = 0;
+#ifdef HAVE_AUDIO
+ if (up->fd_audio)
+ sprintf(tbuf, "chuA %04x %4.0f %2d %2d %2d %2d %1d ",
+ up->status, up->maxsignal, nchar, up->burdist, k,
+ up->syndist, temp);
+ else
+ sprintf(tbuf, "chuA %04x %2d %2d %2d %2d %1d ",
+ up->status, nchar, up->burdist, k, up->syndist,
+ temp);
+
+#else
+ sprintf(tbuf, "chuA %04x %2d %2d %2d %2d %1d ", up->status,
+ nchar, up->burdist, k, up->syndist, temp);
+#endif /* HAVE_AUDIO */
+ for (i = 0; i < nchar; i++)
+ sprintf(&tbuf[strlen(tbuf)], "%02x",
+ up->cbuf[i]);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ if (up->syndist < MINSYNC) {
+ up->status |= AFRAME;
+ return;
+ }
+
+ /*
+ * A valid burst requires the first seconds number to match the
+ * last seconds number. If so, the burst timestamps are
+ * corrected to the current minute and saved for later
+ * processing. In addition, the seconds decode is advanced from
+ * the previous burst to the current one.
+ */
+ if (temp != 0) {
+ pp->second = 30 + temp;
+ offset.l_ui = 30 + temp;
+ offset.l_f = 0;
+ i = 0;
+ if (k < 0)
+ offset = up->charstamp;
+ else if (k > 0)
+ i = 1;
+ for (; i < nchar && i < k + 10; i++) {
+ up->tstamp[up->ntstamp] = up->cstamp[i];
+ L_SUB(&up->tstamp[up->ntstamp], &offset);
+ L_ADD(&offset, &up->charstamp);
+ if (up->ntstamp < MAXSTAGE)
+ up->ntstamp++;
+ }
+ while (temp > up->prevsec) {
+ for (j = 15; j > 0; j--) {
+ up->decode[9][j] = up->decode[9][j - 1];
+ up->decode[19][j] =
+ up->decode[19][j - 1];
+ }
+ up->decode[9][j] = up->decode[19][j] = 0;
+ up->prevsec++;
+ }
+ }
+ i = -(2 * k);
+ for (j = 0; j < nchar; j++) {
+ if (i < 0 || i > 19) {
+ i += 2;
+ continue;
+ }
+ up->decode[i][up->cbuf[j] & 0xf]++;
+ i++;
+ up->decode[i][(up->cbuf[j] >> 4) & 0xf]++;
+ i++;
+ }
+ up->status |= AVALID;
+ up->burstcnt++;
+}
+
+
+/*
+ * chu_poll - called by the transmit procedure
+ */
+static void
+chu_poll(
+ int unit,
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+ l_fp offset;
+ char synchar, qual, leapchar;
+ int minset, i;
+ double dtemp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ if (pp->coderecv == pp->codeproc)
+ up->errflg = CEVNT_TIMEOUT;
+ else
+ pp->polls++;
+
+ /*
+ * If once in sync and the radio has not been heard for awhile
+ * (30 m), it is no longer reachable. If not heard in a long
+ * while (one day), turn out the lights and start from scratch.
+ */
+ minset = ((current_time - peer->update) + 30) / 60;
+ if (up->status & INSYNC) {
+ if (minset > PANIC)
+ up->status = 0;
+ else if (minset <= HOLD)
+ peer->reach |= 1;
+ }
+
+ /*
+ * Process the last burst, if still in the burst buffer.
+ * Don't mess with anything if nothing has been heard. If the
+ * minute contains a valid A frame and valid B frame, assume
+ * synchronized; however, believe the time only if within metric
+ * threshold. Note the quality indicator is only for
+ * diagnostics; the data are used only if in sync and above
+ * metric threshold.
+ */
+ chu_burst(peer);
+ if (up->burstcnt == 0) {
+#ifdef ICOM
+ chu_newchan(peer, 0);
+#endif /* ICOM */
+ return;
+ }
+ dtemp = chu_major(peer);
+ qual = 0;
+ if (up->status & (BFRAME | AFRAME))
+ qual |= SYNERR;
+ if (up->status & (BFORMAT | AFORMAT))
+ qual |= FMTERR;
+ if (up->status & DECODE)
+ qual |= DECERR;
+ if (up->status & STAMP)
+ qual |= TSPERR;
+ if (up->status & AVALID && up->status & BVALID)
+ up->status |= INSYNC;
+ synchar = leapchar = ' ';
+ if (!(up->status & INSYNC)) {
+ pp->leap = LEAP_NOTINSYNC;
+ synchar = '?';
+ } else if (up->leap & 0x2) {
+ pp->leap = LEAP_ADDSECOND;
+ leapchar = 'L';
+ } else if (up->leap & 0x4) {
+ pp->leap = LEAP_DELSECOND;
+ leapchar = 'l';
+ } else {
+ pp->leap = LEAP_NOWARNING;
+ }
+#ifdef HAVE_AUDIO
+ if (up->fd_audio)
+ sprintf(pp->a_lastcode,
+ "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %d %s %.0f %d",
+ synchar, qual, pp->year, pp->day, pp->hour,
+ pp->minute, pp->second, leapchar, up->dst, up->dut,
+ minset, up->gain, up->ident, dtemp, up->ntstamp);
+ else
+ sprintf(pp->a_lastcode,
+ "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d",
+ synchar, qual, pp->year, pp->day, pp->hour,
+ pp->minute, pp->second, leapchar, up->dst, up->dut,
+ minset, up->ident, dtemp, up->ntstamp);
+#else
+ sprintf(pp->a_lastcode,
+ "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d",
+ synchar, qual, pp->year, pp->day, pp->hour, pp->minute,
+ pp->second, leapchar, up->dst, up->dut, minset, up->ident,
+ dtemp, up->ntstamp);
+#endif /* HAVE_AUDIO */
+ pp->lencode = strlen(pp->a_lastcode);
+
+ /*
+ * If in sync and the signal metric is above threshold, the
+ * timecode is ipso fatso valid and can be selected to
+ * discipline the clock. Be sure not to leave stray timestamps
+ * around if signals are too weak or the clock time is invalid.
+ */
+ if (up->status & INSYNC && dtemp > METRIC) {
+ if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT,
+ up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) {
+ up->errflg = CEVNT_BADTIME;
+ } else {
+ offset.l_uf = 0;
+ for (i = 0; i < up->ntstamp; i++)
+ refclock_process_offset(pp, offset,
+ up->tstamp[i], FUDGE +
+ pp->fudgetime1);
+ pp->lastref = up->timestamp;
+ refclock_receive(peer);
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ } else if (pp->sloppyclockflag & CLK_FLAG4) {
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("chu: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+#ifdef ICOM
+ chu_newchan(peer, dtemp);
+#endif /* ICOM */
+ chu_clear(peer);
+ if (up->errflg)
+ refclock_report(peer, up->errflg);
+ up->errflg = 0;
+}
+
+
+/*
+ * chu_major - majority decoder
+ */
+static double
+chu_major(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ u_char code[11]; /* decoded timecode */
+ int mindist; /* minimum distance */
+ int val1, val2; /* maximum distance */
+ int synchar; /* stray cat */
+ int temp;
+ int i, j, k;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Majority decoder. Each burst encodes two replications at each
+ * digit position in the timecode. Each row of the decoding
+ * matrix encodes the number of occurrences of each digit found
+ * at the corresponding position. The maximum over all
+ * occurrences at each position is the distance for this
+ * position and the corresponding digit is the maximum
+ * likelihood candidate. If the distance is zero, assume a miss
+ * '_'; if the distance is not more than half the total number
+ * of occurrences, assume a soft error '*'; if two different
+ * digits with the same distance are found, assume a hard error
+ * '='. These will later cause a format error when the timecode
+ * is interpreted. The decoding distance is defined as the
+ * minimum distance over the first nine digits. The tenth digit
+ * varies over the seconds, so we don't count it.
+ */
+ mindist = 16;
+ for (i = 0; i < 9; i++) {
+ val1 = val2 = 0;
+ k = 0;
+ for (j = 0; j < 16; j++) {
+ temp = up->decode[i][j] + up->decode[i + 10][j];
+ if (temp > val1) {
+ val2 = val1;
+ val1 = temp;
+ k = j;
+ }
+ }
+ if (val1 == 0)
+ code[i] = HEX_MISS;
+ else if (val1 == val2)
+ code[i] = HEX_HARD;
+ else if (val1 <= up->burstcnt)
+ code[i] = HEX_SOFT;
+ else
+ code[i] = k;
+ if (val1 < mindist)
+ mindist = val1;
+ code[i] = hexchar[code[i]];
+ }
+ code[i] = 0;
+
+ /*
+ * A valid timecode requires a minimum distance at least half
+ * the total number of occurrences. A valid timecode also
+ * requires at least 20 valid timestamps.
+ */
+ if (up->burstcnt < MINBURST || mindist < up->burstcnt)
+ up->status |= DECODE;
+ if (up->ntstamp < MINSTAMP)
+ up->status |= STAMP;
+
+ /*
+ * Compute the timecode timestamp from the days, hours and
+ * minutes of the timecode. Use clocktime() for the aggregate
+ * minutes and the minute offset computed from the burst
+ * seconds. Note that this code relies on the filesystem time
+ * for the years and does not use the years of the timecode.
+ */
+ if (sscanf((char *)code, "%1x%3d%2d%2d", &synchar, &pp->day,
+ &pp->hour, &pp->minute) != 4) {
+ up->status |= AFORMAT;
+ return (0);
+ }
+ if (up->status & (DECODE | STAMP)) {
+ up->errflg = CEVNT_BADREPLY;
+ return (0);
+ }
+ return (mindist * 100. / (2. * up->burstcnt));
+}
+
+
+/*
+ * chu_clear - clear decoding matrix
+ */
+static void
+chu_clear(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Clear stuff for the minute.
+ */
+ up->ndx = up->prevsec = 0;
+ up->burstcnt = up->ntstamp = 0;
+ up->status &= INSYNC;
+ for (i = 0; i < 20; i++) {
+ for (j = 0; j < 16; j++)
+ up->decode[i][j] = 0;
+ }
+}
+
+#ifdef ICOM
+/*
+ * chu_newchan - called once per minute to find the best channel;
+ * returns zero on success, nonzero if ICOM error.
+ */
+static int
+chu_newchan(
+ struct peer *peer,
+ double met
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct xmtr *sp;
+ char tbuf[80]; /* trace buffer */
+ int rval;
+ double metric;
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * The radio can be tuned to three channels: 0 (3330 kHz), 1
+ * (7335 kHz) and 2 (14670 kHz). There are five one-minute
+ * dwells in each cycle. During the first dwell the radio is
+ * tuned to one of three probe channels; during the remaining
+ * four dwells the radio is tuned to the data channel. The probe
+ * channel is selects as the least recently used. At the end of
+ * each dwell the channel metrics are measured and the highest
+ * one is selected as the data channel.
+ */
+ if (up->fd_icom <= 0)
+ return (0);
+
+ sp = &up->xmtr[up->achan];
+ sp->metric -= sp->integ[sp->iptr];
+ sp->integ[sp->iptr] = met;
+ sp->metric += sp->integ[sp->iptr];
+ sp->iptr = (sp->iptr + 1) % ISTAGE;
+ metric = 0;
+ j = 0;
+ for (i = 0; i < NCHAN; i++) {
+ up->xmtr[i].probe++;
+ if (i == up->achan)
+ up->xmtr[i].probe = 0;
+ if (up->xmtr[i].metric < metric)
+ continue;
+ metric = up->xmtr[i].metric;
+ j = i;
+ }
+ if (j != up->chan && metric > 0) {
+ up->chan = j;
+ sprintf(tbuf, "chu: QSY to %.3f MHz metric %.0f",
+ qsy[up->chan], metric);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ }
+
+ /*
+ * Start the next dwell. We speed up the initial sync a little.
+ * If not in sync and no bursts were heard the previous dwell,
+ * restart the probe.
+ */
+ rval = 0;
+ if (up->burstcnt == 0 && !(up->status & INSYNC))
+ up->dwell = 0;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "chu: at %ld dwell %d achan %d metric %.0f chan %d\n",
+ current_time, up->dwell, up->achan, sp->metric,
+ up->chan);
+#endif
+ if (up->dwell == 0) {
+ rval = 0;
+ for (i = 0; i < NCHAN; i++) {
+ if (up->xmtr[i].probe < rval)
+ continue;
+ rval = up->xmtr[i].probe;
+ up->achan = i;
+ }
+ rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
+ qsy[up->achan] + TUNE);
+#ifdef DEBUG
+ if (debug)
+ printf("chu: at %ld probe channel %d\n",
+ current_time, up->achan);
+#endif
+ } else {
+ if (up->achan != up->chan) {
+ rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
+ qsy[up->chan] + TUNE);
+ up->achan = up->chan;
+ }
+ }
+ sprintf(up->ident, "CHU%d", up->achan);
+ memcpy(&peer->refid, up->ident, 4);
+ up->dwell = (up->dwell + 1) % DWELL;
+ return (rval);
+}
+#endif /* ICOM */
+
+/*
+ * chu_dist - determine the distance of two octet arguments
+ */
+static int
+chu_dist(
+ int x, /* an octet of bits */
+ int y /* another octet of bits */
+ )
+{
+ int val; /* bit count */
+ int temp;
+ int i;
+
+ /*
+ * The distance is determined as the weight of the exclusive OR
+ * of the two arguments. The weight is determined by the number
+ * of one bits in the result. Each one bit increases the weight,
+ * while each zero bit decreases it.
+ */
+ temp = x ^ y;
+ val = 0;
+ for (i = 0; i < 8; i++) {
+ if ((temp & 0x1) == 0)
+ val++;
+ else
+ val--;
+ temp >>= 1;
+ }
+ return (val);
+}
+
+
+#ifdef HAVE_AUDIO
+/*
+ * chu_gain - adjust codec gain
+ *
+ * This routine is called once each second. If the signal envelope
+ * amplitude is too low, the codec gain is bumped up by four units; if
+ * too high, it is bumped down. The decoder is relatively insensitive to
+ * amplitude, so this crudity works just fine. The input port is set and
+ * the error flag is cleared, mostly to be ornery.
+ */
+static void
+chu_gain(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Apparently, the codec uses only the high order bits of the
+ * gain control field. Thus, it may take awhile for changes to
+ * wiggle the hardware bits.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > MAXGAIN)
+ up->gain = MAXGAIN;
+ } else if (up->clipcnt > MAXCLP) {
+ up->gain -= 4;
+ if (up->gain < 0)
+ up->gain = 0;
+ }
+ audio_gain(up->gain, up->mongain, up->port);
+ up->clipcnt = 0;
+}
+#endif /* HAVE_AUDIO */
+
+
+#else
+int refclock_chu_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_conf.c b/ntpd/refclock_conf.c
new file mode 100644
index 0000000..8a424f0
--- /dev/null
+++ b/ntpd/refclock_conf.c
@@ -0,0 +1,331 @@
+/*
+ * refclock_conf.c - reference clock configuration
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef REFCLOCK
+
+static struct refclock refclock_none = {
+ noentry, noentry, noentry, noentry, noentry, noentry, NOFLAGS
+};
+
+#ifdef CLOCK_LOCAL
+extern struct refclock refclock_local;
+#else
+#define refclock_local refclock_none
+#endif
+
+#if defined(CLOCK_TRAK) && defined(PPS)
+extern struct refclock refclock_trak;
+#else
+#define refclock_trak refclock_none
+#endif
+
+#ifdef CLOCK_PST
+extern struct refclock refclock_pst;
+#else
+#define refclock_pst refclock_none
+#endif
+
+#ifdef CLOCK_CHU
+extern struct refclock refclock_chu;
+#else
+#define refclock_chu refclock_none
+#endif
+
+#ifdef CLOCK_WWV
+extern struct refclock refclock_wwv;
+#else
+#define refclock_wwv refclock_none
+#endif
+
+#ifdef CLOCK_SPECTRACOM
+extern struct refclock refclock_wwvb;
+#else
+#define refclock_wwvb refclock_none
+#endif
+
+#ifdef CLOCK_PARSE
+extern struct refclock refclock_parse;
+#else
+#define refclock_parse refclock_none
+#endif
+
+#if defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
+extern struct refclock refclock_mx4200;
+#else
+#define refclock_mx4200 refclock_none
+#endif
+
+#ifdef CLOCK_AS2201
+extern struct refclock refclock_as2201;
+#else
+#define refclock_as2201 refclock_none
+#endif
+
+#ifdef CLOCK_ARBITER
+extern struct refclock refclock_arbiter;
+#else
+#define refclock_arbiter refclock_none
+#endif
+
+#ifdef CLOCK_TPRO
+extern struct refclock refclock_tpro;
+#else
+#define refclock_tpro refclock_none
+#endif
+
+#ifdef CLOCK_LEITCH
+extern struct refclock refclock_leitch;
+#else
+#define refclock_leitch refclock_none
+#endif
+
+#ifdef CLOCK_IRIG
+extern struct refclock refclock_irig;
+#else
+#define refclock_irig refclock_none
+#endif
+
+#if defined(CLOCK_MSFEES) && defined(PPS)
+extern struct refclock refclock_msfees;
+#else
+#define refclock_msfees refclock_none
+#endif
+
+#ifdef CLOCK_BANC
+extern struct refclock refclock_bancomm;
+#else
+#define refclock_bancomm refclock_none
+#endif
+
+#ifdef CLOCK_TRUETIME
+extern struct refclock refclock_true;
+#else
+#define refclock_true refclock_none
+#endif
+
+#ifdef CLOCK_DATUM
+extern struct refclock refclock_datum;
+#else
+#define refclock_datum refclock_none
+#endif
+
+#ifdef CLOCK_ACTS
+extern struct refclock refclock_acts;
+#else
+#define refclock_acts refclock_none
+#endif
+
+#ifdef CLOCK_HEATH
+extern struct refclock refclock_heath;
+#else
+#define refclock_heath refclock_none
+#endif
+
+#ifdef CLOCK_NMEA
+extern struct refclock refclock_nmea;
+#else
+#define refclock_nmea refclock_none
+#endif
+
+#ifdef CLOCK_ATOM
+extern struct refclock refclock_atom;
+#else
+#define refclock_atom refclock_none
+#endif
+
+#ifdef CLOCK_PTBACTS
+extern struct refclock refclock_ptb;
+#else
+#define refclock_ptb refclock_none
+#endif
+
+#ifdef CLOCK_USNO
+extern struct refclock refclock_usno;
+#else
+#define refclock_usno refclock_none
+#endif
+
+#ifdef CLOCK_HPGPS
+extern struct refclock refclock_hpgps;
+#else
+#define refclock_hpgps refclock_none
+#endif
+
+#ifdef CLOCK_GPSVME
+extern struct refclock refclock_gpsvme;
+#else
+#define refclock_gpsvme refclock_none
+#endif
+
+#ifdef CLOCK_ARCRON_MSF
+extern struct refclock refclock_arc;
+#else
+#define refclock_arc refclock_none
+#endif
+
+#ifdef CLOCK_SHM
+extern struct refclock refclock_shm;
+#else
+#define refclock_shm refclock_none
+#endif
+
+#ifdef CLOCK_PALISADE
+extern struct refclock refclock_palisade;
+#else
+#define refclock_palisade refclock_none
+#endif
+
+#if defined(CLOCK_ONCORE) && defined(HAVE_PPSAPI)
+extern struct refclock refclock_oncore;
+#else
+#define refclock_oncore refclock_none
+#endif
+
+#if defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI)
+extern struct refclock refclock_jupiter;
+#else
+#define refclock_jupiter refclock_none
+#endif
+
+#if defined(CLOCK_CHRONOLOG)
+extern struct refclock refclock_chronolog;
+#else
+#define refclock_chronolog refclock_none
+#endif
+
+#if defined(CLOCK_DUMBCLOCK)
+extern struct refclock refclock_dumbclock;
+#else
+#define refclock_dumbclock refclock_none
+#endif
+
+#ifdef CLOCK_ULINK
+extern struct refclock refclock_ulink;
+#else
+#define refclock_ulink refclock_none
+#endif
+
+#ifdef CLOCK_PCF
+extern struct refclock refclock_pcf;
+#else
+#define refclock_pcf refclock_none
+#endif
+
+#ifdef CLOCK_FG
+extern struct refclock refclock_fg;
+#else
+#define refclock_fg refclock_none
+#endif
+
+#ifdef CLOCK_HOPF_SERIAL
+extern struct refclock refclock_hopfser;
+#else
+#define refclock_hopfser refclock_none
+#endif
+
+#ifdef CLOCK_HOPF_PCI
+extern struct refclock refclock_hopfpci;
+#else
+#define refclock_hopfpci refclock_none
+#endif
+
+#ifdef CLOCK_JJY
+extern struct refclock refclock_jjy;
+#else
+#define refclock_jjy refclock_none
+#endif
+
+#ifdef CLOCK_TT560
+extern struct refclock refclock_tt560;
+#else
+#define refclock_tt560 refclock_none
+#endif
+
+#ifdef CLOCK_ZYFER
+extern struct refclock refclock_zyfer;
+#else
+#define refclock_zyfer refclock_none
+#endif
+
+#ifdef CLOCK_RIPENCC
+extern struct refclock refclock_ripencc;
+#else
+#define refclock_ripencc refclock_none
+#endif
+
+#ifdef CLOCK_NEOCLOCK4X
+extern struct refclock refclock_neoclock4x;
+#else
+#define refclock_neoclock4x refclock_none
+#endif
+
+/*
+ * Order is clock_start(), clock_shutdown(), clock_poll(),
+ * clock_control(), clock_init(), clock_buginfo, clock_flags;
+ *
+ * Types are defined in ntp.h. The index must match this.
+ */
+struct refclock *refclock_conf[] = {
+ &refclock_none, /* 0 REFCLK_NONE */
+ &refclock_local, /* 1 REFCLK_LOCAL */
+ &refclock_trak, /* 2 REFCLK_GPS_TRAK */
+ &refclock_pst, /* 3 REFCLK_WWV_PST */
+ &refclock_wwvb, /* 4 REFCLK_SPECTRACOM */
+ &refclock_true, /* 5 REFCLK_TRUETIME */
+ &refclock_irig, /* 6 REFCLK_IRIG_AUDIO */
+ &refclock_chu, /* 7 REFCLK_CHU_AUDIO */
+ &refclock_parse, /* 8 REFCLK_PARSE */
+ &refclock_mx4200, /* 9 REFCLK_GPS_MX4200 */
+ &refclock_as2201, /* 10 REFCLK_GPS_AS2201 */
+ &refclock_arbiter, /* 11 REFCLK_GPS_ARBITER */
+ &refclock_tpro, /* 12 REFCLK_IRIG_TPRO */
+ &refclock_leitch, /* 13 REFCLK_ATOM_LEITCH */
+ &refclock_msfees, /* 14 REFCLK_MSF_EES */
+ &refclock_true, /* 15 alias for REFCLK_TRUETIME */
+ &refclock_bancomm, /* 16 REFCLK_IRIG_BANCOMM */
+ &refclock_datum, /* 17 REFCLK_GPS_DATUM */
+ &refclock_acts, /* 18 REFCLK_NIST_ACTS */
+ &refclock_heath, /* 19 REFCLK_WWV_HEATH */
+ &refclock_nmea, /* 20 REFCLK_GPS_NMEA */
+ &refclock_gpsvme, /* 21 REFCLK_GPS_VME */
+ &refclock_atom, /* 22 REFCLK_ATOM_PPS */
+ &refclock_ptb, /* 23 REFCLK_PTB_ACTS */
+ &refclock_usno, /* 24 REFCLK_USNO */
+ &refclock_true, /* 25 alias for REFCLK_TRUETIME */
+ &refclock_hpgps, /* 26 REFCLK_GPS_HP */
+ &refclock_arc, /* 27 REFCLK_ARCRON_MSF */
+ &refclock_shm, /* 28 REFCLK_SHM */
+ &refclock_palisade, /* 29 REFCLK_PALISADE */
+ &refclock_oncore, /* 30 REFCLK_ONCORE */
+ &refclock_jupiter, /* 31 REFCLK_GPS_JUPITER */
+ &refclock_chronolog, /* 32 REFCLK_CHRONOLOG */
+ &refclock_dumbclock, /* 33 REFCLK_DUMBCLOCK */
+ &refclock_ulink, /* 34 REFCLOCK_ULINK */
+ &refclock_pcf, /* 35 REFCLOCK_PCF */
+ &refclock_wwv, /* 36 REFCLOCK_WWV_AUDIO */
+ &refclock_fg, /* 37 REFCLOCK_FG */
+ &refclock_hopfser, /* 38 REFCLK_HOPF_SERIAL */
+ &refclock_hopfpci, /* 39 REFCLK_HOPF_PCI */
+ &refclock_jjy, /* 40 REFCLK_JJY */
+ &refclock_tt560, /* 41 REFCLK_TT560 */
+ &refclock_zyfer, /* 42 REFCLK_ZYFER */
+ &refclock_ripencc, /* 43 REFCLK_RIPENCC */
+ &refclock_neoclock4x /* 44 REFCLK_NEOCLOCK4X */
+};
+
+u_char num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *);
+
+#else
+int refclock_conf_bs;
+#endif
diff --git a/ntpd/refclock_datum.c b/ntpd/refclock_datum.c
new file mode 100644
index 0000000..82b7369
--- /dev/null
+++ b/ntpd/refclock_datum.c
@@ -0,0 +1,869 @@
+/*
+** refclock_datum - clock driver for the Datum Programmable Time Server
+**
+** Important note: This driver assumes that you have termios. If you have
+** a system that does not have termios, you will have to modify this driver.
+**
+** Sorry, I have only tested this driver on SUN and HP platforms.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_DATUM)
+
+/*
+** Include Files
+*/
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(WWVBCLK)
+#include <sys/clkdefs.h>
+#endif /* WWVBCLK */
+#endif /* STREAM */
+
+#include "ntp_stdlib.h"
+
+/*
+** This driver supports the Datum Programmable Time System (PTS) clock.
+** The clock works in very straight forward manner. When it receives a
+** time code request (e.g., the ascii string "//k/mn"), it responds with
+** a seven byte BCD time code. This clock only responds with a
+** time code after it first receives the "//k/mn" message. It does not
+** periodically send time codes back at some rate once it is started.
+** the returned time code can be broken down into the following fields.
+**
+** _______________________________
+** Bit Index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+** ===============================
+** byte 0: | - - - - | H D |
+** ===============================
+** byte 1: | T D | U D |
+** ===============================
+** byte 2: | - - | T H | U H |
+** ===============================
+** byte 3: | - | T M | U M |
+** ===============================
+** byte 4: | - | T S | U S |
+** ===============================
+** byte 5: | t S | h S |
+** ===============================
+** byte 6: | m S | - - - - |
+** ===============================
+**
+** In the table above:
+**
+** "-" means don't care
+** "H D", "T D", and "U D" means Hundreds, Tens, and Units of Days
+** "T H", and "UH" means Tens and Units of Hours
+** "T M", and "U M" means Tens and Units of Minutes
+** "T S", and "U S" means Tens and Units of Seconds
+** "t S", "h S", and "m S" means tenths, hundredths, and thousandths
+** of seconds
+**
+** The Datum PTS communicates throught the RS232 port on your machine.
+** Right now, it assumes that you have termios. This driver has been tested
+** on SUN and HP workstations. The Datum PTS supports various IRIG and
+** NASA input codes. This driver assumes that the name of the device is
+** /dev/datum. You will need to make a soft link to your RS232 device or
+** create a new driver to use this refclock.
+*/
+
+/*
+** Datum PTS defines
+*/
+
+/*
+** Note that if GMT is defined, then the Datum PTS must use Greenwich
+** time. Otherwise, this driver allows the Datum PTS to use the current
+** wall clock for its time. It determines the time zone offset by minimizing
+** the error after trying several time zone offsets. If the Datum PTS
+** time is Greenwich time and GMT is not defined, everything should still
+** work since the time zone will be found to be 0. What this really means
+** is that your system time (at least to start with) must be within the
+** correct time by less than +- 30 minutes. The default is for GMT to not
+** defined. If you really want to force GMT without the funny +- 30 minute
+** stuff then you must define (uncomment) GMT below.
+*/
+
+/*
+#define GMT
+#define DEBUG_DATUM_PTC
+#define LOG_TIME_ERRORS
+*/
+
+
+#define PRECISION (-10) /* precision assumed 1/1024 ms */
+#define REFID "DATM" /* reference id */
+#define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */
+#define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */
+
+#define DATUM_MAX_ERROR2 (DATUM_MAX_ERROR*DATUM_MAX_ERROR)
+
+/*
+** The Datum PTS structure
+*/
+
+/*
+** I don't use a fixed array of MAXUNITS like everyone else just because
+** I don't like to program that way. Sorry if this bothers anyone. I assume
+** that you can use any id for your unit and I will search for it in a
+** dynamic array of units until I find it. I was worried that users might
+** enter a bad id in their configuration file (larger than MAXUNITS) and
+** besides, it is just cleaner not to have to assume that you have a fixed
+** number of anything in a program.
+*/
+
+struct datum_pts_unit {
+ struct peer *peer; /* peer used by ntp */
+ struct refclockio io; /* io structure used by ntp */
+ int PTS_fd; /* file descriptor for PTS */
+ u_int unit; /* id for unit */
+ u_long timestarted; /* time started */
+ l_fp lastrec; /* time tag for the receive time (system) */
+ l_fp lastref; /* reference time (Datum time) */
+ u_long yearstart; /* the year that this clock started */
+ int coderecv; /* number of time codes received */
+ int day; /* day */
+ int hour; /* hour */
+ int minute; /* minutes */
+ int second; /* seconds */
+ int msec; /* miliseconds */
+ int usec; /* miliseconds */
+ u_char leap; /* funny leap character code */
+ char retbuf[8]; /* returned time from the datum pts */
+ char nbytes; /* number of bytes received from datum pts */
+ double sigma2; /* average squared error (roughly) */
+ int tzoff; /* time zone offest from GMT */
+};
+
+/*
+** PTS static constant variables for internal use
+*/
+
+static char TIME_REQUEST[6]; /* request message sent to datum for time */
+static int nunits; /* number of active units */
+static struct datum_pts_unit
+**datum_pts_unit; /* dynamic array of datum PTS structures */
+
+/*
+** Callback function prototypes that ntpd needs to know about.
+*/
+
+static int datum_pts_start P((int, struct peer *));
+static void datum_pts_shutdown P((int, struct peer *));
+static void datum_pts_poll P((int, struct peer *));
+static void datum_pts_control P((int, struct refclockstat *,
+ struct refclockstat *, struct peer *));
+static void datum_pts_init P((void));
+static void datum_pts_buginfo P((int, struct refclockbug *, struct peer *));
+
+/*
+** This is the call back function structure that ntpd actually uses for
+** this refclock.
+*/
+
+struct refclock refclock_datum = {
+ datum_pts_start, /* start up a new Datum refclock */
+ datum_pts_shutdown, /* shutdown a Datum refclock */
+ datum_pts_poll, /* sends out the time request */
+ datum_pts_control, /* not used */
+ datum_pts_init, /* initialization (called first) */
+ datum_pts_buginfo, /* not used */
+ NOFLAGS /* we are not setting any special flags */
+};
+
+/*
+** The datum_pts_receive callback function is handled differently from the
+** rest. It is passed to the ntpd io data structure. Basically, every
+** 64 seconds, the datum_pts_poll() routine is called. It sends out the time
+** request message to the Datum Programmable Time System. Then, ntpd
+** waits on a select() call to receive data back. The datum_pts_receive()
+** function is called as data comes back. We expect a seven byte time
+** code to be returned but the datum_pts_receive() function may only get
+** a few bytes passed to it at a time. In other words, this routine may
+** get called by the io stuff in ntpd a few times before we get all seven
+** bytes. Once the last byte is received, we process it and then pass the
+** new time measurement to ntpd for updating the system time. For now,
+** there is no 3 state filtering done on the time measurements. The
+** jitter may be a little high but at least for its current use, it is not
+** a problem. We have tried to keep things as simple as possible. This
+** clock should not jitter more than 1 or 2 mseconds at the most once
+** things settle down. It is important to get the right drift calibrated
+** in the ntpd.drift file as well as getting the right tick set up right
+** using tickadj for SUNs. Tickadj is not used for the HP but you need to
+** remember to bring up the adjtime daemon because HP does not support
+** the adjtime() call.
+*/
+
+static void datum_pts_receive P((struct recvbuf *));
+
+/*......................................................................*/
+/* datum_pts_start - start up the datum PTS. This means open the */
+/* RS232 device and set up the data structure for my unit. */
+/*......................................................................*/
+
+static int
+datum_pts_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct datum_pts_unit **temp_datum_pts_unit;
+ struct datum_pts_unit *datum_pts;
+#ifdef HAVE_TERMIOS
+ struct termios arg;
+#endif
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Starting Datum PTS unit %d\n", unit);
+#endif
+
+ /*
+ ** Create the memory for the new unit
+ */
+
+ temp_datum_pts_unit = (struct datum_pts_unit **)
+ malloc((nunits+1)*sizeof(struct datum_pts_unit *));
+ if (nunits > 0) memcpy(temp_datum_pts_unit, datum_pts_unit,
+ nunits*sizeof(struct datum_pts_unit *));
+ free(datum_pts_unit);
+ datum_pts_unit = temp_datum_pts_unit;
+ datum_pts_unit[nunits] = (struct datum_pts_unit *)
+ malloc(sizeof(struct datum_pts_unit));
+ datum_pts = datum_pts_unit[nunits];
+
+ datum_pts->unit = unit; /* set my unit id */
+ datum_pts->yearstart = 0; /* initialize the yearstart to 0 */
+ datum_pts->sigma2 = 0.0; /* initialize the sigma2 to 0 */
+
+ /*
+ ** Open the Datum PTS device
+ */
+
+ datum_pts->PTS_fd = open("/dev/datum",O_RDWR);
+
+ fcntl(datum_pts->PTS_fd, F_SETFL, 0); /* clear the descriptor flags */
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Opening RS232 port with file descriptor %d\n",
+ datum_pts->PTS_fd);
+#endif
+
+ /*
+ ** Set up the RS232 terminal device information. Note that we assume that
+ ** we have termios. This code has only been tested on SUNs and HPs. If your
+ ** machine does not have termios this driver cannot be initialized. You can change this
+ ** if you want by editing this source. Please give the changes back to the
+ ** ntp folks so that it can become part of their regular distribution.
+ */
+
+#ifdef HAVE_TERMIOS
+
+ arg.c_iflag = IGNBRK;
+ arg.c_oflag = 0;
+ arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL;
+ arg.c_lflag = 0;
+ arg.c_cc[VMIN] = 0; /* start timeout timer right away (not used) */
+ arg.c_cc[VTIME] = 30; /* 3 second timout on reads (not used) */
+
+ tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg);
+
+#else
+
+ msyslog(LOG_ERR, "Datum_PTS: Termios not supported in this driver");
+ (void)close(datum_pts->PTS_fd);
+
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ return 0;
+
+#endif
+
+ /*
+ ** Initialize the ntpd IO structure
+ */
+
+ datum_pts->peer = peer;
+ datum_pts->io.clock_recv = datum_pts_receive;
+ datum_pts->io.srcclock = (caddr_t)datum_pts;
+ datum_pts->io.datalen = 0;
+ datum_pts->io.fd = datum_pts->PTS_fd;
+
+ if (!io_addclock(&(datum_pts->io))) {
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Problem adding clock\n");
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Problem adding clock");
+ (void)close(datum_pts->PTS_fd);
+
+ return 0;
+ }
+
+ /*
+ ** Now add one to the number of units and return a successful code
+ */
+
+ nunits++;
+ return 1;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_shutdown - this routine shuts doen the device and */
+/* removes the memory for the unit. */
+/*......................................................................*/
+
+static void
+datum_pts_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ int i,j;
+ struct datum_pts_unit **temp_datum_pts_unit;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Shutdown Datum PTS\n");
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS");
+
+ /*
+ ** First we have to find the right unit (i.e., the one with the same id).
+ ** We do this by looping through the dynamic array of units intil we find
+ ** it. Note, that I don't simply use an array with a maximimum number of
+ ** Datum PTS units. Everything is completely dynamic.
+ */
+
+ for (i=0; i<nunits; i++) {
+ if (datum_pts_unit[i]->unit == unit) {
+
+ /*
+ ** We found the unit so close the file descriptor and free up the memory used
+ ** by the structure.
+ */
+
+ io_closeclock(&datum_pts_unit[i]->io);
+ close(datum_pts_unit[i]->PTS_fd);
+ free(datum_pts_unit[i]);
+
+ /*
+ ** Now clean up the datum_pts_unit dynamic array so that there are no holes.
+ ** This may mean moving pointers around, etc., to keep things compact.
+ */
+
+ if (nunits > 1) {
+
+ temp_datum_pts_unit = (struct datum_pts_unit **)
+ malloc((nunits-1)*sizeof(struct datum_pts_unit *));
+ if (i!= 0) memcpy(temp_datum_pts_unit, datum_pts_unit,
+ i*sizeof(struct datum_pts_unit *));
+
+ for (j=i+1; j<nunits; j++) {
+ temp_datum_pts_unit[j-1] = datum_pts_unit[j];
+ }
+
+ free(datum_pts_unit);
+ datum_pts_unit = temp_datum_pts_unit;
+
+ }else{
+
+ free(datum_pts_unit);
+ datum_pts_unit = NULL;
+
+ }
+
+ return;
+
+ }
+ }
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Error, could not shut down unit %d\n",unit);
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Could not shut down Datum PTS unit %d",unit);
+
+}
+
+/*......................................................................*/
+/* datum_pts_poll - this routine sends out the time request to the */
+/* Datum PTS device. The time will be passed back in the */
+/* datum_pts_receive() routine. */
+/*......................................................................*/
+
+static void
+datum_pts_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ int i;
+ int unit_index;
+ int error_code;
+ struct datum_pts_unit *datum_pts;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Poll Datum PTS\n");
+#endif
+
+ /*
+ ** Find the right unit and send out a time request once it is found.
+ */
+
+ unit_index = -1;
+ for (i=0; i<nunits; i++) {
+ if (datum_pts_unit[i]->unit == unit) {
+ unit_index = i;
+ datum_pts = datum_pts_unit[i];
+ error_code = write(datum_pts->PTS_fd, TIME_REQUEST, 6);
+ if (error_code != 6) perror("TIME_REQUEST");
+ datum_pts->nbytes = 0;
+ break;
+ }
+ }
+
+ /*
+ ** Print out an error message if we could not find the right unit.
+ */
+
+ if (unit_index == -1) {
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Error, could not poll unit %d\n",unit);
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Could not poll unit %d",unit);
+ return;
+
+ }
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_control - not used */
+/*......................................................................*/
+
+static void
+datum_pts_control(
+ int unit,
+ struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *peer
+ )
+{
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Control Datum PTS\n");
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_init - initializes things for all possible Datum */
+/* time code generators that might be used. In practice, this is */
+/* only called once at the beginning before anything else is */
+/* called. */
+/*......................................................................*/
+
+static void
+datum_pts_init(void)
+{
+
+ /* */
+ /*...... open up the log file if we are debugging ......................*/
+ /* */
+
+ /*
+ ** Open up the log file if we are debugging. For now, send data out to the
+ ** screen (stdout).
+ */
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Init Datum PTS\n");
+#endif
+
+ /*
+ ** Initialize the time request command string. This is the only message
+ ** that we ever have to send to the Datum PTS (although others are defined).
+ */
+
+ memcpy(TIME_REQUEST, "//k/mn",6);
+
+ /*
+ ** Initialize the number of units to 0 and set the dynamic array of units to
+ ** NULL since there are no units defined yet.
+ */
+
+ datum_pts_unit = NULL;
+ nunits = 0;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_buginfo - not used */
+/*......................................................................*/
+
+static void
+datum_pts_buginfo(
+ int unit,
+ register struct refclockbug *bug,
+ register struct peer *peer
+ )
+{
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Buginfo Datum PTS\n");
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_receive - receive the time buffer that was read in */
+/* by the ntpd io handling routines. When 7 bytes have been */
+/* received (it may take several tries before all 7 bytes are */
+/* received), then the time code must be unpacked and sent to */
+/* the ntpd clock_receive() routine which causes the systems */
+/* clock to be updated (several layers down). */
+/*......................................................................*/
+
+static void
+datum_pts_receive(
+ struct recvbuf *rbufp
+ )
+{
+ int i;
+ l_fp tstmp;
+ struct datum_pts_unit *datum_pts;
+ char *dpt;
+ int dpend;
+ int tzoff;
+ int timerr;
+ double ftimerr, abserr;
+#ifdef DEBUG_DATUM_PTC
+ double dispersion;
+#endif
+ int goodtime;
+ /*double doffset;*/
+
+ /*
+ ** Get the time code (maybe partial) message out of the rbufp buffer.
+ */
+
+ datum_pts = (struct datum_pts_unit *)rbufp->recv_srcclock;
+ dpt = (char *)&rbufp->recv_space;
+ dpend = rbufp->recv_length;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Receive Datum PTS: %d bytes\n", dpend);
+#endif
+
+ /* */
+ /*...... save the ntp system time when the first byte is received ......*/
+ /* */
+
+ /*
+ ** Save the ntp system time when the first byte is received. Note that
+ ** because it may take several calls to this routine before all seven
+ ** bytes of our return message are finally received by the io handlers in
+ ** ntpd, we really do want to use the time tag when the first byte is
+ ** received to reduce the jitter.
+ */
+
+ if (datum_pts->nbytes == 0) {
+ datum_pts->lastrec = rbufp->recv_time;
+ }
+
+ /*
+ ** Increment our count to the number of bytes received so far. Return if we
+ ** haven't gotten all seven bytes yet.
+ */
+
+ for (i=0; i<dpend; i++) {
+ datum_pts->retbuf[datum_pts->nbytes+i] = dpt[i];
+ }
+
+ datum_pts->nbytes += dpend;
+
+ if (datum_pts->nbytes != 7) {
+ return;
+ }
+
+ /*
+ ** Convert the seven bytes received in our time buffer to day, hour, minute,
+ ** second, and msecond values. The usec value is not used for anything
+ ** currently. It is just the fractional part of the time stored in units
+ ** of microseconds.
+ */
+
+ datum_pts->day = 100*(datum_pts->retbuf[0] & 0x0f) +
+ 10*((datum_pts->retbuf[1] & 0xf0)>>4) +
+ (datum_pts->retbuf[1] & 0x0f);
+
+ datum_pts->hour = 10*((datum_pts->retbuf[2] & 0x30)>>4) +
+ (datum_pts->retbuf[2] & 0x0f);
+
+ datum_pts->minute = 10*((datum_pts->retbuf[3] & 0x70)>>4) +
+ (datum_pts->retbuf[3] & 0x0f);
+
+ datum_pts->second = 10*((datum_pts->retbuf[4] & 0x70)>>4) +
+ (datum_pts->retbuf[4] & 0x0f);
+
+ datum_pts->msec = 100*((datum_pts->retbuf[5] & 0xf0) >> 4) +
+ 10*(datum_pts->retbuf[5] & 0x0f) +
+ ((datum_pts->retbuf[6] & 0xf0)>>4);
+
+ datum_pts->usec = 1000*datum_pts->msec;
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("day %d, hour %d, minute %d, second %d, msec %d\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec);
+#endif
+
+ /*
+ ** Get the GMT time zone offset. Note that GMT should be zero if the Datum
+ ** reference time is using GMT as its time base. Otherwise we have to
+ ** determine the offset if the Datum PTS is using time of day as its time
+ ** base.
+ */
+
+ goodtime = 0; /* We are not sure about the time and offset yet */
+
+#ifdef GMT
+
+ /*
+ ** This is the case where the Datum PTS is using GMT so there is no time
+ ** zone offset.
+ */
+
+ tzoff = 0; /* set time zone offset to 0 */
+
+#else
+
+ /*
+ ** This is the case where the Datum PTS is using regular time of day for its
+ ** time so we must compute the time zone offset. The way we do it is kind of
+ ** funny but it works. We loop through different time zones (0 to 24) and
+ ** pick the one that gives the smallest error (+- one half hour). The time
+ ** zone offset is stored in the datum_pts structure for future use. Normally,
+ ** the clocktime() routine is only called once (unless the time zone offset
+ ** changes due to daylight savings) since the goodtime flag is set when a
+ ** good time is found (with a good offset). Note that even if the Datum
+ ** PTS is using GMT, this mechanism will still work since it should come up
+ ** with a value for tzoff = 0 (assuming that your system clock is within
+ ** a half hour of the Datum time (even with time zone differences).
+ */
+
+ for (tzoff=0; tzoff<24; tzoff++) {
+ if (clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ (tzoff + datum_pts->tzoff) % 24,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+ datum_pts->lastref.l_uf = 0;
+ error = datum_pts->lastref.l_ui - datum_pts->lastrec.l_ui;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone (clocktime method) = %d, error = %d\n", tzoff, error);
+#endif
+
+ if ((error < 1799) && (error > -1799)) {
+ tzoff = (tzoff + datum_pts->tzoff) % 24;
+ datum_pts->tzoff = tzoff;
+ goodtime = 1;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone found (clocktime method) = %d\n",tzoff);
+#endif
+
+ break;
+ }
+
+ }
+ }
+
+#endif
+
+ /*
+ ** Make sure that we have a good time from the Datum PTS. Clocktime() also
+ ** sets yearstart and lastref.l_ui. We will have to set astref.l_uf (i.e.,
+ ** the fraction of a second) stuff later.
+ */
+
+ if (!goodtime) {
+
+ if (!clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ {
+ printf("Error: bad clocktime\n");
+ printf("GMT %d, lastrec %d, yearstart %d, lastref %d\n",
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ datum_pts->yearstart,
+ datum_pts->lastref.l_ui);
+ }
+#endif
+
+ msyslog(LOG_ERR, "Datum_PTS: Bad clocktime");
+
+ return;
+
+ }else{
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Good clocktime\n");
+#endif
+
+ }
+
+ }
+
+ /*
+ ** We have datum_pts->lastref.l_ui set (which is the integer part of the
+ ** time. Now set the microseconds field.
+ */
+
+ TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf);
+
+ /*
+ ** Compute the time correction as the difference between the reference
+ ** time (i.e., the Datum time) minus the receive time (system time).
+ */
+
+ tstmp = datum_pts->lastref; /* tstmp is the datum ntp time */
+ L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is now the correction */
+ datum_pts->coderecv++; /* increment a counter */
+
+#ifdef DEBUG_DATUM_PTC
+ dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */
+ ftimerr = dispersion;
+ ftimerr /= (1024.0 * 64.0);
+ if (debug)
+ printf("dispersion = %d, %f\n", dispersion, ftimerr);
+#endif
+
+ /*
+ ** Pass the new time to ntpd through the refclock_receive function. Note
+ ** that we are not trying to make any corrections due to the time it takes
+ ** for the Datum PTS to send the message back. I am (erroneously) assuming
+ ** that the time for the Datum PTS to send the time back to us is negligable.
+ ** I suspect that this time delay may be as much as 15 ms or so (but probably
+ ** less). For our needs at JPL, this kind of error is ok so it is not
+ ** necessary to use fudge factors in the ntp.conf file. Maybe later we will.
+ */
+ /*LFPTOD(&tstmp, doffset);*/
+ datum_pts->lastref = datum_pts->lastrec;
+ refclock_receive(datum_pts->peer);
+
+ /*
+ ** Compute sigma squared (not used currently). Maybe later, this could be
+ ** used for the dispersion estimate. The problem is that ntpd does not link
+ ** in the math library so sqrt() is not available. Anyway, this is useful
+ ** for debugging. Maybe later I will just use absolute values for the time
+ ** error to come up with my dispersion estimate. Anyway, for now my dispersion
+ ** is set to 0.
+ */
+
+ timerr = tstmp.l_ui<<20;
+ timerr |= (tstmp.l_uf>>12) & 0x000fffff;
+ ftimerr = timerr;
+ ftimerr /= 1024*1024;
+ abserr = ftimerr;
+ if (ftimerr < 0.0) abserr = -ftimerr;
+
+ if (datum_pts->sigma2 == 0.0) {
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = abserr*abserr;
+ }else{
+ datum_pts->sigma2 = DATUM_MAX_ERROR2;
+ }
+ }else{
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*abserr*abserr;
+ }else{
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*DATUM_MAX_ERROR2;
+ }
+ }
+
+#ifdef DEBUG_DATUM_PTC
+ if (debug)
+ printf("Time error = %f seconds\n", ftimerr);
+#endif
+
+#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS)
+ if (debug)
+ printf("PTS: day %d, hour %d, minute %d, second %d, msec %d, Time Error %f\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec,
+ ftimerr);
+#endif
+
+}
+#else
+int refclock_datum_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_dumbclock.c b/ntpd/refclock_dumbclock.c
new file mode 100644
index 0000000..2788649
--- /dev/null
+++ b/ntpd/refclock_dumbclock.c
@@ -0,0 +1,385 @@
+/*
+ * refclock_dumbclock - clock driver for a unknown time distribution system
+ * that only provides hh:mm:ss (in local time, yet!).
+ */
+
+/*
+ * Must interpolate back to local time. Very annoying.
+ */
+#define GET_LOCALTIME
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(SYS_WINNT)
+#undef close
+#define close closesocket
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports a generic dumb clock that only outputs hh:mm:ss,
+ * in local time, no less.
+ *
+ * Input format:
+ *
+ * hh:mm:ss <cr>
+ *
+ * hh:mm:ss -- what you'd expect, with a 24 hour clock. (Heck, that's the only
+ * way it could get stupider.) We take time on the <cr>.
+ *
+ * The original source of this module was the WWVB module.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/dumbclock%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "dumbclock" /* reference ID */
+#define DESCRIPTION "Dumb clock" /* WRU */
+
+
+/*
+ * Insanity check. Since the time is local, we need to make sure that during midnight
+ * transitions, we can convert back to Unix time. If the conversion results in some number
+ * worse than this number of seconds away, assume the next day and retry.
+ */
+#define INSANE_SECONDS 3600
+
+/*
+ * Dumb clock control structure
+ */
+struct dumbclock_unit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ u_char linect; /* count ignored lines (for monitor */
+ struct tm ymd; /* struct tm for y/m/d only */
+};
+
+/*
+ * Function prototypes
+ */
+static int dumbclock_start P((int, struct peer *));
+static void dumbclock_shutdown P((int, struct peer *));
+static void dumbclock_receive P((struct recvbuf *));
+#if 0
+static void dumbclock_poll P((int, struct peer *));
+#endif
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_dumbclock = {
+ dumbclock_start, /* start up driver */
+ dumbclock_shutdown, /* shut down driver */
+ noentry, /* poll the driver -- a nice fabrication */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * dumbclock_start - open the devices and initialize data for processing
+ */
+static int
+dumbclock_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct dumbclock_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ struct tm *tm_time_p;
+ time_t now;
+
+ /*
+ * Open serial port. Don't bother with CLK line discipline, since
+ * it's not available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+#ifdef DEBUG
+ if (debug)
+ printf ("starting Dumbclock with device %s\n",device);
+#endif
+ fd = refclock_open(device, SPEED232, 0);
+ if (fd < 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct dumbclock_unit *)emalloc(sizeof(struct dumbclock_unit));
+ if (up == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct dumbclock_unit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = dumbclock_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+
+ time(&now);
+#ifdef GET_LOCALTIME
+ tm_time_p = localtime(&now);
+#else
+ tm_time_p = gmtime(&now);
+#endif
+ if (tm_time_p)
+ {
+ up->ymd = *tm_time_p;
+ }
+ else
+ {
+ return 0;
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * dumbclock_shutdown - shut down the clock
+ */
+static void
+dumbclock_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct dumbclock_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct dumbclock_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * dumbclock_receive - receive data from the serial interface
+ */
+static void
+dumbclock_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct dumbclock_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int hours; /* hour-of-day */
+ int minutes; /* minutes-past-the-hour */
+ int seconds; /* seconds */
+ int temp; /* int temp */
+ int got_good; /* got a good time flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct dumbclock_unit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = (u_short)temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+
+#ifdef DEBUG
+ if (debug)
+ printf("dumbclock: timecode %d %s\n",
+ pp->lencode, pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business. Check the timecode format...
+ */
+ got_good=0;
+ if (sscanf(pp->a_lastcode,"%02d:%02d:%02d",
+ &hours,&minutes,&seconds) == 3)
+ {
+ struct tm *gmtp;
+ struct tm *lt_p;
+ time_t asserted_time; /* the SPM time based on the composite time+date */
+ struct tm asserted_tm; /* the struct tm of the same */
+ int adjyear;
+ int adjmon;
+ int reality_delta;
+ time_t now;
+
+
+ /*
+ * Convert to GMT for sites that distribute localtime. This
+ * means we have to figure out what day it is. Easier said
+ * than done...
+ */
+
+ asserted_tm.tm_year = up->ymd.tm_year;
+ asserted_tm.tm_mon = up->ymd.tm_mon;
+ asserted_tm.tm_mday = up->ymd.tm_mday;
+ asserted_tm.tm_hour = hours;
+ asserted_tm.tm_min = minutes;
+ asserted_tm.tm_sec = seconds;
+ asserted_tm.tm_isdst = -1;
+
+#ifdef GET_LOCALTIME
+ asserted_time = mktime (&asserted_tm);
+ time(&now);
+#else
+#include "GMT unsupported for dumbclock!"
+#endif
+ reality_delta = asserted_time - now;
+
+ /*
+ * We assume that if the time is grossly wrong, it's because we got the
+ * year/month/day wrong.
+ */
+ if (reality_delta > INSANE_SECONDS)
+ {
+ asserted_time -= SECSPERDAY; /* local clock behind real time */
+ }
+ else if (-reality_delta > INSANE_SECONDS)
+ {
+ asserted_time += SECSPERDAY; /* local clock ahead of real time */
+ }
+ lt_p = localtime(&asserted_time);
+ if (lt_p)
+ {
+ up->ymd = *lt_p;
+ }
+ else
+ {
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+
+ if ((gmtp = gmtime (&asserted_time)) == NULL)
+ {
+ refclock_report (peer, CEVNT_FAULT);
+ return;
+ }
+ adjyear = gmtp->tm_year+1900;
+ adjmon = gmtp->tm_mon+1;
+ pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
+ pp->hour = gmtp->tm_hour;
+ pp->minute = gmtp->tm_min;
+ pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+ if (debug)
+ printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
+ pp->second);
+#endif
+
+ got_good=1;
+ }
+
+ if (!got_good)
+ {
+ if (up->linect > 0)
+ up->linect--;
+ else
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ up->lasthour = (u_char)pp->hour;
+}
+
+#if 0
+/*
+ * dumbclock_poll - called by the transmit procedure
+ */
+static void
+dumbclock_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct dumbclock_unit *up;
+ struct refclockproc *pp;
+ char pollchar;
+
+ /*
+ * Time to poll the clock. The Chrono-log clock is supposed to
+ * respond to a 'T' by returning a timecode in the format(s)
+ * specified above. Ours does (can?) not, but this seems to be
+ * an installation-specific problem. This code is dyked out,
+ * but may be re-enabled if anyone ever finds a Chrono-log that
+ * actually listens to this command.
+ */
+#if 0
+ pp = peer->procptr;
+ up = (struct dumbclock_unit *)pp->unitptr;
+ if (peer->reach == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ if (up->linect > 0)
+ pollchar = 'R';
+ else
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+#endif
+}
+#endif
+
+#else
+int refclock_dumbclock_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_fg.c b/ntpd/refclock_fg.c
new file mode 100644
index 0000000..ebcf1b5
--- /dev/null
+++ b/ntpd/refclock_fg.c
@@ -0,0 +1,348 @@
+/*
+ * refclock_fg - clock driver for the Forum Graphic GPS datating station
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_FG)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Forum Graphic GPS dating station.
+ * More information about FG GPS is available on http://www.forumgraphic.com
+ * Contact das@amt.ru for any question about this driver.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/fgclock%d"
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS"
+#define DESCRIPTION "Forum Graphic GPS dating station"
+#define LENFG 26 /* timecode length */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+
+/*
+ * Function prototypes
+ */
+static int fg_init P((int));
+static int fg_start P((int, struct peer *));
+static void fg_shutdown P((int, struct peer *));
+static void fg_poll P((int, struct peer *));
+static void fg_receive P((struct recvbuf *));
+
+/*
+ * Forum Graphic unit control structure
+ */
+
+struct fgunit {
+ int pollnum; /* Use peer.poll instead? */
+ int status; /* Hug to check status information on GPS */
+ int y2kwarn; /* Y2K bug */
+};
+
+/*
+ * Queries definition
+ */
+static char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_fg = {
+ fg_start, /* start up driver */
+ fg_shutdown, /* shut down driver */
+ fg_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * fg_init - Initialization of FG GPS.
+ */
+
+static int
+fg_init(
+ int fd
+ )
+{
+ if (write(fd, fginit, LENFG) != LENFG)
+ return 0;
+
+ return (1);
+
+}
+
+/*
+ * fg_start - open the device and initialize data for processing
+ */
+static int
+fg_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct fgunit *up;
+ int fd;
+ char device[20];
+
+
+ /*
+ * Open device file for reading.
+ */
+ (void)sprintf(device, DEVICE, unit);
+
+#ifdef DEBUG
+ if (debug)
+ printf ("starting FG with device %s\n",device);
+#endif
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+
+ if (!(up = (struct fgunit *)
+ emalloc(sizeof(struct fgunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct fgunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = fg_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ return (0);
+ }
+
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 3);
+ up->pollnum = 0;
+
+ /*
+ * Setup dating station to use GPS receiver.
+ * GPS receiver should work before this operation.
+ */
+ if(!fg_init(pp->io.fd))
+ refclock_report(peer, CEVNT_FAULT);
+
+ return (1);
+}
+
+
+/*
+ * fg_shutdown - shut down the clock
+ */
+static void
+fg_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct fgunit *up;
+
+ pp = peer->procptr;
+ up = (struct fgunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * fg_poll - called by the transmit procedure
+ */
+static void
+fg_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Time to poll the clock. The FG clock responds to a
+ * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
+ * above. If nothing is heard from the clock for two polls,
+ * declare a timeout and keep going.
+ */
+
+ if (write(pp->io.fd, fgdate, LENFG) != LENFG)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+
+ if (peer->burst > 0)
+ return;
+ /*
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ */
+ peer->burst = NSTAGE;
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+
+ return;
+
+}
+
+/*
+ * fg_receive - receive data from the serial interface
+ */
+static void
+fg_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct refclockproc *pp;
+ struct fgunit *up;
+ struct peer *peer;
+ char *bpt;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ * We can't use gtlin function because we need bynary data in buf */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct fgunit *)pp->unitptr;
+
+ /*
+ * Below hug to implement receiving of status information
+ */
+ if(!up->pollnum)
+ {
+ up->pollnum++;
+ return;
+ }
+
+
+ if (rbufp->recv_length < (LENFG-2))
+ {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return; /* The reply is invalid discard it. */
+ }
+
+ /* Below I trying to find a correct reply in buffer.
+ * Sometime GPS reply located in the beginnig of buffer,
+ * sometime you can find it with some offset.
+ */
+
+ bpt = (char *)rbufp->recv_space.X_recv_buffer;
+ while(*bpt != '')
+ bpt++;
+
+#define BP2(x) ( bpt[x] & 15 )
+#define BP1(x) (( bpt[x] & 240 ) >> 4)
+
+ pp->year = BP1(2)*10 + BP2(2);
+
+ if(pp->year == 94)
+ {
+ refclock_report(peer, CEVNT_BADREPLY);
+ if(!fg_init(pp->io.fd))
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ /* GPS is just powered up. The date is invalid -
+ discarding it. Initilize GPS one more time */
+ /* Sorry - this driver will broken in 2094 ;) */
+ }
+
+ if (pp->year < 99)
+ pp->year += 100;
+
+ pp->year += 1900;
+ pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
+
+/*
+ After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
+ benahour. It doubles day number for an hours in replys after 10:10:10 UTC
+ and doubles min every hour at HH:10:ss for a minute.
+ Hope it is a problem of my unit only and not a Y2K problem of FG GPS.
+ Below small code to avoid such situation.
+*/
+ if(up->y2kwarn > 10)
+ pp->hour = BP1(6)*10 + BP2(6);
+ else
+ pp->hour = BP1(5)*10 + BP2(5);
+
+ if((up->y2kwarn > 10) && (pp->hour == 10))
+ {
+ pp->minute = BP1(7)*10 + BP2(7);
+ pp->second = BP1(8)*10 + BP2(8);
+ pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
+ pp->nsec += BP1(10) * 1000;
+ } else {
+ pp->hour = BP1(5)*10 + BP2(5);
+ pp->minute = BP1(6)*10 + BP2(6);
+ pp->second = BP1(7)*10 + BP2(7);
+ pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
+ pp->nsec += BP1(9) * 1000;
+ }
+
+ if((pp->hour == 10) && (pp->minute == 10))
+ {
+ up->y2kwarn++;
+ }
+
+ sprintf(pp->a_lastcode, "%d %d %d %d %d", pp->year, pp->day, pp->hour, pp->minute, pp->second);
+ pp->lencode = strlen(pp->a_lastcode);
+ /*get_systime(&pp->lastrec);*/
+
+#ifdef DEBUG
+ if (debug)
+ printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
+ pp->year, pp->day, pp->hour, pp->minute, pp->second);
+#endif
+
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ pp->disp = (10e-6);
+ pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
+ /* pp->leap = LEAP_NOWARNING; */
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ return;
+}
+
+
+#else
+int refclock_fg_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_gpsvme.c b/ntpd/refclock_gpsvme.c
new file mode 100644
index 0000000..a7fcf5f
--- /dev/null
+++ b/ntpd/refclock_gpsvme.c
@@ -0,0 +1,622 @@
+/*
+ * refclock_gpsvme.c NTP clock driver for the TrueTime GPS-VME
+ * R. Schmidt, Time Service, US Naval Obs. res@tuttle.usno.navy.mil
+ *
+ * The refclock type has been defined as 16 (until new id assigned).
+ * These DEFS are included in the Makefile:
+ * DEFS= -DHAVE_TERMIOS -DSYS_HPUX=9
+ * DEFS_LOCAL= -DREFCLOCK
+ * CLOCKDEFS= -DGPSVME
+ * The file map_vme.c does the VME memory mapping, and includes vme_init().
+ * map_vme.c is HP-UX specific, because HPUX cannot mmap() device files! Boo!
+ * The file gps.h provides TrueTime register info.
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_GPSVME)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+
+#include "gps.h"
+#include "/etc/conf/h/io.h"
+
+/* GLOBAL STUFF BY RES */
+
+#include <time.h>
+
+#define PRIO 120 /* set the realtime priority */
+#define NREGS 7 /* number of registers we will use */
+
+extern int init_vme(); /* This is just a call to map_vme() */
+ /* It doesn't have to be extern */
+ /* the map_vme() call */
+extern unsigned short *greg[NREGS]; /* made extern to avoid being in both map_vme.c and this file */
+extern void *gps_base; /* mjb lmco 12/20/99 */
+
+extern caddr_t map_vme ();
+extern void unmap_vme(); /* Unmaps the VME space */
+
+struct vmedate { /* structure needed by ntp */
+ unsigned short year; /* *tptr is a pointer to this */
+ unsigned short doy;
+ unsigned short hr;
+ unsigned short mn;
+ unsigned short sec;
+ unsigned long frac;
+ unsigned short status;
+};
+
+struct vmedate *get_gpsvme_time();
+struct vmedate * time_vme; /* added to emulate LM refclock_gpsvme
+ (Made global per RES suggestion to fix mem leak DW lmco) mjb lmco 12/15/99 */
+
+/* END OF STUFF FROM RES */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 2 /* max number of VME units */
+#define BMAX 50 /* timecode buffer length */
+
+/*
+ * VME interface parameters.
+ */
+#define VMEPRECISION (-21) /* precision assumed (1 us) */
+#define USNOREFID "USNO\0" /* Or whatever? */
+#define VMEREFID "GPS" /* reference id */
+#define VMEDESCRIPTION "GPS" /* who we are */
+#define VMEHSREFID 0x7f7f1001 /* 127.127.16.01 refid hi strata */
+
+/* I'm using clock type 16 until one is assigned */
+/* This is set also in vme_control, below */
+
+
+#define GMT 0 /* hour offset from Greenwich */
+
+/*
+ * VME unit control structure.
+ */
+struct vmeunit {
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ struct vmedate vmedata; /* data returned from vme read */
+ l_fp lastrec; /* last local time */
+ l_fp lastref; /* last timecode time */
+ char lastcode[BMAX]; /* last timecode received */
+ u_short lencode; /* length of last timecode */
+ u_long lasttime; /* last time clock heard from */
+ u_short unit; /* unit number for this guy */
+ u_short status; /* clock status */
+ u_short lastevent; /* last clock event */
+ u_short year; /* year of eternity */
+ u_short day; /* day of year */
+ u_short hour; /* hour of day */
+ u_short minute; /* minute of hour */
+ u_short second; /* seconds of minute */
+ u_long usec; /* microsecond of second */
+ u_long yearstart; /* start of current year */
+ u_short leap; /* leap indicators */
+ /*
+ * Status tallies
+ */
+ u_long polls; /* polls sent */
+ u_long noreply; /* no replies to polls */
+ u_long coderecv; /* timecodes received */
+ u_long badformat; /* bad format */
+ u_long baddata; /* bad data */
+ u_long timestarted; /* time we started this */
+};
+
+/*
+ * Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back.
+ */
+static struct vmeunit *vmeunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp fudgefactor[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_char sloppyclockflag[MAXUNITS];
+
+/*
+ * Function prototypes
+ */
+static void vme_init (void);
+static int vme_start (int, struct peer *);
+static void vme_shutdown (int, struct peer *);
+static void vme_report_event (struct vmeunit *, int);
+static void vme_receive (struct recvbuf *);
+static void vme_poll (int unit, struct peer *);
+static void vme_control (int, struct refclockstat *, struct refclockstat *, struct peer *);
+static void vme_buginfo (int, struct refclockbug *, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_gpsvme = {
+ vme_start, vme_shutdown, vme_poll,
+ vme_control, vme_init, vme_buginfo, NOFLAGS
+};
+
+int fd_vme; /* file descriptor for ioctls */
+int regvalue;
+
+/*
+ * vme_init - initialize internal vme driver data
+ */
+static void
+vme_init(void)
+{
+ register int i;
+ /*
+ * Just zero the data arrays
+ */
+ /*
+ bzero((char *)vmeunits, sizeof vmeunits);
+ bzero((char *)unitinuse, sizeof unitinuse);
+ */
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor[i].l_ui = 0;
+ fudgefactor[i].l_uf = 0;
+ stratumtouse[i] = 0;
+ sloppyclockflag[i] = 0;
+ }
+}
+
+/*
+ * vme_start - open the VME device and initialize data for processing
+ */
+static int
+vme_start(
+ u_int unit,
+ struct peer *peer
+ )
+{
+ register struct vmeunit *vme;
+ register int i;
+ int dummy;
+ char vmedev[20];
+
+ /*
+ * Check configuration info.
+ */
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "vme_start: unit %d invalid", unit);
+ return (0);
+ }
+ if (unitinuse[unit]) {
+ msyslog(LOG_ERR, "vme_start: unit %d in use", unit);
+ return (0);
+ }
+
+ /*
+ * Open VME device
+ */
+#ifdef DEBUG
+
+ printf("Opening VME DEVICE \n");
+#endif
+ init_vme(); /* This is in the map_vme.c external file */
+
+ /*
+ * Allocate unit structure
+ */
+ if (vmeunits[unit] != 0) {
+ vme = vmeunits[unit]; /* The one we want is okay */
+ } else {
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && vmeunits[i] != 0)
+ break;
+ }
+ if (i < MAXUNITS) {
+ /*
+ * Reclaim this one
+ */
+ vme = vmeunits[i];
+ vmeunits[i] = 0;
+ } else {
+ vme = (struct vmeunit *)
+ emalloc(sizeof(struct vmeunit));
+ time_vme = (struct vmedate *)malloc(sizeof(struct vmedate)); /* Added to emulate LM's refclock_gpsvme
+ (added to fix mem lead DW lmco) mjb lmco 12/22/99 */
+ }
+ }
+ bzero((char *)vme, sizeof(struct vmeunit));
+ vmeunits[unit] = vme;
+
+ /*
+ * Set up the structures
+ */
+ vme->peer = peer;
+ vme->unit = (u_short)unit;
+ vme->timestarted = current_time;
+
+ vme->io.clock_recv = vme_receive;
+ vme->io.srcclock = (caddr_t)vme;
+ vme->io.datalen = 0;
+ vme->io.fd = fd_vme;
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = VMEPRECISION;
+ peer->stratum = stratumtouse[unit];
+ memcpy( (char *)&peer->refid, USNOREFID,4);
+
+ /* peer->refid = htonl(VMEHSREFID); */
+
+ unitinuse[unit] = 1;
+ return (1);
+}
+
+
+/*
+ * vme_shutdown - shut down a VME clock
+ */
+static void
+vme_shutdown(
+ int unit
+ )
+{
+ register struct vmeunit *vme;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit);
+ return;
+ }
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ unmap_vme();
+ vme = vmeunits[unit];
+ io_closeclock(&vme->io);
+ unitinuse[unit] = 0;
+}
+
+/*
+ * vme_report_event - note the occurance of an event
+ *
+ * This routine presently just remembers the report and logs it, but
+ * does nothing heroic for the trap handler.
+ */
+static void
+vme_report_event(
+ struct vmeunit *vme,
+ int code
+ )
+{
+ struct peer *peer;
+
+ peer = vme->peer;
+ if (vme->status != (u_short)code) {
+ vme->status = (u_short)code;
+ if (code != CEVNT_NOMINAL)
+ vme->lastevent = (u_short)code;
+ msyslog(LOG_INFO,
+ "clock %s event %x", ntoa(&peer->srcadr), code);
+ }
+}
+
+
+/*
+ * vme_receive - receive data from the VME device.
+ *
+ * Note: This interface would be interrupt-driven. We don't use that
+ * now, but include a dummy routine for possible future adventures.
+ */
+static void
+vme_receive(
+ struct recvbuf *rbufp
+ )
+{
+}
+
+/*
+ * vme_poll - called by the transmit procedure
+ */
+static void
+vme_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct vmedate *tptr;
+ struct vmeunit *vme;
+ l_fp tstmp;
+ time_t tloc;
+ struct tm *tadr;
+ long ltemp;
+
+
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit);
+ return;
+ }
+ vme = vmeunits[unit]; /* Here is the structure */
+ vme->polls++;
+
+ tptr = &vme->vmedata;
+
+ if ((tptr = get_gpsvme_time()) == NULL ) {
+ vme_report_event(vme, CEVNT_BADREPLY);
+ return;
+ }
+
+ get_systime(&vme->lastrec);
+ vme->lasttime = current_time;
+
+ /*
+ * Get VME time and convert to timestamp format.
+ * The year must come from the system clock.
+ */
+ /*
+ time(&tloc);
+ tadr = gmtime(&tloc);
+ tptr->year = (unsigned short)(tadr->tm_year + 1900);
+ */
+
+ sprintf(vme->lastcode,
+ "%3.3d %2.2d:%2.2d:%2.2d.%.6d %1d\0",
+ tptr->doy, tptr->hr, tptr->mn,
+ tptr->sec, tptr->frac, tptr->status);
+
+ record_clock_stats(&(vme->peer->srcadr), vme->lastcode);
+ vme->lencode = (u_short) strlen(vme->lastcode);
+
+ vme->day = tptr->doy;
+ vme->hour = tptr->hr;
+ vme->minute = tptr->mn;
+ vme->second = tptr->sec;
+ vme->nsec = tptr->frac * 1000;
+
+#ifdef DEBUG
+ if (debug)
+ printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
+ vme->day, vme->hour, vme->minute, vme->second,
+ vme->nsec, tptr->status);
+#endif
+ if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
+ vme_report_event(vme, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Now, compute the reference time value. Use the heavy
+ * machinery for the seconds and the millisecond field for the
+ * fraction when present. If an error in conversion to internal
+ * format is found, the program declares bad data and exits.
+ * Note that this code does not yet know how to do the years and
+ * relies on the clock-calendar chip for sanity.
+ */
+ if (!clocktime(vme->day, vme->hour, vme->minute,
+ vme->second, GMT, vme->lastrec.l_ui,
+ &vme->yearstart, &vme->lastref.l_ui)) {
+ vme->baddata++;
+ vme_report_event(vme, CEVNT_BADTIME);
+ msyslog(LOG_ERR, "refclock_gpsvme: bad data!!");
+ return;
+ }
+ vme->lastref.l_uf = 0;
+ DTOLFP(vme->nsec / 1e9, &ltemp);
+ L_ADD(&vme->lastrec, &ltemp);
+ tstmp = vme->lastref;
+
+ L_SUB(&tstmp, &vme->lastrec);
+ vme->coderecv++;
+
+ L_ADD(&tstmp, &(fudgefactor[vme->unit]));
+ vme->lastref = vme->lastrec;
+ refclock_receive(vme->peer);
+}
+
+/*
+ * vme_control - set fudge factors, return statistics2
+ */
+static void
+vme_control(
+ u_int unit,
+ struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer * peer
+ )
+{
+ register struct vmeunit *vme;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit);
+ return;
+ }
+
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ DTOLFP(in->fudgetime1, &fudgefactor[unit]); /* added mjb lmco 12/20/99 */
+
+ if (in->haveflags & CLK_HAVEVAL1) {
+ stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ /*
+ * Should actually reselect clock, but
+ * will wait for the next timecode
+ */
+ vme = vmeunits[unit];
+ peer = vme->peer;
+ peer->stratum = stratumtouse[unit];
+ if (stratumtouse[unit] <= 1)
+ memcpy( (char *)&peer->refid, USNOREFID,4);
+ else
+ peer->refid = htonl(VMEHSREFID);
+ }
+ }
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ sloppyclockflag[unit] = in->flags & CLK_FLAG1;
+ }
+ }
+
+ if (out != 0) {
+ out->type = 16; /*set by RES SHOULD BE CHANGED */
+ out->haveflags
+ = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1;
+ out->clockdesc = VMEDESCRIPTION;
+ LFPTOD(&fudgefactor[unit], out->fudgetime1); /* added mjb lmco 12/20/99 */
+
+ out ->fudgetime2 = 0; /* should do what above was supposed to do mjb lmco 12/20/99 */
+
+ out->fudgeval1 = (long)stratumtouse[unit]; /*changed from above LONG was not
+ defined mjb lmco 12/15/99 */
+
+ out->fudgeval2 = 0;
+ out->flags = sloppyclockflag[unit];
+ if (unitinuse[unit]) {
+ vme = vmeunits[unit];
+ out->lencode = vme->lencode;
+ out->p_lastcode = vme->lastcode;
+ out->timereset = current_time - vme->timestarted;
+ out->polls = vme->polls;
+ out->noresponse = vme->noreply;
+ out->badformat = vme->badformat;
+ out->baddata = vme->baddata;
+ out->lastevent = vme->lastevent;
+ out->currentstatus = vme->status;
+ } else {
+ out->lencode = 0;
+ out->p_lastcode = "";
+ out->polls = out->noresponse = 0;
+ out->badformat = out->baddata = 0;
+ out->timereset = 0;
+ out->currentstatus = out->lastevent = CEVNT_NOMINAL;
+ }
+ }
+}
+
+/*
+ * vme_buginfo - return clock dependent debugging info
+ */
+static void
+vme_buginfo(
+ int unit,
+ register struct refclockbug *bug,
+ struct peer * peer
+ )
+{
+ register struct vmeunit *vme;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ vme = vmeunits[unit];
+
+ bug->nvalues = 11;
+ bug->ntimes = 5;
+ if (vme->lasttime != 0)
+ bug->values[0] = current_time - vme->lasttime;
+ else
+ bug->values[0] = 0;
+ bug->values[2] = (u_long)vme->year;
+ bug->values[3] = (u_long)vme->day;
+ bug->values[4] = (u_long)vme->hour;
+ bug->values[5] = (u_long)vme->minute;
+ bug->values[6] = (u_long)vme->second;
+ bug->values[7] = (u_long)vme->nsec;
+ bug->values[9] = vme->yearstart;
+ bug->stimes = 0x1c;
+ bug->times[0] = vme->lastref;
+ bug->times[1] = vme->lastrec;
+}
+/* -------------------------------------------------------*/
+/* get_gpsvme_time() */
+/* R. Schmidt, USNO, 1995 */
+/* It's ugly, but hey, it works and its free */
+
+#include "gps.h" /* defines for TrueTime GPS-VME */
+
+#define PBIAS 193 /* 193 microsecs to read the GPS experimentally found */
+
+struct vmedate *
+get_gpsvme_time(void)
+{
+ extern struct vmedate *time_vme;
+ unsigned short set, hr, min, sec, ums, hms, status;
+ int ret;
+ char ti[3];
+
+ long tloc ;
+ time_t mktime(),time();
+ struct tm *gmtime(), *gmt;
+ char *gpsmicro;
+ gpsmicro = (char *) malloc(7);
+
+ *greg = (unsigned short *)malloc(sizeof(short) * NREGS);
+
+
+ /* reference the freeze command address general register 1 */
+ set = *greg[0];
+ /* read the registers : */
+ /* get year */
+ time_vme->year = (unsigned short) *greg[6];
+ /* Get doy */
+ time_vme->doy = (unsigned short) (*greg[5] & MASKDAY);
+ /* Get hour */
+ time_vme->hr = (unsigned short) ((*greg[4] & MASKHI) >>8);
+ /* Get minutes */
+ time_vme->mn = (unsigned short) (*greg[4] & MASKLO);
+ /* Get seconds */
+ time_vme->sec = (unsigned short) (*greg[3] & MASKHI) >>8;
+ /* get microseconds in 2 parts and put together */
+ ums = *greg[2];
+ hms = *greg[3] & MASKLO;
+
+ time_vme->status = (unsigned short) *greg[5] >>13;
+
+ /* reference the unfreeze command address general register 1 */
+ set = *greg[1];
+
+ sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums);
+ time_vme->frac = (u_long) gpsmicro;
+
+ /* unmap_vme(); */
+
+ if (!status) {
+ return (NULL); /* fixed mjb lmco 12/20/99 */
+ }
+ else
+ return (time_vme);
+}
+
+#else
+int refclock_gpsvme_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_heath.c b/ntpd/refclock_heath.c
new file mode 100644
index 0000000..c16cef3
--- /dev/null
+++ b/ntpd/refclock_heath.c
@@ -0,0 +1,421 @@
+/*
+ * refclock_heath - clock driver for Heath GC-1000 and and GC-1000 II
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HEATH)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* not HAVE_SYS_IOCTL_H */
+
+/*
+ * This driver supports the Heath GC-1000 Most Accurate Clock, with
+ * RS232C Output Accessory. This is a WWV/WWVH receiver somewhat less
+ * robust than other supported receivers. Its claimed accuracy is 100 ms
+ * when actually synchronized to the broadcast signal, but this doesn't
+ * happen even most of the time, due to propagation conditions, ambient
+ * noise sources, etc. When not synchronized, the accuracy is at the
+ * whim of the internal clock oscillator, which can wander into the
+ * sunset without warning. Since the indicated precision is 100 ms,
+ * expect a host synchronized only to this thing to wander to and fro,
+ * occasionally being rudely stepped when the offset exceeds the default
+ * clock_max of 128 ms.
+ *
+ * There are two GC-1000 versions supported by this driver. The original
+ * GC-1000 with RS-232 output first appeared in 1983, but dissapeared
+ * from the market a few years later. The GC-1000 II with RS-232 output
+ * first appeared circa 1990, but apparently is no longer manufactured.
+ * The two models differ considerably, both in interface and commands.
+ * The GC-1000 has a pseudo-bipolar timecode output triggered by a RTS
+ * transition. The timecode includes both the day of year and time of
+ * day. The GC-1000 II has a true bipolar output and a complement of
+ * single character commands. The timecode includes only the time of
+ * day.
+ *
+ * GC-1000
+ *
+ * The internal DIPswitches should be set to operate in MANUAL mode. The
+ * external DIPswitches should be set to GMT and 24-hour format.
+ *
+ * In MANUAL mode the clock responds to a rising edge of the request to
+ * send (RTS) modem control line by sending the timecode. Therefore, it
+ * is necessary that the operating system implement the TIOCMBIC and
+ * TIOCMBIS ioctl system calls and TIOCM_RTS control bit. Present
+ * restrictions require the use of a POSIX-compatible programming
+ * interface, although other interfaces may work as well.
+ *
+ * A simple hardware modification to the clock can be made which
+ * prevents the clock hearing the request to send (RTS) if the HI SPEC
+ * lamp is out. Route the HISPEC signal to the tone decoder board pin
+ * 19, from the display, pin 19. Isolate pin 19 of the decoder board
+ * first, but maintain connection with pin 10. Also isolate pin 38 of
+ * the CPU on the tone board, and use half an added 7400 to gate the
+ * original signal to pin 38 with that from pin 19.
+ *
+ * The clock message consists of 23 ASCII printing characters in the
+ * following format:
+ *
+ * hh:mm:ss.f AM dd/mm/yr<cr>
+ *
+ * hh:mm:ss.f = hours, minutes, seconds
+ * f = deciseconds ('?' when out of spec)
+ * AM/PM/bb = blank in 24-hour mode
+ * dd/mm/yr = day, month, year
+ *
+ * The alarm condition is indicated by '?', rather than a digit, at f.
+ * Note that 0?:??:??.? is displayed before synchronization is first
+ * established and hh:mm:ss.? once synchronization is established and
+ * then lost again for about a day.
+ *
+ * GC-1000 II
+ *
+ * Commands consist of a single letter and are case sensitive. When
+ * enterred in lower case, a description of the action performed is
+ * displayed. When enterred in upper case the action is performed.
+ * Following is a summary of descriptions as displayed by the clock:
+ *
+ * The clock responds with a command The 'A' command returns an ASCII
+ * local time string: HH:MM:SS.T xx<CR>, where
+ *
+ * HH = hours
+ * MM = minutes
+ * SS = seconds
+ * T = tenths-of-seconds
+ * xx = 'AM', 'PM', or ' '
+ * <CR> = carriage return
+ *
+ * The 'D' command returns 24 pairs of bytes containing the variable
+ * divisor value at the end of each of the previous 24 hours. This
+ * allows the timebase trimming process to be observed. UTC hour 00 is
+ * always returned first. The first byte of each pair is the high byte
+ * of (variable divisor * 16); the second byte is the low byte of
+ * (variable divisor * 16). For example, the byte pair 3C 10 would be
+ * returned for a divisor of 03C1 hex (961 decimal).
+ *
+ * The 'I' command returns: | TH | TL | ER | DH | DL | U1 | I1 | I2 | ,
+ * where
+ *
+ * TH = minutes since timebase last trimmed (high byte)
+ * TL = minutes since timebase last trimmed (low byte)
+ * ER = last accumulated error in 1.25 ms increments
+ * DH = high byte of (current variable divisor * 16)
+ * DL = low byte of (current variable divisor * 16)
+ * U1 = UT1 offset (/.1 s): | + | 4 | 2 | 1 | 0 | 0 | 0 | 0 |
+ * I1 = information byte 1: | W | C | D | I | U | T | Z | 1 | ,
+ * where
+ *
+ * W = set by WWV(H)
+ * C = CAPTURE LED on
+ * D = TRIM DN LED on
+ * I = HI SPEC LED on
+ * U = TRIM UP LED on
+ * T = DST switch on
+ * Z = UTC switch on
+ * 1 = UT1 switch on
+ *
+ * I2 = information byte 2: | 8 | 8 | 4 | 2 | 1 | D | d | S | ,
+ * where
+ *
+ * 8, 8, 4, 2, 1 = TIME ZONE switch settings
+ * D = DST bit (#55) in last-received frame
+ * d = DST bit (#2) in last-received frame
+ * S = clock is in simulation mode
+ *
+ * The 'P' command returns 24 bytes containing the number of frames
+ * received without error during UTC hours 00 through 23, providing an
+ * indication of hourly propagation. These bytes are updated each hour
+ * to reflect the previous 24 hour period. UTC hour 00 is always
+ * returned first.
+ *
+ * The 'T' command returns the UTC time: | HH | MM | SS | T0 | , where
+ * HH = tens-of-hours and hours (packed BCD)
+ * MM = tens-of-minutes and minutes (packed BCD)
+ * SS = tens-of-seconds and seconds (packed BCD)
+ * T = tenths-of-seconds (BCD)
+ *
+ * Fudge Factors
+ *
+ * A fudge time1 value of .04 s appears to center the clock offset
+ * residuals. The fudge time2 parameter is the local time offset east of
+ * Greenwich, which depends on DST. Sorry about that, but the clock
+ * gives no hint on what the DIPswitches say.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/heath%d" /* device name and unit */
+#define PRECISION (-4) /* precision assumed (about 100 ms) */
+#define REFID "WWV\0" /* reference ID */
+#define DESCRIPTION "Heath GC-1000 Most Accurate Clock" /* WRU */
+
+#define LENHEATH1 23 /* min timecode length */
+#define LENHEATH2 13 /* min timecode length */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Baud rate table. The GC-1000 supports 1200, 2400 and 4800; the
+ * GC-1000 II supports only 9600.
+ */
+static int speed[] = {B1200, B2400, B4800, B9600};
+
+/*
+ * Function prototypes
+ */
+static int heath_start P((int, struct peer *));
+static void heath_shutdown P((int, struct peer *));
+static void heath_receive P((struct recvbuf *));
+static void heath_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_heath = {
+ heath_start, /* start up driver */
+ heath_shutdown, /* shut down driver */
+ heath_poll, /* transmit poll message */
+ noentry, /* not used (old heath_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old heath_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * heath_start - open the devices and initialize data for processing
+ */
+static int
+heath_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, speed[peer->ttl & 0x3], 0)))
+ return (0);
+ pp = peer->procptr;
+ pp->io.clock_recv = heath_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * heath_shutdown - shut down the clock
+ */
+static void
+heath_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ io_closeclock(&pp->io);
+}
+
+
+/*
+ * heath_receive - receive data from the serial interface
+ */
+static void
+heath_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int month, day;
+ int i;
+ char dsec, a[5];
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &trtmp);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ switch (pp->lencode) {
+
+ /*
+ * GC-1000 timecode format: "hh:mm:ss.f AM mm/dd/yy"
+ * GC-1000 II timecode format: "hh:mm:ss.f "
+ */
+ case LENHEATH1:
+ if (sscanf(pp->a_lastcode,
+ "%2d:%2d:%2d.%c%5c%2d/%2d/%2d", &pp->hour,
+ &pp->minute, &pp->second, &dsec, a, &month, &day,
+ &pp->year) != 8) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+
+ /*
+ * GC-1000 II timecode format: "hh:mm:ss.f "
+ */
+ case LENHEATH2:
+ if (sscanf(pp->a_lastcode, "%2d:%2d:%2d.%c", &pp->hour,
+ &pp->minute, &pp->second, &dsec) != 4) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+
+ default:
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * We determine the day of the year from the DIPswitches. This
+ * should be fixed, since somebody might forget to set them.
+ * Someday this hazard will be fixed by a fiendish scheme that
+ * looks at the timecode and year the radio shows, then computes
+ * the residue of the seconds mod the seconds in a leap cycle.
+ * If in the third year of that cycle and the third and later
+ * months of that year, add one to the day. Then, correct the
+ * timecode accordingly. Icky pooh. This bit of nonsense could
+ * be avoided if the engineers had been required to write a
+ * device driver before finalizing the timecode format.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+ /*
+ * Determine synchronization and last update
+ */
+ if (!isdigit((int)dsec))
+ pp->leap = LEAP_NOTINSYNC;
+ else {
+ pp->nsec = (dsec - '0') * 100000000;
+ pp->leap = LEAP_NOWARNING;
+ }
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+}
+
+
+/*
+ * heath_poll - called by the transmit procedure
+ */
+static void
+heath_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ int bits = TIOCM_RTS;
+
+ /*
+ * At each poll we check for timeout and toggle the RTS modem
+ * control line, then take a timestamp. Presumably, this is the
+ * event the radio captures to generate the timecode.
+ * Apparently, the radio takes about a second to make up its
+ * mind to send a timecode, so the receive timestamp is
+ * worthless.
+ */
+ pp = peer->procptr;
+
+ /*
+ * We toggle the RTS modem control lead (GC-1000) and sent a T
+ * (GC-1000 II) to kick a timecode loose from the radio. This
+ * code works only for POSIX and SYSV interfaces. With bsd you
+ * are on your own. We take a timestamp between the up and down
+ * edges to lengthen the pulse, which should be about 50 usec on
+ * a Sun IPC. With hotshot CPUs, the pulse might get too short.
+ * Later.
+ */
+ if (ioctl(pp->io.fd, TIOCMBIC, (char *)&bits) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+ get_systime(&pp->lastrec);
+ if (write(pp->io.fd, "T", 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ ioctl(pp->io.fd, TIOCMBIS, (char *)&bits);
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("heath: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+ peer->burst = MAXSTAGE;
+ pp->polls++;
+}
+
+#else
+int refclock_heath_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_hopfpci.c b/ntpd/refclock_hopfpci.c
new file mode 100644
index 0000000..1b02319
--- /dev/null
+++ b/ntpd/refclock_hopfpci.c
@@ -0,0 +1,273 @@
+/*
+ * refclock_hopfpci.c
+ *
+ * - clock driver for hopf 6039 PCI board (GPS or DCF77)
+ * Bernd Altmeier altmeier@atlsoft.de
+ *
+ * latest source and further information can be found at:
+ * http://www.ATLSoft.de/ntp
+ *
+ * In order to run this driver you have to install and test
+ * the PCI-board driver for your system first.
+ *
+ * On Linux/UNIX
+ *
+ * The driver attempts to open the device /dev/hopf6039 .
+ * The device entry will be made by the installation process of
+ * the kernel module for the PCI-bus board. The driver sources
+ * belongs to the delivery equipment of the PCI-board.
+ *
+ * On Windows NT/2000
+ *
+ * The driver attempts to open the device by calling the function
+ * "OpenHopfDevice()". This function will be installed by the
+ * Device Driver for the PCI-bus board. The driver belongs to the
+ * delivery equipment of the PCI-board.
+ *
+ *
+ * Start 21.03.2000 Revision: 01.20
+ * changes 22.12.2000 Revision: 01.40 flag1 = 1 sync even if Quarz
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HOPF_PCI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#undef fileno
+#include <ctype.h>
+#undef fileno
+
+#ifndef SYS_WINNT
+# include <sys/ipc.h>
+# include <sys/ioctl.h>
+# include <assert.h>
+# include <unistd.h>
+# include <stdio.h>
+# include "hopf6039.h"
+#else
+# include "hopf_PCI_io.h"
+#endif
+
+/*
+ * hopfpci interface definitions
+ */
+#define PRECISION (-10) /* precision assumed (1 ms) */
+#define REFID "hopf" /* reference ID */
+#define DESCRIPTION "hopf Elektronik PCI radio board"
+
+#define NSAMPLES 3 /* stages of median filter */
+#ifndef SYS_WINNT
+# define DEVICE "/dev/hopf6039" /* device name inode*/
+#else
+# define DEVICE "hopf6039" /* device name WinNT */
+#endif
+
+#define LEWAPWAR 0x20 /* leap second warning bit */
+
+#define HOPF_OPMODE 0xC0 /* operation mode mask */
+#define HOPF_INVALID 0x00 /* no time code available */
+#define HOPF_INTERNAL 0x40 /* internal clock */
+#define HOPF_RADIO 0x80 /* radio clock */
+#define HOPF_RADIOHP 0xC0 /* high precision radio clock */
+
+
+/*
+ * hopfclock unit control structure.
+ */
+struct hopfclock_unit {
+ short unit; /* NTP refclock unit number */
+ char leap_status; /* leap second flag */
+};
+int fd; /* file descr. */
+
+/*
+ * Function prototypes
+ */
+static int hopfpci_start (int, struct peer *);
+static void hopfpci_shutdown (int, struct peer *);
+static void hopfpci_poll (int unit, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_hopfpci = {
+ hopfpci_start, /* start up driver */
+ hopfpci_shutdown, /* shut down driver */
+ hopfpci_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * hopfpci_start - attach to hopf PCI board 6039
+ */
+static int
+hopfpci_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct hopfclock_unit *up;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct hopfclock_unit *) emalloc(sizeof(struct hopfclock_unit));
+
+ if (!(up)) {
+ msyslog(LOG_ERR, "hopfPCIClock(%d) emalloc: %m",unit);
+#ifdef DEBUG
+ printf("hopfPCIClock(%d) emalloc\n",unit);
+#endif
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct hopfclock_unit));
+
+#ifndef SYS_WINNT
+
+ fd = open(DEVICE,O_RDWR); /* try to open hopf clock device */
+
+#else
+ if (!OpenHopfDevice()){
+ msyslog(LOG_ERR,"Start: %s unit: %d failed!",DEVICE,unit);
+ return (0);
+ }
+#endif
+
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = INVALID_SOCKET;
+ pp->unitptr = (caddr_t)up;
+
+ get_systime(&pp->lastrec);
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ if (pp->unitptr!=0) {
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ up->leap_status = 0;
+ up->unit = (short) unit;
+ return (1);
+ }
+ else {
+ return 0;
+ }
+}
+
+
+/*
+ * hopfpci_shutdown - shut down the clock
+ */
+static void
+hopfpci_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ register struct hopfpciTime *up;
+
+ pp = peer->procptr;
+ up = (struct hopfpciTime *)pp->unitptr;
+
+#ifndef SYS_WINNT
+ close(fd);
+#else
+ CloseHopfDevice();
+/* UnmapViewOfFile (up); */
+#endif
+}
+
+
+/*
+ * hopfpci_poll - called by the transmit procedure
+ */
+static void
+hopfpci_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ register struct hopfpciTime *up;
+ HOPFTIME m_time;
+
+ pp = peer->procptr;
+ up = (struct hopfpciTime *)pp->unitptr;
+
+#ifndef SYS_WINNT
+ ioctl(fd,HOPF_CLOCK_GET_UTC,&m_time);
+#else
+ GetHopfSystemTime(&m_time);
+#endif
+ pp->polls++;
+
+ pp->day = ymd2yd(m_time.wYear,m_time.wMonth,m_time.wDay);
+ pp->hour = m_time.wHour;
+ pp->minute = m_time.wMinute;
+ pp->second = m_time.wSecond;
+ pp->nsec = m_time.wMilliseconds * 1000000;
+ if (m_time.wStatus & LEWAPWAR)
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ sprintf(pp->a_lastcode,"ST: %02X T: %02d:%02d:%02d.%03ld D: %02d.%02d.%04d",
+ m_time.wStatus, pp->hour, pp->minute, pp->second,
+ pp->nsec / 1000000, m_time.wDay, m_time.wMonth, m_time.wYear);
+ pp->lencode = (u_short)strlen(pp->a_lastcode);
+
+ get_systime(&pp->lastrec);
+
+ /*
+ * If clock has no valid status then report error and exit
+ */
+ if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INVALID) { /* time ok? */
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+
+ /*
+ * Test if time is running on internal quarz
+ * if CLK_FLAG1 is set, sychronize even if no radio operation
+ */
+
+ if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INTERNAL){
+ if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ }
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ return;
+}
+
+#else
+int refclock_hopfpci_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_hopfser.c b/ntpd/refclock_hopfser.c
new file mode 100644
index 0000000..1d27333
--- /dev/null
+++ b/ntpd/refclock_hopfser.c
@@ -0,0 +1,378 @@
+/*
+ *
+ * refclock_hopfser.c
+ * - clock driver for hopf serial boards (GPS or DCF77)
+ *
+ * Date: 30.03.2000 Revision: 01.10
+ *
+ * latest source and further information can be found at:
+ * http://www.ATLSoft.de/ntp
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(SYS_WINNT)
+#undef close
+#define close closesocket
+#endif
+
+#if defined(REFCLOCK) && (defined(CLOCK_HOPF_SERIAL))
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined HAVE_SYS_MODEM_H
+# include <sys/modem.h>
+# define TIOCMSET MCSETA
+# define TIOCMGET MCGETA
+# define TIOCM_RTS MRTS
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# ifdef TERMIOS_NEEDS__SVID3
+# define _SVID3
+# endif
+# include <termios.h>
+# ifdef TERMIOS_NEEDS__SVID3
+# undef _SVID3
+# endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+/*
+ * clock definitions
+ */
+#define DESCRIPTION "hopf Elektronik serial clock" /* Long name */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "hopf\0" /* reference ID */
+/*
+ * I/O definitions
+ */
+#define DEVICE "/dev/hopfclock%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+
+
+#define STX 0x02
+#define ETX 0x03
+#define CR 0x0c
+#define LF 0x0a
+
+/* parse states */
+#define REC_QUEUE_EMPTY 0
+#define REC_QUEUE_FULL 1
+
+#define HOPF_OPMODE 0x0C /* operation mode mask */
+#define HOPF_INVALID 0x00 /* no time code available */
+#define HOPF_INTERNAL 0x04 /* internal clock */
+#define HOPF_RADIO 0x08 /* radio clock */
+#define HOPF_RADIOHP 0x0C /* high precision radio clock */
+
+/*
+ * hopfclock unit control structure.
+ */
+struct hopfclock_unit {
+ l_fp laststamp; /* last receive timestamp */
+ short unit; /* NTP refclock unit number */
+ u_long polled; /* flag to detect noreplies */
+ char leap_status; /* leap second flag */
+ int rpt_next;
+};
+
+/*
+ * Function prototypes
+ */
+
+static int hopfserial_start P((int, struct peer *));
+static void hopfserial_shutdown P((int, struct peer *));
+static void hopfserial_receive P((struct recvbuf *));
+static void hopfserial_poll P((int, struct peer *));
+/* static void hopfserial_io P((struct recvbuf *)); */
+/*
+ * Transfer vector
+ */
+struct refclock refclock_hopfser = {
+ hopfserial_start, /* start up driver */
+ hopfserial_shutdown, /* shut down driver */
+ hopfserial_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * hopfserial_start - open the devices and initialize data for processing
+ */
+static int
+hopfserial_start (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hopfclock_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+#ifdef SYS_WINNT
+ (void) sprintf(gpsdev, "COM%d:", unit);
+#else
+ (void) sprintf(gpsdev, DEVICE, unit);
+#endif
+ /* LDISC_STD, LDISC_RAW
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ fd = refclock_open(gpsdev, SPEED232, LDISC_CLK);
+ if (fd <= 0) {
+#ifdef DEBUG
+ printf("hopfSerialClock(%d) start: open %s failed\n", unit, gpsdev);
+#endif
+ return 0;
+ }
+
+ msyslog(LOG_NOTICE, "hopfSerialClock(%d) fd: %d dev: %s", unit, fd,
+ gpsdev);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct hopfclock_unit *) emalloc(sizeof(struct hopfclock_unit));
+
+ if (!(up)) {
+ msyslog(LOG_ERR, "hopfSerialClock(%d) emalloc: %m",unit);
+#ifdef DEBUG
+ printf("hopfSerialClock(%d) emalloc\n",unit);
+#endif
+ (void) close(fd);
+ return (0);
+ }
+
+ memset((char *)up, 0, sizeof(struct hopfclock_unit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = hopfserial_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+#ifdef DEBUG
+ printf("hopfSerialClock(%d) io_addclock\n",unit);
+#endif
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ pp->clockdesc = DESCRIPTION;
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ up->leap_status = 0;
+ up->unit = (short) unit;
+
+ return (1);
+}
+
+
+/*
+ * hopfserial_shutdown - shut down the clock
+ */
+static void
+hopfserial_shutdown (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hopfclock_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct hopfclock_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+
+/*
+ * hopfserial_receive - receive data from the serial interface
+ */
+
+static void
+hopfserial_receive (
+ struct recvbuf *rbufp
+ )
+{
+ struct hopfclock_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ int synch; /* synchhronization indicator */
+ int DoW; /* Dow */
+
+ int day, month; /* ddd conversion */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct hopfclock_unit *)pp->unitptr;
+
+ if (up->rpt_next == 0 )
+ return;
+
+
+ up->rpt_next = 0; /* wait until next poll interval occur */
+
+ pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
+
+ if (pp->lencode == 0)
+ return;
+
+ sscanf(pp->a_lastcode,
+#if 1
+ "%1x%1x%2d%2d%2d%2d%2d%2d", /* ...cr,lf */
+#else
+ "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */
+#endif
+ &synch,
+ &DoW,
+ &pp->hour,
+ &pp->minute,
+ &pp->second,
+ &day,
+ &month,
+ &pp->year);
+
+
+ /*
+ Validate received values at least enough to prevent internal
+ array-bounds problems, etc.
+ */
+ if((pp->hour < 0) || (pp->hour > 23) ||
+ (pp->minute < 0) || (pp->minute > 59) ||
+ (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
+ (day < 1) || (day > 31) ||
+ (month < 1) || (month > 12) ||
+ (pp->year < 0) || (pp->year > 99)) {
+ /* Data out of range. */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ /*
+ some preparations
+ */
+ pp->day = ymd2yd(pp->year,month,day);
+ pp->leap=0;
+
+ /* Year-2000 check! */
+ /* wrap 2-digit date into 4-digit */
+
+ if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* < 98 */
+ pp->year += 1900;
+
+ /* preparation for timecode ntpq rl command ! */
+
+#if 0
+ wsprintf(pp->a_lastcode,
+ "STATUS: %1X%1X, DATE: %02d.%02d.%04d TIME: %02d:%02d:%02d",
+ synch,
+ DoW,
+ day,
+ month,
+ pp->year,
+ pp->hour,
+ pp->minute,
+ pp->second);
+
+ pp->lencode = strlen(pp->a_lastcode);
+ if ((synch && 0xc) == 0 ){ /* time ok? */
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+#endif
+ /*
+ * If clock has no valid status then report error and exit
+ */
+ if ((synch & HOPF_OPMODE) == HOPF_INVALID ){ /* time ok? */
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+
+ /*
+ * Test if time is running on internal quarz
+ * if CLK_FLAG1 is set, sychronize even if no radio operation
+ */
+
+ if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){
+ if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ }
+
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+#if 0
+ msyslog(LOG_ERR, " D:%x D:%d D:%d",synch,pp->minute,pp->second);
+#endif
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ return;
+}
+
+
+/*
+ * hopfserial_poll - called by the transmit procedure
+ *
+ */
+static void
+hopfserial_poll (
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hopfclock_unit *up;
+ struct refclockproc *pp;
+ pp = peer->procptr;
+
+ up = (struct hopfclock_unit *)pp->unitptr;
+
+ pp->polls++;
+ up->rpt_next = 1;
+
+#if 0
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#endif
+
+ return;
+}
+
+#else
+int refclock_hopfser_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_hpgps.c b/ntpd/refclock_hpgps.c
new file mode 100644
index 0000000..7c801bb
--- /dev/null
+++ b/ntpd/refclock_hpgps.c
@@ -0,0 +1,604 @@
+/*
+ * refclock_hpgps - clock driver for HP 58503A GPS receiver
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HPGPS)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* Version 0.1 April 1, 1995
+ * 0.2 April 25, 1995
+ * tolerant of missing timecode response prompt and sends
+ * clear status if prompt indicates error;
+ * can use either local time or UTC from receiver;
+ * can get receiver status screen via flag4
+ *
+ * WARNING!: This driver is UNDER CONSTRUCTION
+ * Everything in here should be treated with suspicion.
+ * If it looks wrong, it probably is.
+ *
+ * Comments and/or questions to: Dave Vitanye
+ * Hewlett Packard Company
+ * dave@scd.hp.com
+ * (408) 553-2856
+ *
+ * Thanks to the author of the PST driver, which was the starting point for
+ * this one.
+ *
+ * This driver supports the HP 58503A Time and Frequency Reference Receiver.
+ * This receiver uses HP SmartClock (TM) to implement an Enhanced GPS receiver.
+ * The receiver accuracy when locked to GPS in normal operation is better
+ * than 1 usec. The accuracy when operating in holdover is typically better
+ * than 10 usec. per day.
+ *
+ * The receiver should be operated with factory default settings.
+ * Initial driver operation: expects the receiver to be already locked
+ * to GPS, configured and able to output timecode format 2 messages.
+ *
+ * The driver uses the poll sequence :PTIME:TCODE? to get a response from
+ * the receiver. The receiver responds with a timecode string of ASCII
+ * printing characters, followed by a <cr><lf>, followed by a prompt string
+ * issued by the receiver, in the following format:
+ * T#yyyymmddhhmmssMFLRVcc<cr><lf>scpi >
+ *
+ * The driver processes the response at the <cr> and <lf>, so what the
+ * driver sees is the prompt from the previous poll, followed by this
+ * timecode. The prompt from the current poll is (usually) left unread until
+ * the next poll. So (except on the very first poll) the driver sees this:
+ *
+ * scpi > T#yyyymmddhhmmssMFLRVcc<cr><lf>
+ *
+ * The T is the on-time character, at 980 msec. before the next 1PPS edge.
+ * The # is the timecode format type. We look for format 2.
+ * Without any of the CLK or PPS stuff, then, the receiver buffer timestamp
+ * at the <cr> is 24 characters later, which is about 25 msec. at 9600 bps,
+ * so the first approximation for fudge time1 is nominally -0.955 seconds.
+ * This number probably needs adjusting for each machine / OS type, so far:
+ * -0.955000 on an HP 9000 Model 712/80 HP-UX 9.05
+ * -0.953175 on an HP 9000 Model 370 HP-UX 9.10
+ *
+ * This receiver also provides a 1PPS signal, but I haven't figured out
+ * how to deal with any of the CLK or PPS stuff yet. Stay tuned.
+ *
+ */
+
+/*
+ * Fudge Factors
+ *
+ * Fudge time1 is used to accomodate the timecode serial interface adjustment.
+ * Fudge flag4 can be set to request a receiver status screen summary, which
+ * is recorded in the clockstats file.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/hpgps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "HP 58503A GPS Time and Frequency Reference Receiver"
+
+#define SMAX 23*80+1 /* for :SYSTEM:PRINT? status screen response */
+
+#define MTZONE 2 /* number of fields in timezone reply */
+#define MTCODET2 12 /* number of fields in timecode format T2 */
+#define NTCODET2 21 /* number of chars to checksum in format T2 */
+
+/*
+ * Tables to compute the day of year from yyyymmdd timecode.
+ * Viva la leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct hpgpsunit {
+ int pollcnt; /* poll message counter */
+ int tzhour; /* timezone offset, hours */
+ int tzminute; /* timezone offset, minutes */
+ int linecnt; /* set for expected multiple line responses */
+ char *lastptr; /* pointer to receiver response data */
+ char statscrn[SMAX]; /* receiver status screen buffer */
+};
+
+/*
+ * Function prototypes
+ */
+static int hpgps_start P((int, struct peer *));
+static void hpgps_shutdown P((int, struct peer *));
+static void hpgps_receive P((struct recvbuf *));
+static void hpgps_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_hpgps = {
+ hpgps_start, /* start up driver */
+ hpgps_shutdown, /* shut down driver */
+ hpgps_poll, /* transmit poll message */
+ noentry, /* not used (old hpgps_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old hpgps_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * hpgps_start - open the devices and initialize data for processing
+ */
+static int
+hpgps_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct hpgpsunit *)
+ emalloc(sizeof(struct hpgpsunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct hpgpsunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = hpgps_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->tzhour = 0;
+ up->tzminute = 0;
+
+ *up->statscrn = '\0';
+ up->lastptr = up->statscrn;
+ up->pollcnt = 2;
+
+ /*
+ * Get the identifier string, which is logged but otherwise ignored,
+ * and get the local timezone information
+ */
+ up->linecnt = 1;
+ if (write(pp->io.fd, "*IDN?\r:PTIME:TZONE?\r", 20) != 20)
+ refclock_report(peer, CEVNT_FAULT);
+
+ return (1);
+}
+
+
+/*
+ * hpgps_shutdown - shut down the clock
+ */
+static void
+hpgps_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct hpgpsunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * hpgps_receive - receive data from the serial interface
+ */
+static void
+hpgps_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ char tcodechar1; /* identifies timecode format */
+ char tcodechar2; /* identifies timecode format */
+ char timequal; /* time figure of merit: 0-9 */
+ char freqqual; /* frequency figure of merit: 0-3 */
+ char leapchar; /* leapsecond: + or 0 or - */
+ char servchar; /* request for service: 0 = no, 1 = yes */
+ char syncchar; /* time info is invalid: 0 = no, 1 = yes */
+ short expectedsm; /* expected timecode byte checksum */
+ short tcodechksm; /* computed timecode byte checksum */
+ int i,m,n;
+ int month, day, lastday;
+ char *tcp; /* timecode pointer (skips over the prompt) */
+ char prompt[BMAX]; /* prompt in response from receiver */
+
+ /*
+ * Initialize pointers and read the receiver response
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct hpgpsunit *)pp->unitptr;
+ *pp->a_lastcode = '\0';
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: lencode: %d timecode:%s\n",
+ pp->lencode, pp->a_lastcode);
+#endif
+
+ /*
+ * If there's no characters in the reply, we can quit now
+ */
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * If linecnt is greater than zero, we are getting information only,
+ * such as the receiver identification string or the receiver status
+ * screen, so put the receiver response at the end of the status
+ * screen buffer. When we have the last line, write the buffer to
+ * the clockstats file and return without further processing.
+ *
+ * If linecnt is zero, we are expecting either the timezone
+ * or a timecode. At this point, also write the response
+ * to the clockstats file, and go on to process the prompt (if any),
+ * timezone, or timecode and timestamp.
+ */
+
+
+ if (up->linecnt-- > 0) {
+ if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) {
+ *up->lastptr++ = '\n';
+ (void)strcpy(up->lastptr, pp->a_lastcode);
+ up->lastptr += pp->lencode;
+ }
+ if (up->linecnt == 0)
+ record_clock_stats(&peer->srcadr, up->statscrn);
+
+ return;
+ }
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ pp->lastrec = trtmp;
+
+ up->lastptr = up->statscrn;
+ *up->lastptr = '\0';
+ up->pollcnt = 2;
+
+ /*
+ * We get down to business: get a prompt if one is there, issue
+ * a clear status command if it contains an error indication.
+ * Next, check for either the timezone reply or the timecode reply
+ * and decode it. If we don't recognize the reply, or don't get the
+ * proper number of decoded fields, or get an out of range timezone,
+ * or if the timecode checksum is bad, then we declare bad format
+ * and exit.
+ *
+ * Timezone format (including nominal prompt):
+ * scpi > -H,-M<cr><lf>
+ *
+ * Timecode format (including nominal prompt):
+ * scpi > T2yyyymmddhhmmssMFLRVcc<cr><lf>
+ *
+ */
+
+ (void)strcpy(prompt,pp->a_lastcode);
+ tcp = strrchr(pp->a_lastcode,'>');
+ if (tcp == NULL)
+ tcp = pp->a_lastcode;
+ else
+ tcp++;
+ prompt[tcp - pp->a_lastcode] = '\0';
+ while ((*tcp == ' ') || (*tcp == '\t')) tcp++;
+
+ /*
+ * deal with an error indication in the prompt here
+ */
+ if (strrchr(prompt,'E') > strrchr(prompt,'s')){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: error indicated in prompt: %s\n", prompt);
+#endif
+ if (write(pp->io.fd, "*CLS\r\r", 6) != 6)
+ refclock_report(peer, CEVNT_FAULT);
+ }
+
+ /*
+ * make sure we got a timezone or timecode format and
+ * then process accordingly
+ */
+ m = sscanf(tcp,"%c%c", &tcodechar1, &tcodechar2);
+
+ if (m != 2){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: no format indicator\n");
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ switch (tcodechar1) {
+
+ case '+':
+ case '-':
+ m = sscanf(tcp,"%d,%d", &up->tzhour, &up->tzminute);
+ if (m != MTZONE) {
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: only %d fields recognized in timezone\n", m);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ if ((up->tzhour < -12) || (up->tzhour > 13) ||
+ (up->tzminute < -59) || (up->tzminute > 59)){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: timezone %d, %d out of range\n",
+ up->tzhour, up->tzminute);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ return;
+
+ case 'T':
+ break;
+
+ default:
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: unrecognized reply format %c%c\n",
+ tcodechar1, tcodechar2);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ } /* end of tcodechar1 switch */
+
+
+ switch (tcodechar2) {
+
+ case '2':
+ m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx",
+ &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second,
+ &timequal, &freqqual, &leapchar, &servchar, &syncchar,
+ &expectedsm);
+ n = NTCODET2;
+
+ if (m != MTCODET2){
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: only %d fields recognized in timecode\n", m);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+
+ default:
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: unrecognized timecode format %c%c\n",
+ tcodechar1, tcodechar2);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ } /* end of tcodechar2 format switch */
+
+ /*
+ * Compute and verify the checksum.
+ * Characters are summed starting at tcodechar1, ending at just
+ * before the expected checksum. Bail out if incorrect.
+ */
+ tcodechksm = 0;
+ while (n-- > 0) tcodechksm += *tcp++;
+ tcodechksm &= 0x00ff;
+
+ if (tcodechksm != expectedsm) {
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: checksum %2hX doesn't match %2hX expected\n",
+ tcodechksm, expectedsm);
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Compute the day of year from the yyyymmdd format.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ if ( ! isleap_4(pp->year) ) { /* Y2KFixes */
+ /* not a leap year */
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++) day += day1tab[i];
+ lastday = 365;
+ } else {
+ /* a leap year */
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++) day += day2tab[i];
+ lastday = 366;
+ }
+
+ /*
+ * Deal with the timezone offset here. The receiver timecode is in
+ * local time = UTC + :PTIME:TZONE, so SUBTRACT the timezone values.
+ * For example, Pacific Standard Time is -8 hours , 0 minutes.
+ * Deal with the underflows and overflows.
+ */
+ pp->minute -= up->tzminute;
+ pp->hour -= up->tzhour;
+
+ if (pp->minute < 0) {
+ pp->minute += 60;
+ pp->hour--;
+ }
+ if (pp->minute > 59) {
+ pp->minute -= 60;
+ pp->hour++;
+ }
+ if (pp->hour < 0) {
+ pp->hour += 24;
+ day--;
+ if (day < 1) {
+ pp->year--;
+ if ( isleap_4(pp->year) ) /* Y2KFixes */
+ day = 366;
+ else
+ day = 365;
+ }
+ }
+
+ if (pp->hour > 23) {
+ pp->hour -= 24;
+ day++;
+ if (day > lastday) {
+ pp->year++;
+ day = 1;
+ }
+ }
+
+ pp->day = day;
+
+ /*
+ * Decode the MFLRV indicators.
+ * NEED TO FIGURE OUT how to deal with the request for service,
+ * time quality, and frequency quality indicators some day.
+ */
+ if (syncchar != '0') {
+ pp->leap = LEAP_NOTINSYNC;
+ }
+ else {
+ switch (leapchar) {
+
+ case '+':
+ pp->leap = LEAP_ADDSECOND;
+ break;
+
+ case '0':
+ pp->leap = LEAP_NOWARNING;
+ break;
+
+ case '-':
+ pp->leap = LEAP_DELSECOND;
+ break;
+
+ default:
+#ifdef DEBUG
+ if (debug)
+ printf("hpgps: unrecognized leap indicator: %c\n",
+ leapchar);
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } /* end of leapchar switch */
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /*
+ * If CLK_FLAG4 is set, ask for the status screen response.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4){
+ up->linecnt = 22;
+ if (write(pp->io.fd, ":SYSTEM:PRINT?\r", 15) != 15)
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+
+/*
+ * hpgps_poll - called by the transmit procedure
+ */
+static void
+hpgps_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct hpgpsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The HP 58503A responds to a
+ * ":PTIME:TCODE?" by returning a timecode in the format specified
+ * above. If nothing is heard from the clock for two polls,
+ * declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = (struct hpgpsunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ if (write(pp->io.fd, ":PTIME:TCODE?\r", 14) != 14) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+ else
+ pp->polls++;
+}
+
+#else
+int refclock_hpgps_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_irig.c b/ntpd/refclock_irig.c
new file mode 100644
index 0000000..0b35368
--- /dev/null
+++ b/ntpd/refclock_irig.c
@@ -0,0 +1,1049 @@
+/*
+ * refclock_irig - audio IRIG-B/E demodulator/decoder
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_IRIG)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#include "audio.h"
+
+/*
+ * Audio IRIG-B/E demodulator/decoder
+ *
+ * This driver receives, demodulates and decodes IRIG-B/E signals when
+ * connected to the audio codec /dev/audio. The IRIG signal format is an
+ * amplitude-modulated carrier with pulse-width modulated data bits. For
+ * IRIG-B, the carrier frequency is 1000 Hz and bit rate 100 b/s; for
+ * IRIG-E, the carrier frequenchy is 100 Hz and bit rate 10 b/s. The
+ * driver automatically recognizes which format is in use.
+ *
+ * The program processes 8000-Hz mu-law companded samples using separate
+ * signal filters for IRIG-B and IRIG-E, a comb filter, envelope
+ * detector and automatic threshold corrector. Cycle crossings relative
+ * to the corrected slice level determine the width of each pulse and
+ * its value - zero, one or position identifier. The data encode 20 BCD
+ * digits which determine the second, minute, hour and day of the year
+ * and sometimes the year and synchronization condition. The comb filter
+ * exponentially averages the corresponding samples of successive baud
+ * intervals in order to reliably identify the reference carrier cycle.
+ * A type-II phase-lock loop (PLL) performs additional integration and
+ * interpolation to accurately determine the zero crossing of that
+ * cycle, which determines the reference timestamp. A pulse-width
+ * discriminator demodulates the data pulses, which are then encoded as
+ * the BCD digits of the timecode.
+ *
+ * The timecode and reference timestamp are updated once each second
+ * with IRIG-B (ten seconds with IRIG-E) and local clock offset samples
+ * saved for later processing. At poll intervals of 64 s, the saved
+ * samples are processed by a trimmed-mean filter and used to update the
+ * system clock.
+ *
+ * An automatic gain control feature provides protection against
+ * overdriven or underdriven input signal amplitudes. It is designed to
+ * maintain adequate demodulator signal amplitude while avoiding
+ * occasional noise spikes. In order to assure reliable capture, the
+ * decompanded input signal amplitude must be greater than 100 units and
+ * the codec sample frequency error less than 250 PPM (.025 percent).
+ *
+ * The program performs a number of error checks to protect against
+ * overdriven or underdriven input signal levels, incorrect signal
+ * format or improper hardware configuration. Specifically, if any of
+ * the following errors occur for a time measurement, the data are
+ * rejected.
+ *
+ * o The peak carrier amplitude is less than DRPOUT (100). This usually
+ * means dead IRIG signal source, broken cable or wrong input port.
+ *
+ * o The frequency error is greater than MAXFREQ +-250 PPM (.025%). This
+ * usually means broken codec hardware or wrong codec configuration.
+ *
+ * o The modulation index is less than MODMIN (0.5). This usually means
+ * overdriven IRIG signal or wrong IRIG format.
+ *
+ * o A frame synchronization error has occurred. This usually means
+ * wrong IRIG signal format or the IRIG signal source has lost
+ * synchronization (signature control).
+ *
+ * o A data decoding error has occurred. This usually means wrong IRIG
+ * signal format.
+ *
+ * o The current second of the day is not exactly one greater than the
+ * previous one. This usually means a very noisy IRIG signal or
+ * insufficient CPU resources.
+ *
+ * o An audio codec error (overrun) occurred. This usually means
+ * insufficient CPU resources, as sometimes happens with Sun SPARC
+ * IPCs when doing something useful.
+ *
+ * Note that additional checks are done elsewhere in the reference clock
+ * interface routines.
+ *
+ * Debugging aids
+ *
+ * The timecode format used for debugging and data recording includes
+ * data helpful in diagnosing problems with the IRIG signal and codec
+ * connections. With debugging enabled (-d on the ntpd command line),
+ * the driver produces one line for each timecode in the following
+ * format:
+ *
+ * 00 1 98 23 19:26:52 721 143 0.694 20 0.1 66.5 3094572411.00027
+ *
+ * The most recent line is also written to the clockstats file at 64-s
+ * intervals.
+ *
+ * The first field contains the error flags in hex, where the hex bits
+ * are interpreted as below. This is followed by the IRIG status
+ * indicator, year of century, day of year and time of day. The status
+ * indicator and year are not produced by some IRIG devices. Following
+ * these fields are the signal amplitude (0-8100), codec gain (0-255),
+ * modulation index (0-1), time constant (2-20), carrier phase error
+ * (us) and carrier frequency error (PPM). The last field is the on-time
+ * timestamp in NTP format.
+ *
+ * The fraction part of the on-time timestamp is a good indicator of how
+ * well the driver is doing. With an UltrSPARC 30 and Solaris 2.7, this
+ * thing can keep the clock within a few tens of microseconds relative
+ * to the IRIG-B signal. Accuracy with IRIG-E is about ten times worse.
+ * Unfortunately, Sun broke the 2.7 audio driver in 2.8, which has a
+ * 10-ms sawtooth modulation. The driver attempts to remove the
+ * modulation by some clever estimation techniques which mostly work.
+ * Your experience may vary.
+ *
+ * Unlike other drivers, which can have multiple instantiations, this
+ * one supports only one. It does not seem likely that more than one
+ * audio codec would be useful in a single machine. More than one would
+ * probably chew up too much CPU time anyway.
+ *
+ * Fudge factors
+ *
+ * Fudge flag4 causes the dubugging output described above to be
+ * recorded in the clockstats file. When the audio driver is compiled,
+ * fudge flag2 selects the audio input port, where 0 is the mike port
+ * (default) and 1 is the line-in port. It does not seem useful to
+ * select the compact disc player port. Fudge flag3 enables audio
+ * monitoring of the input signal. For this purpose, the monitor gain is
+ * set to a default value. Fudgetime2 is used as a frequency vernier for
+ * broken codec sample frequency.
+ */
+/*
+ * Interface definitions
+ */
+#define DEVICE_AUDIO "/dev/audio" /* audio device name */
+#define PRECISION (-17) /* precision assumed (about 10 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "Generic IRIG Audio Driver" /* WRU */
+#define AUDIO_BUFSIZ 320 /* audio buffer size (40 ms) */
+#define SECOND 8000 /* nominal sample rate (Hz) */
+#define BAUD 80 /* samples per baud interval */
+#define OFFSET 128 /* companded sample offset */
+#define SIZE 256 /* decompanding table size */
+#define CYCLE 8 /* samples per carrier cycle */
+#define SUBFLD 10 /* bits per subfield */
+#define FIELD 10 /* subfields per field */
+#define MINTC 2 /* min PLL time constant */
+#define MAXTC 20 /* max PLL time constant max */
+#define MAXSIG 6000. /* maximum signal level */
+#define MAXCLP 100 /* max clips above reference per s */
+#define DRPOUT 100. /* dropout signal level */
+#define MODMIN 0.5 /* minimum modulation index */
+#define MAXFREQ (250e-6 * SECOND) /* freq tolerance (.025%) */
+#define PI 3.1415926535 /* the real thing */
+#ifdef IRIG_SUCKS
+#define WIGGLE 11 /* wiggle filter length */
+#endif /* IRIG_SUCKS */
+
+/*
+ * Experimentally determined filter delays
+ */
+#define IRIG_B .0019 /* IRIG-B filter delay */
+#define IRIG_E .0019 /* IRIG-E filter delay */
+
+/*
+ * Data bit definitions
+ */
+#define BIT0 0 /* zero */
+#define BIT1 1 /* one */
+#define BITP 2 /* position identifier */
+
+/*
+ * Error flags (up->errflg)
+ */
+#define IRIG_ERR_AMP 0x01 /* low carrier amplitude */
+#define IRIG_ERR_FREQ 0x02 /* frequency tolerance exceeded */
+#define IRIG_ERR_MOD 0x04 /* low modulation index */
+#define IRIG_ERR_SYNCH 0x08 /* frame synch error */
+#define IRIG_ERR_DECODE 0x10 /* frame decoding error */
+#define IRIG_ERR_CHECK 0x20 /* second numbering discrepancy */
+#define IRIG_ERR_ERROR 0x40 /* codec error (overrun) */
+#define IRIG_ERR_SIGERR 0x80 /* IRIG status error (Spectracom) */
+
+/*
+ * IRIG unit control structure
+ */
+struct irigunit {
+ u_char timecode[21]; /* timecode string */
+ l_fp timestamp; /* audio sample timestamp */
+ l_fp tick; /* audio sample increment */
+ double integ[BAUD]; /* baud integrator */
+ double phase, freq; /* logical clock phase and frequency */
+ double zxing; /* phase detector integrator */
+ double yxing; /* cycle phase */
+ double exing; /* envelope phase */
+ double modndx; /* modulation index */
+ double irig_b; /* IRIG-B signal amplitude */
+ double irig_e; /* IRIG-E signal amplitude */
+ int errflg; /* error flags */
+ /*
+ * Audio codec variables
+ */
+ double comp[SIZE]; /* decompanding table */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int mongain; /* codec monitor gain */
+ int clipcnt; /* sample clipped count */
+ int seccnt; /* second interval counter */
+
+ /*
+ * RF variables
+ */
+ double hpf[5]; /* IRIG-B filter shift register */
+ double lpf[5]; /* IRIG-E filter shift register */
+ double intmin, intmax; /* integrated envelope min and max */
+ double envmax; /* peak amplitude */
+ double envmin; /* noise amplitude */
+ double maxsignal; /* integrated peak amplitude */
+ double noise; /* integrated noise amplitude */
+ double lastenv[CYCLE]; /* last cycle amplitudes */
+ double lastint[CYCLE]; /* last integrated cycle amplitudes */
+ double lastsig; /* last carrier sample */
+ double fdelay; /* filter delay */
+ int decim; /* sample decimation factor */
+ int envphase; /* envelope phase */
+ int envptr; /* envelope phase pointer */
+ int carphase; /* carrier phase */
+ int envsw; /* envelope state */
+ int envxing; /* envelope slice crossing */
+ int tc; /* time constant */
+ int tcount; /* time constant counter */
+ int badcnt; /* decimation interval counter */
+
+ /*
+ * Decoder variables
+ */
+ int pulse; /* cycle counter */
+ int cycles; /* carrier cycles */
+ int dcycles; /* data cycles */
+ int xptr; /* translate table pointer */
+ int lastbit; /* last code element length */
+ int second; /* previous second */
+ int fieldcnt; /* subfield count in field */
+ int bits; /* demodulated bits */
+ int bitcnt; /* bit count in subfield */
+#ifdef IRIG_SUCKS
+ l_fp wigwag; /* wiggle accumulator */
+ int wp; /* wiggle filter pointer */
+ l_fp wiggle[WIGGLE]; /* wiggle filter */
+ l_fp wigbot[WIGGLE]; /* wiggle bottom fisher*/
+#endif /* IRIG_SUCKS */
+ l_fp wuggle;
+};
+
+/*
+ * Function prototypes
+ */
+static int irig_start P((int, struct peer *));
+static void irig_shutdown P((int, struct peer *));
+static void irig_receive P((struct recvbuf *));
+static void irig_poll P((int, struct peer *));
+
+/*
+ * More function prototypes
+ */
+static void irig_base P((struct peer *, double));
+static void irig_rf P((struct peer *, double));
+static void irig_decode P((struct peer *, int));
+static void irig_gain P((struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_irig = {
+ irig_start, /* start up driver */
+ irig_shutdown, /* shut down driver */
+ irig_poll, /* transmit poll message */
+ noentry, /* not used (old irig_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old irig_buginfo) */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Global variables
+ */
+static char hexchar[] = { /* really quick decoding table */
+ '0', '8', '4', 'c', /* 0000 0001 0010 0011 */
+ '2', 'a', '6', 'e', /* 0100 0101 0110 0111 */
+ '1', '9', '5', 'd', /* 1000 1001 1010 1011 */
+ '3', 'b', '7', 'f' /* 1100 1101 1110 1111 */
+};
+
+
+/*
+ * irig_start - open the devices and initialize data for processing
+ */
+static int
+irig_start(
+ int unit, /* instance number (used for PCM) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ int fd; /* file descriptor */
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device
+ */
+ fd = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
+ if (fd < 0)
+ return (0);
+#ifdef DEBUG
+ if (debug)
+ audio_show();
+#endif
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct irigunit *)
+ emalloc(sizeof(struct irigunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct irigunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = irig_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void)close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->tc = MINTC;
+ up->decim = 1;
+ up->fdelay = IRIG_B;
+ up->gain = 127;
+
+ /*
+ * The companded samples are encoded sign-magnitude. The table
+ * contains all the 256 values in the interest of speed.
+ */
+ up->comp[0] = up->comp[OFFSET] = 0.;
+ up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
+ up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
+ step = 2.;
+ for (i = 3; i < OFFSET; i++) {
+ up->comp[i] = up->comp[i - 1] + step;
+ up->comp[OFFSET + i] = -up->comp[i];
+ if (i % 16 == 0)
+ step *= 2.;
+ }
+ DTOLFP(1. / SECOND, &up->tick);
+ return (1);
+}
+
+
+/*
+ * irig_shutdown - shut down the clock
+ */
+static void
+irig_shutdown(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * irig_receive - receive data from the audio device
+ *
+ * This routine reads input samples and adjusts the logical clock to
+ * track the irig clock by dropping or duplicating codec samples.
+ */
+static void
+irig_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ int bufcnt; /* buffer counter */
+ l_fp ltemp; /* l_fp temp */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * Main loop - read until there ain't no more. Note codec
+ * samples are bit-inverted.
+ */
+ DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
+ L_SUB(&rbufp->recv_time, &ltemp);
+ up->timestamp = rbufp->recv_time;
+ dpt = rbufp->recv_buffer;
+ for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
+ sample = up->comp[~*dpt++ & 0xff];
+
+ /*
+ * Clip noise spikes greater than MAXSIG. If no clips,
+ * increase the gain a tad; if the clips are too high,
+ * decrease a tad.
+ */
+ if (sample > MAXSIG) {
+ sample = MAXSIG;
+ up->clipcnt++;
+ } else if (sample < -MAXSIG) {
+ sample = -MAXSIG;
+ up->clipcnt++;
+ }
+
+ /*
+ * Variable frequency oscillator. The codec oscillator
+ * runs at the nominal rate of 8000 samples per second,
+ * or 125 us per sample. A frequency change of one unit
+ * results in either duplicating or deleting one sample
+ * per second, which results in a frequency change of
+ * 125 PPM.
+ */
+ up->phase += up->freq / SECOND;
+ up->phase += pp->fudgetime2 / 1e6;
+ if (up->phase >= .5) {
+ up->phase -= 1.;
+ } else if (up->phase < -.5) {
+ up->phase += 1.;
+ irig_rf(peer, sample);
+ irig_rf(peer, sample);
+ } else {
+ irig_rf(peer, sample);
+ }
+ L_ADD(&up->timestamp, &up->tick);
+
+ /*
+ * Once each second, determine the IRIG format and gain.
+ */
+ up->seccnt = (up->seccnt + 1) % SECOND;
+ if (up->seccnt == 0) {
+ if (up->irig_b > up->irig_e) {
+ up->decim = 1;
+ up->fdelay = IRIG_B;
+ } else {
+ up->decim = 10;
+ up->fdelay = IRIG_E;
+ }
+ irig_gain(peer);
+ up->irig_b = up->irig_e = 0;
+ }
+ }
+
+ /*
+ * Set the input port and monitor gain for the next buffer.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = 2;
+ else
+ up->port = 1;
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ up->mongain = MONGAIN;
+ else
+ up->mongain = 0;
+}
+
+/*
+ * irig_rf - RF processing
+ *
+ * This routine filters the RF signal using a highpass filter for IRIG-B
+ * and a lowpass filter for IRIG-E. In case of IRIG-E, the samples are
+ * decimated by a factor of ten. The lowpass filter functions also as a
+ * decimation filter in this case. Note that the codec filters function
+ * as roofing filters to attenuate both the high and low ends of the
+ * passband. IIR filter coefficients were determined using Matlab Signal
+ * Processing Toolkit.
+ */
+static void
+irig_rf(
+ struct peer *peer, /* peer structure pointer */
+ double sample /* current signal sample */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ double irig_b, irig_e; /* irig filter outputs */
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * IRIG-B filter. 4th-order elliptic, 800-Hz highpass, 0.3 dB
+ * passband ripple, -50 dB stopband ripple, phase delay .0022
+ * s)
+ */
+ irig_b = (up->hpf[4] = up->hpf[3]) * 2.322484e-01;
+ irig_b += (up->hpf[3] = up->hpf[2]) * -1.103929e+00;
+ irig_b += (up->hpf[2] = up->hpf[1]) * 2.351081e+00;
+ irig_b += (up->hpf[1] = up->hpf[0]) * -2.335036e+00;
+ up->hpf[0] = sample - irig_b;
+ irig_b = up->hpf[0] * 4.335855e-01
+ + up->hpf[1] * -1.695859e+00
+ + up->hpf[2] * 2.525004e+00
+ + up->hpf[3] * -1.695859e+00
+ + up->hpf[4] * 4.335855e-01;
+ up->irig_b += irig_b * irig_b;
+
+ /*
+ * IRIG-E filter. 4th-order elliptic, 130-Hz lowpass, 0.3 dB
+ * passband ripple, -50 dB stopband ripple, phase delay .0219 s.
+ */
+ irig_e = (up->lpf[4] = up->lpf[3]) * 8.694604e-01;
+ irig_e += (up->lpf[3] = up->lpf[2]) * -3.589893e+00;
+ irig_e += (up->lpf[2] = up->lpf[1]) * 5.570154e+00;
+ irig_e += (up->lpf[1] = up->lpf[0]) * -3.849667e+00;
+ up->lpf[0] = sample - irig_e;
+ irig_e = up->lpf[0] * 3.215696e-03
+ + up->lpf[1] * -1.174951e-02
+ + up->lpf[2] * 1.712074e-02
+ + up->lpf[3] * -1.174951e-02
+ + up->lpf[4] * 3.215696e-03;
+ up->irig_e += irig_e * irig_e;
+
+ /*
+ * Decimate by a factor of either 1 (IRIG-B) or 10 (IRIG-E).
+ */
+ up->badcnt = (up->badcnt + 1) % up->decim;
+ if (up->badcnt == 0) {
+ if (up->decim == 1)
+ irig_base(peer, irig_b);
+ else
+ irig_base(peer, irig_e);
+ }
+}
+
+/*
+ * irig_base - baseband processing
+ *
+ * This routine processes the baseband signal and demodulates the AM
+ * carrier using a synchronous detector. It then synchronizes to the
+ * data frame at the baud rate and decodes the data pulses.
+ */
+static void
+irig_base(
+ struct peer *peer, /* peer structure pointer */
+ double sample /* current signal sample */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ /*
+ * Local variables
+ */
+ double xxing; /* phase detector interpolated output */
+ double lope; /* integrator output */
+ double env; /* envelope detector output */
+ double dtemp; /* double temp */
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * Synchronous baud integrator. Corresponding samples of current
+ * and past baud intervals are integrated to refine the envelope
+ * amplitude and phase estimate. We keep one cycle of both the
+ * raw and integrated data for later use.
+ */
+ up->envphase = (up->envphase + 1) % BAUD;
+ up->carphase = (up->carphase + 1) % CYCLE;
+ up->integ[up->envphase] += (sample - up->integ[up->envphase]) /
+ (5 * up->tc);
+ lope = up->integ[up->envphase];
+ up->lastenv[up->carphase] = sample;
+ up->lastint[up->carphase] = lope;
+
+ /*
+ * Phase detector. Sample amplitudes are integrated over the
+ * baud interval. Cycle phase is determined from these
+ * amplitudes using an eight-sample cyclic buffer. A phase
+ * change of 360 degrees produces an output change of one unit.
+ */
+ if (up->lastsig > 0 && lope <= 0) {
+ xxing = lope / (up->lastsig - lope);
+ up->zxing += (up->carphase - 4 + xxing) / CYCLE;
+ }
+ up->lastsig = lope;
+
+ /*
+ * Update signal/noise estimates and PLL phase/frequency.
+ */
+ if (up->envphase == 0) {
+
+ /*
+ * Update envelope signal and noise estimates and mess
+ * with error bits.
+ */
+ up->maxsignal = up->intmax;
+ up->noise = up->intmin;
+ if (up->maxsignal < DRPOUT)
+ up->errflg |= IRIG_ERR_AMP;
+ if (up->maxsignal > 0)
+ up->modndx = (up->intmax - up->intmin) /
+ up->intmax;
+ else
+ up->modndx = 0;
+ if (up->modndx < MODMIN)
+ up->errflg |= IRIG_ERR_MOD;
+ up->intmin = 1e6; up->intmax = 0;
+ if (up->errflg & (IRIG_ERR_AMP | IRIG_ERR_FREQ |
+ IRIG_ERR_MOD | IRIG_ERR_SYNCH)) {
+ up->tc = MINTC;
+ up->tcount = 0;
+ }
+
+ /*
+ * Update PLL phase and frequency. The PLL time constant
+ * is set initially to stabilize the frequency within a
+ * minute or two, then increases to the maximum. The
+ * frequency is clamped so that the PLL capture range
+ * cannot be exceeded.
+ */
+ dtemp = up->zxing * up->decim / BAUD;
+ up->yxing = dtemp;
+ up->zxing = 0.;
+ up->phase += dtemp / up->tc;
+ up->freq += dtemp / (4. * up->tc * up->tc);
+ if (up->freq > MAXFREQ) {
+ up->freq = MAXFREQ;
+ up->errflg |= IRIG_ERR_FREQ;
+ } else if (up->freq < -MAXFREQ) {
+ up->freq = -MAXFREQ;
+ up->errflg |= IRIG_ERR_FREQ;
+ }
+ }
+
+ /*
+ * Synchronous demodulator. There are eight samples in the cycle
+ * and ten cycles in the baud interval. The amplitude of each
+ * cycle is determined at the last sample in the cycle. The
+ * beginning of the data pulse is determined from the integrated
+ * samples, while the end of the pulse is determined from the
+ * raw samples. The raw data bits are demodulated relative to
+ * the slice level and left-shifted in the decoding register.
+ */
+ if (up->carphase != 7)
+ return;
+ env = (up->lastenv[2] - up->lastenv[6]) / 2.;
+ lope = (up->lastint[2] - up->lastint[6]) / 2.;
+ if (lope > up->intmax)
+ up->intmax = lope;
+ if (lope < up->intmin)
+ up->intmin = lope;
+
+ /*
+ * Pulse code demodulator and reference timestamp. The decoder
+ * looks for a sequence of ten bits; the first two bits must be
+ * one, the last two bits must be zero. Frame synch is asserted
+ * when three correct frames have been found.
+ */
+ up->pulse = (up->pulse + 1) % 10;
+ if (up->pulse == 1)
+ up->envmax = env;
+ else if (up->pulse == 9)
+ up->envmin = env;
+ up->dcycles <<= 1;
+ if (env >= (up->envmax + up->envmin) / 2.)
+ up->dcycles |= 1;
+ up->cycles <<= 1;
+ if (lope >= (up->maxsignal + up->noise) / 2.)
+ up->cycles |= 1;
+ if ((up->cycles & 0x303c0f03) == 0x300c0300) {
+ l_fp ltemp;
+ int bitz;
+
+ /*
+ * The PLL time constant starts out small, in order to
+ * sustain a frequency tolerance of 250 PPM. It
+ * gradually increases as the loop settles down. Note
+ * that small wiggles are not believed, unless they
+ * persist for lots of samples.
+ */
+ if (up->pulse != 9)
+ up->errflg |= IRIG_ERR_SYNCH;
+ up->pulse = 9;
+ up->exing = -up->yxing;
+ if (fabs(up->envxing - up->envphase) <= 1) {
+ up->tcount++;
+ if (up->tcount > 50 * up->tc) {
+ up->tc++;
+ if (up->tc > MAXTC)
+ up->tc = MAXTC;
+ up->tcount = 0;
+ up->envxing = up->envphase;
+ } else {
+ up->exing -= up->envxing - up->envphase;
+ }
+ } else {
+ up->tcount = 0;
+ up->envxing = up->envphase;
+ }
+
+ /*
+ * Determine a reference timestamp, accounting for the
+ * codec delay and filter delay. Note the timestamp is
+ * for the previous frame, so we have to backtrack for
+ * this plus the delay since the last carrier positive
+ * zero crossing.
+ */
+ dtemp = up->decim * ((up->exing + BAUD) / SECOND + 1.) +
+ up->fdelay;
+ DTOLFP(dtemp, &ltemp);
+ pp->lastrec = up->timestamp;
+ L_SUB(&pp->lastrec, &ltemp);
+
+ /*
+ * The data bits are collected in ten-bit frames. The
+ * first two and last two bits are determined by frame
+ * sync and ignored here; the resulting patterns
+ * represent zero (0-1 bits), one (2-4 bits) and
+ * position identifier (5-6 bits). The remaining
+ * patterns represent errors and are treated as zeros.
+ */
+ bitz = up->dcycles & 0xfc;
+ switch(bitz) {
+
+ case 0x00:
+ case 0x80:
+ irig_decode(peer, BIT0);
+ break;
+
+ case 0xc0:
+ case 0xe0:
+ case 0xf0:
+ irig_decode(peer, BIT1);
+ break;
+
+ case 0xf8:
+ case 0xfc:
+ irig_decode(peer, BITP);
+ break;
+
+ default:
+ irig_decode(peer, 0);
+ up->errflg |= IRIG_ERR_DECODE;
+ }
+ }
+}
+
+
+/*
+ * irig_decode - decode the data
+ *
+ * This routine assembles bits into digits, digits into subfields and
+ * subfields into the timecode field. Bits can have values of zero, one
+ * or position identifier. There are four bits per digit, two digits per
+ * subfield and ten subfields per field. The last bit in every subfield
+ * and the first bit in the first subfield are position identifiers.
+ */
+static void
+irig_decode(
+ struct peer *peer, /* peer structure pointer */
+ int bit /* data bit (0, 1 or 2) */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+#ifdef IRIG_SUCKS
+ int i;
+#endif /* IRIG_SUCKS */
+
+ /*
+ * Local variables
+ */
+ char syncchar; /* sync character (Spectracom) */
+ char sbs[6]; /* binary seconds since 0h */
+ char spare[2]; /* mulligan digits */
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * Assemble subfield bits.
+ */
+ up->bits <<= 1;
+ if (bit == BIT1) {
+ up->bits |= 1;
+ } else if (bit == BITP && up->lastbit == BITP) {
+
+ /*
+ * Frame sync - two adjacent position identifiers.
+ * Monitor the reference timestamp and wiggle the
+ * clock, but only if no errors have occurred.
+ */
+ up->bitcnt = 1;
+ up->fieldcnt = 0;
+ up->lastbit = 0;
+ if (up->errflg == 0) {
+#ifdef IRIG_SUCKS
+ l_fp ltemp;
+
+ /*
+ * You really don't wanna know what comes down
+ * here. Leave it to say Solaris 2.8 broke the
+ * nice clean audio stream, apparently affected
+ * by a 5-ms sawtooth jitter. Sundown on
+ * Solaris. This leaves a little twilight.
+ *
+ * The scheme involves differentiation, forward
+ * learning and integration. The sawtooth has a
+ * period of 11 seconds. The timestamp
+ * differences are integrated and subtracted
+ * from the signal.
+ */
+ ltemp = pp->lastrec;
+ L_SUB(&ltemp, &pp->lastref);
+ if (ltemp.l_f < 0)
+ ltemp.l_i = -1;
+ else
+ ltemp.l_i = 0;
+ pp->lastref = pp->lastrec;
+ if (!L_ISNEG(&ltemp))
+ L_CLR(&up->wigwag);
+ else
+ L_ADD(&up->wigwag, &ltemp);
+ L_SUB(&pp->lastrec, &up->wigwag);
+ up->wiggle[up->wp] = ltemp;
+
+ /*
+ * Bottom fisher. To understand this, you have
+ * to know about velocity microphones and AM
+ * transmitters. No further explanation is
+ * offered, as this is truly a black art.
+ */
+ up->wigbot[up->wp] = pp->lastrec;
+ for (i = 0; i < WIGGLE; i++) {
+ if (i != up->wp)
+ up->wigbot[i].l_ui++;
+ L_SUB(&pp->lastrec, &up->wigbot[i]);
+ if (L_ISNEG(&pp->lastrec))
+ L_ADD(&pp->lastrec,
+ &up->wigbot[i]);
+ else
+ pp->lastrec = up->wigbot[i];
+ }
+ up->wp++;
+ up->wp %= WIGGLE;
+ up->wuggle = pp->lastrec;
+ refclock_process(pp);
+#else /* IRIG_SUCKS */
+ pp->lastref = pp->lastrec;
+ up->wuggle = pp->lastrec;
+ refclock_process(pp);
+#endif /* IRIG_SUCKS */
+ }
+ up->errflg = 0;
+ }
+ up->bitcnt = (up->bitcnt + 1) % SUBFLD;
+ if (up->bitcnt == 0) {
+
+ /*
+ * End of subfield. Encode two hexadecimal digits in
+ * little-endian timecode field.
+ */
+ if (up->fieldcnt == 0)
+ up->bits <<= 1;
+ if (up->xptr < 2)
+ up->xptr = 2 * FIELD;
+ up->timecode[--up->xptr] = hexchar[(up->bits >> 5) &
+ 0xf];
+ up->timecode[--up->xptr] = hexchar[up->bits & 0xf];
+ up->fieldcnt = (up->fieldcnt + 1) % FIELD;
+ if (up->fieldcnt == 0) {
+
+ /*
+ * End of field. Decode the timecode and wind
+ * the clock. Not all IRIG generators have the
+ * year; if so, it is nonzero after year 2000.
+ * Not all have the hardware status bit; if so,
+ * it is lit when the source is okay and dim
+ * when bad. We watch this only if the year is
+ * nonzero. Not all are configured for signature
+ * control. If so, all BCD digits are set to
+ * zero if the source is bad. In this case the
+ * refclock_process() will reject the timecode
+ * as invalid.
+ */
+ up->xptr = 2 * FIELD;
+ if (sscanf((char *)up->timecode,
+ "%6s%2d%c%2s%3d%2d%2d%2d", sbs, &pp->year,
+ &syncchar, spare, &pp->day, &pp->hour,
+ &pp->minute, &pp->second) != 8)
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ up->second = (up->second + up->decim) % 60;
+ if (pp->year > 0) {
+ pp->year += 2000;
+ if (syncchar == '0')
+ up->errflg |= IRIG_ERR_CHECK;
+ }
+ if (pp->second != up->second)
+ up->errflg |= IRIG_ERR_CHECK;
+ up->second = pp->second;
+ sprintf(pp->a_lastcode,
+ "%02x %c %2d %3d %02d:%02d:%02d %4.0f %3d %6.3f %2d %6.1f %6.1f %s",
+ up->errflg, syncchar, pp->year, pp->day,
+ pp->hour, pp->minute, pp->second,
+ up->maxsignal, up->gain, up->modndx,
+ up->tc, up->exing * 1e6 / SECOND, up->freq *
+ 1e6 / SECOND, ulfptoa(&up->wuggle, 6));
+ pp->lencode = strlen(pp->a_lastcode);
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ record_clock_stats(&peer->srcadr,
+ pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("irig: %s\n",
+ pp->a_lastcode);
+#endif /* DEBUG */
+ }
+ }
+ }
+ up->lastbit = bit;
+}
+
+
+/*
+ * irig_poll - called by the transmit procedure
+ *
+ * This routine sweeps up the timecode updates since the last poll. For
+ * IRIG-B there should be at least 60 updates; for IRIG-E there should
+ * be at least 6. If nothing is heard, a timeout event is declared and
+ * any orphaned timecode updates are sent to foster care.
+ */
+static void
+irig_poll(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ } else {
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("irig: %s\n", pp->a_lastcode);
+#endif /* DEBUG */
+ }
+ pp->polls++;
+
+}
+
+
+/*
+ * irig_gain - adjust codec gain
+ *
+ * This routine is called once each second. If the signal envelope
+ * amplitude is too low, the codec gain is bumped up by four units; if
+ * too high, it is bumped down. The decoder is relatively insensitive to
+ * amplitude, so this crudity works just fine. The input port is set and
+ * the error flag is cleared, mostly to be ornery.
+ */
+static void
+irig_gain(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct irigunit *up;
+
+ pp = peer->procptr;
+ up = (struct irigunit *)pp->unitptr;
+
+ /*
+ * Apparently, the codec uses only the high order bits of the
+ * gain control field. Thus, it may take awhile for changes to
+ * wiggle the hardware bits.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > MAXGAIN)
+ up->gain = MAXGAIN;
+ } else if (up->clipcnt > MAXCLP) {
+ up->gain -= 4;
+ if (up->gain < 0)
+ up->gain = 0;
+ }
+ audio_gain(up->gain, up->mongain, up->port);
+ up->clipcnt = 0;
+}
+
+#else
+int refclock_irig_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_jjy.c b/ntpd/refclock_jjy.c
new file mode 100644
index 0000000..2aa9d6a
--- /dev/null
+++ b/ntpd/refclock_jjy.c
@@ -0,0 +1,710 @@
+/*
+ * refclock_jjy - clock driver for JJY receivers
+ */
+
+/**********************************************************************/
+/* */
+/* Copyright (C) 2001, Takao Abe. All rights reserved. */
+/* */
+/* Permission to use, copy, modify, and distribute this software */
+/* and its documentation for any purpose is hereby granted */
+/* without fee, provided that the following conditions are met: */
+/* */
+/* One retains the entire copyright notice properly, and both the */
+/* copyright notice and this license. in the documentation and/or */
+/* other materials provided with the distribution. */
+/* */
+/* This software and the name of the author must not be used to */
+/* endorse or promote products derived from this software without */
+/* prior written permission. */
+/* */
+/* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESSED OR IMPLIED */
+/* WARRANTIES OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, THE */
+/* IMPLIED WARRANTIES OF MERCHANTABLILITY AND FITNESS FOR A */
+/* PARTICULAR PURPOSE. */
+/* IN NO EVENT SHALL THE AUTHOR TAKAO ABE BE LIABLE FOR ANY DIRECT, */
+/* INDIRECT, GENERAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
+/* ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE */
+/* GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS */
+/* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */
+/* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ( INCLUDING */
+/* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF */
+/* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+/* */
+/* This driver is developed in my private time, and is opened as */
+/* voluntary contributions for the NTP. */
+/* The manufacturer of the JJY receiver has not participated in */
+/* a development of this driver. */
+/* The manufacturer does not warrant anything about this driver, */
+/* and is not liable for anything about this driver. */
+/* */
+/**********************************************************************/
+/* */
+/* Author Takao Abe */
+/* Email abetakao@bea.hi-ho.ne.jp */
+/* Homepage http://www.bea.hi-ho.ne.jp/abetakao/ */
+/* */
+/**********************************************************************/
+/* */
+/* History */
+/* */
+/* 2001/07/15 */
+/* [New] Support the Tristate Ltd. JJY receiver */
+/* */
+/* 2001/08/04 */
+/* [Change] Log to clockstats even if bad reply */
+/* [Fix] PRECISION = (-3) (about 100 ms) */
+/* [Add] Support the C-DEX Co.Ltd. JJY receiver */
+/* 2001/12/04 */
+/* [Fix] C-DEX JST2000 ( fukusima@goto.info.waseda.ac.jp ) */
+/* */
+/**********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_JJY)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_tty.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/**********************************************************************/
+/* */
+/* The Tristate Ltd. JJY receiver JJY01 */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* date<CR><LF> YYYY/MM/DD XXX<CR><LF> */
+/* time<CR><LF> HH:MM:SS<CR><LF> */
+/* stim<CR><LF> HH:MM:SS<CR><LF> Reply at just second */
+/* */
+/* During synchronization after a receiver is turned on, */
+/* It replies the past time from 2000/01/01 00:00:00. */
+/* The function "refclock_process" checks the time and tells */
+/* as an insanity time. */
+/* */
+/**********************************************************************/
+/* */
+/* The C-DEX Co. Ltd. JJY receiver JST2000 */
+/* */
+/* Command Response Remarks */
+/* ------------ ---------------------- --------------------- */
+/* <ENQ>1J<ETX> <STX>JYYMMDD HHMMSSS<ETX> */
+/* */
+/**********************************************************************/
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/jjy%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define REFID "JJY" /* reference ID */
+#define DESCRIPTION "JJY Receiver"
+#define PRECISION (-3) /* precision assumed (about 100 ms) */
+
+/*
+ * JJY unit control structure
+ */
+struct jjyunit {
+ char unittype ; /* UNITTYPE_XXXXXXXXXX */
+ short version ;
+ short linediscipline ; /* LDISC_CLK or LDISC_RAW */
+ int linecount ;
+ int lineerror ;
+ int year, month, day, hour, minute, second, msecond ;
+/* LDISC_RAW only */
+#define MAX_LINECOUNT 8
+#define MAX_RAWBUF 64
+ int lineexpect ;
+ int charexpect [ MAX_LINECOUNT ] ;
+ int charcount ;
+ char rawbuf [ MAX_RAWBUF ] ;
+};
+
+#define UNITTYPE_TRISTATE_JJY01 1
+#define UNITTYPE_CDEX_JST2000 2
+
+/*
+ * Function prototypes
+ */
+static int jjy_start P((int, struct peer *));
+static void jjy_shutdown P((int, struct peer *));
+static void jjy_poll P((int, struct peer *));
+static void jjy_poll_tristate_jjy01 P((int, struct peer *));
+static void jjy_poll_cdex_jst2000 P((int, struct peer *));
+static void jjy_receive P((struct recvbuf *));
+static int jjy_receive_tristate_jjy01 P((struct recvbuf *));
+static int jjy_receive_cdex_jst2000 P((struct recvbuf *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_jjy = {
+ jjy_start, /* start up driver */
+ jjy_shutdown, /* shutdown driver */
+ jjy_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Start up driver return code
+ */
+#define RC_START_SUCCESS 1
+#define RC_START_ERROR 0
+
+/*
+ * Local constants definition
+ */
+
+#define MAX_LOGTEXT 64
+
+
+/**************************************************************************************************/
+/* jjy_start - open the devices and initialize data for processing */
+/**************************************************************************************************/
+static int
+jjy_start ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ int fd ;
+ char *pDeviceName ;
+ short iDiscipline ;
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_start (refclock_jjy.c) : %s mode=%d ", ntoa(&peer->srcadr), peer->ttl ) ;
+ printf ( DEVICE, unit ) ;
+ printf ( "\n" ) ;
+ }
+#endif
+ /*
+ * Open serial port
+ */
+ if ( ! ( pDeviceName = (char*) emalloc ( strlen(DEVICE) + 10 ) ) ) {
+ return RC_START_ERROR ;
+ }
+ sprintf ( pDeviceName, DEVICE, unit ) ;
+
+ /*
+ * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
+ */
+ switch ( peer->ttl ) {
+ case 0 :
+ case 1 : iDiscipline = LDISC_CLK ; break ;
+ case 2 : iDiscipline = LDISC_RAW ; break ;
+ default :
+ msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode",
+ ntoa(&peer->srcadr), peer->ttl ) ;
+ free ( (void*) pDeviceName ) ;
+ return RC_START_ERROR ;
+ }
+
+ if ( ! ( fd = refclock_open ( pDeviceName, SPEED232, iDiscipline ) ) ) {
+ free ( (void*) pDeviceName ) ;
+ return RC_START_ERROR ;
+ }
+ free ( (void*) pDeviceName ) ;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if ( ! ( up = (struct jjyunit *) emalloc (sizeof(struct jjyunit)) ) ) {
+ close ( fd ) ;
+ return RC_START_ERROR ;
+ }
+
+ memset ( (char*)up, 0, sizeof(struct jjyunit) ) ;
+ up->linediscipline = iDiscipline ;
+
+ /*
+ * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
+ */
+ switch ( peer->ttl ) {
+ case 0 :
+ /*
+ * The mode 0 is a default clock type at this time.
+ * But this will be change to auto-detect mode in the future.
+ */
+ case 1 :
+ up->unittype = UNITTYPE_TRISTATE_JJY01 ;
+ up->version = 100 ;
+ up->lineexpect = 2 ;
+ up->charexpect[0] = 14 ; /* YYYY/MM/DD WWW<CR><LF> */
+ up->charexpect[1] = 8 ; /* HH:MM:SS<CR><LF> */
+ break ;
+ case 2 :
+ up->unittype = UNITTYPE_CDEX_JST2000 ;
+ up->lineexpect = 1 ;
+ up->charexpect[0] = 15 ; /* <STX>JYYMMDD HHMMSSS<ETX> */
+ break ;
+ default :
+ msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode",
+ ntoa(&peer->srcadr), peer->ttl ) ;
+ close ( fd ) ;
+ free ( (void*) up ) ;
+ return RC_START_ERROR ;
+ }
+
+ pp = peer->procptr ;
+ pp->unitptr = (caddr_t) up ;
+ pp->io.clock_recv = jjy_receive ;
+ pp->io.srcclock = (caddr_t) peer ;
+ pp->io.datalen = 0 ;
+ pp->io.fd = fd ;
+ if ( ! io_addclock(&pp->io) ) {
+ close ( fd ) ;
+ free ( (void*) up ) ;
+ return RC_START_ERROR ;
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION ;
+ peer->burst = 1 ;
+ pp->clockdesc = DESCRIPTION ;
+ memcpy ( (char*)&pp->refid, REFID, strlen(REFID) ) ;
+
+ return RC_START_SUCCESS ;
+
+}
+
+
+/**************************************************************************************************/
+/* jjy_shutdown - shutdown the clock */
+/**************************************************************************************************/
+static void
+jjy_shutdown ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr ;
+ up = (struct jjyunit *) pp->unitptr ;
+ io_closeclock ( &pp->io ) ;
+ free ( (void*) up ) ;
+
+}
+
+
+/**************************************************************************************************/
+/* jjy_receive - receive data from the serial interface */
+/**************************************************************************************************/
+static void
+jjy_receive ( struct recvbuf *rbufp )
+{
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ l_fp tRecvTimestamp; /* arrival timestamp */
+ int rc ;
+ char sLogText [ MAX_LOGTEXT ] ;
+ int i, bCntrlChar ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *) rbufp->recv_srcclock ;
+ pp = peer->procptr ;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ /*
+ * Get next input line
+ */
+ pp->lencode = refclock_gtlin ( rbufp, pp->a_lastcode, BMAX, &tRecvTimestamp ) ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ /*
+ * The reply with <STX> and <ETX> may give a blank line
+ */
+ if ( pp->lencode == 0 && up->charcount == 0 ) return ;
+ /*
+ * Copy received charaters to temporary buffer
+ */
+ for ( i = 0 ; i < pp->lencode && up->charcount < MAX_RAWBUF - 2 ; i ++ , up->charcount ++ ) {
+ up->rawbuf[up->charcount] = pp->a_lastcode[i] ;
+ }
+ while ( up->charcount > 0 && up->rawbuf[0] < ' ' ) {
+ for ( i = 0 ; i < up->charcount - 1 ; i ++ ) up->rawbuf[i] = up->rawbuf[i+1] ;
+ up->charcount -- ;
+ }
+ bCntrlChar = 0 ;
+ for ( i = 0 ; i < up->charcount ; i ++ ) {
+ if ( up->rawbuf[i] < ' ' ) {
+ bCntrlChar = 1 ;
+ break ;
+ }
+ }
+ if ( pp->lencode > 0 && up->linecount < up->lineexpect ) {
+ if ( bCntrlChar == 0 && up->charcount < up->charexpect[up->linecount] ) return ;
+ }
+ up->rawbuf[up->charcount] = 0 ;
+ } else {
+ /*
+ * The reply with <CR><LF> gives a blank line
+ */
+ if ( pp->lencode == 0 ) return ;
+ }
+ /*
+ * We get down to business
+ */
+
+ pp->lastrec = tRecvTimestamp ;
+
+ up->linecount ++ ;
+
+ if ( up->lineerror != 0 ) return ;
+
+ switch ( up->unittype ) {
+
+ case UNITTYPE_TRISTATE_JJY01 :
+ rc = jjy_receive_tristate_jjy01 ( rbufp ) ;
+ break ;
+
+ case UNITTYPE_CDEX_JST2000 :
+ rc = jjy_receive_cdex_jst2000 ( rbufp ) ;
+ break ;
+
+ default :
+ rc = 0 ;
+ break ;
+
+ }
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ if ( up->linecount <= up->lineexpect && up->charcount > up->charexpect[up->linecount-1] ) {
+ for ( i = 0 ; i < up->charcount - up->charexpect[up->linecount-1] ; i ++ ) {
+ up->rawbuf[i] = up->rawbuf[i+up->charexpect[up->linecount-1]] ;
+ }
+ up->charcount -= up->charexpect[up->linecount-1] ;
+ } else {
+ up->charcount = 0 ;
+ }
+ }
+
+ if ( rc == 0 ) return ;
+
+ if ( up->lineerror != 0 ) {
+ refclock_report ( peer, CEVNT_BADREPLY ) ;
+ strcpy ( sLogText, "BAD REPLY [" ) ;
+ if ( up->linediscipline == LDISC_RAW ) {
+ strncat ( sLogText, up->rawbuf, MAX_LOGTEXT - strlen ( sLogText ) - 1 ) ;
+ } else {
+ strncat ( sLogText, pp->a_lastcode, MAX_LOGTEXT - strlen ( sLogText ) - 1 ) ;
+ }
+ sLogText[MAX_LOGTEXT-1] = 0 ;
+ if ( strlen ( sLogText ) < MAX_LOGTEXT - 2 ) strcat ( sLogText, "]" ) ;
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+ return ;
+ }
+
+ pp->year = up->year ;
+ pp->day = ymd2yd ( up->year, up->month, up->day ) ;
+ pp->hour = up->hour ;
+ pp->minute = up->minute ;
+ pp->second = up->second ;
+ pp->nsec = up->msecond * 1000000;
+
+ /*
+ * JST to UTC
+ */
+ pp->hour -= 9 ;
+ if ( pp->hour < 0 ) {
+ pp->hour += 24 ;
+ pp->day -- ;
+ if ( pp->day < 1 ) {
+ pp->year -- ;
+ pp->day = ymd2yd ( pp->year, 12, 31 ) ;
+ }
+ }
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_receive (refclock_jjy.c) : %04d/%02d/%02d %02d:%02d:%02d JST ",
+ up->year, up->month, up->day, up->hour, up->minute, up->second ) ;
+ printf ( "( %04d/%03d %02d:%02d:%02d UTC )\n",
+ pp->year, pp->day, pp->hour, pp->minute, pp->second ) ;
+ }
+#endif
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if ( ! refclock_process ( pp ) ) {
+ refclock_report(peer, CEVNT_BADTIME);
+ sprintf ( sLogText, "BAD TIME %04d/%02d/%02d %02d:%02d:%02d JST",
+ up->year, up->month, up->day, up->hour, up->minute, up->second ) ;
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+ return ;
+ }
+
+ sprintf ( sLogText, "%04d/%02d/%02d %02d:%02d:%02d JST",
+ up->year, up->month, up->day, up->hour, up->minute, up->second ) ;
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats ( &peer->srcadr, sLogText ) ;
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp )
+{
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *) rbufp->recv_srcclock ;
+ pp = peer->procptr ;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ switch ( up->linecount ) {
+
+ case 1 : /* YYYY/MM/DD */
+
+ if ( iLen < 10 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+ rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year, &up->month, &up->day ) ;
+ if ( rc != 3 || up->year < 2000 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+ return 0 ;
+
+ case 2 : /* HH:MM:SS */
+
+ if ( iLen < 8 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+ rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour, &up->minute, &up->second ) ;
+ if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+ up->msecond = 0 ;
+ if ( up->hour == 0 && up->minute == 0 && up->second <= 2 ) {
+ /*
+ * The command "date" and "time" ( or "stim" ) were sent to the JJY receiver continuously.
+ * But the JJY receiver replies a date and time separately.
+ * Just after midnight transtions, we ignore this time.
+ */
+ return 0 ;
+ }
+ break ;
+
+ default : /* Unexpected reply */
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ return 1 ;
+
+}
+
+/**************************************************************************************************/
+
+static int
+jjy_receive_cdex_jst2000 ( struct recvbuf *rbufp )
+{
+
+ struct jjyunit *up ;
+ struct refclockproc *pp ;
+ struct peer *peer;
+
+ char *pBuf ;
+ int iLen ;
+ int rc ;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *) rbufp->recv_srcclock ;
+ pp = peer->procptr ;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ if ( up->linediscipline == LDISC_RAW ) {
+ pBuf = up->rawbuf ;
+ iLen = up->charcount ;
+ } else {
+ pBuf = pp->a_lastcode ;
+ iLen = pp->lencode ;
+ }
+
+ switch ( up->linecount ) {
+
+ case 1 : /* JYYMMDD HHMMSSS */
+
+ if ( iLen < 15 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+ rc = sscanf ( pBuf, "J%2d%2d%2d%*1d%2d%2d%2d%1d",
+ &up->year, &up->month, &up->day, &up->hour, &up->minute, &up->second, &up->msecond ) ;
+ if ( rc != 7 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31
+ || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
+ up->lineerror = 1 ;
+ break ;
+ }
+ up->year += 2000 ;
+ up->msecond *= 100 ;
+ break ;
+
+ default : /* Unexpected reply */
+
+ up->lineerror = 1 ;
+ break ;
+
+ }
+
+ return 1 ;
+
+}
+
+/**************************************************************************************************/
+/* jjy_poll - called by the transmit procedure */
+/**************************************************************************************************/
+static void
+jjy_poll ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ if ( pp->polls > 0 && up->linecount == 0 ) {
+ /*
+ * No reply for last command
+ */
+ refclock_report ( peer, CEVNT_TIMEOUT ) ;
+ }
+
+#ifdef DEBUG
+ if ( debug ) {
+ printf ( "jjy_poll (refclock_jjy.c) : %ld\n", pp->polls ) ;
+ }
+#endif
+
+ pp->polls ++ ;
+
+ up->linecount = 0 ;
+ up->lineerror = 0 ;
+ up->charcount = 0 ;
+
+ switch ( up->unittype ) {
+
+ case UNITTYPE_TRISTATE_JJY01 :
+ jjy_poll_tristate_jjy01 ( unit, peer ) ;
+ break ;
+
+ case UNITTYPE_CDEX_JST2000 :
+ jjy_poll_cdex_jst2000 ( unit, peer ) ;
+ break ;
+
+ default :
+ break ;
+
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_tristate_jjy01 ( int unit, struct peer *peer )
+{
+
+ struct jjyunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct jjyunit *) pp->unitptr ;
+
+ /*
+ * Send "date<CR><LF>" command
+ */
+
+ if ( write ( pp->io.fd, "date\r\n",6 ) != 6 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+ /*
+ * Send "stim<CR><LF>" or "time<CR><LF>" command
+ */
+
+ if ( up->version >= 100 ) {
+ if ( write ( pp->io.fd, "stim\r\n",6 ) != 6 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+ } else {
+ if ( write ( pp->io.fd, "time\r\n",6 ) != 6 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+ }
+
+}
+
+/**************************************************************************************************/
+
+static void
+jjy_poll_cdex_jst2000 ( int unit, struct peer *peer )
+{
+
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Send "<ENQ>1J<ETX>" command
+ */
+
+ if ( write ( pp->io.fd, "\0051J\003", 4 ) != 4 ) {
+ refclock_report ( peer, CEVNT_FAULT ) ;
+ }
+
+}
+
+#else
+int refclock_jjy_bs ;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_jupiter.c b/ntpd/refclock_jupiter.c
new file mode 100644
index 0000000..eff088b
--- /dev/null
+++ b/ntpd/refclock_jupiter.c
@@ -0,0 +1,1140 @@
+/*
+ * Copyright (c) 1997, 1998, 2003
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. The name of the University may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "jupiter.h"
+
+#ifdef HAVE_PPSAPI
+# ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+# else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# endif
+# endif
+#endif
+
+#ifdef XNTP_BIG_ENDIAN
+#define getshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
+#define putshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
+#else
+#define getshort(s) (s)
+#define putshort(s) (s)
+#endif
+
+/* XXX */
+#ifdef sun
+char *strerror(int);
+#endif
+
+/*
+ * This driver supports the Rockwell Jupiter GPS Receiver board
+ * adapted to precision timing applications. It requires the
+ * ppsclock line discipline or streams module described in the
+ * Line Disciplines and Streams Drivers page. It also requires a
+ * gadget box and 1-PPS level converter, such as described in the
+ * Pulse-per-second (PPS) Signal Interfacing page.
+ *
+ * It may work (with minor modifications) with other Rockwell GPS
+ * receivers such as the CityTracker.
+ */
+
+/*
+ * GPS Definitions
+ */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* baud */
+
+/*
+ * Radio interface parameters
+ */
+#define PRECISION (-18) /* precision assumed (about 4 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "Rockwell Jupiter GPS Receiver" /* who we are */
+#define DEFFUDGETIME 0 /* default fudge time (ms) */
+
+/* Unix timestamp for the GPS epoch: January 6, 1980 */
+#define GPS_EPOCH 315964800
+
+/* Double short to unsigned int */
+#define DS2UI(p) ((getshort((p)[1]) << 16) | getshort((p)[0]))
+
+/* Double short to signed int */
+#define DS2I(p) ((getshort((p)[1]) << 16) | getshort((p)[0]))
+
+/* One week's worth of seconds */
+#define WEEKSECS (7 * 24 * 60 * 60)
+
+/*
+ * Jupiter unit control structure.
+ */
+struct instance {
+ struct peer *peer; /* peer */
+ u_int pollcnt; /* poll message counter */
+ u_int polled; /* Hand in a time sample? */
+#ifdef HAVE_PPSAPI
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t pps_handle; /* pps handle */
+ u_int assert; /* pps edge to use */
+ struct timespec ts; /* last timestamp */
+#endif
+ l_fp limit;
+ u_int gpos_gweek; /* Current GPOS GPS week number */
+ u_int gpos_sweek; /* Current GPOS GPS seconds into week */
+ u_int gweek; /* current GPS week number */
+ u_int32 lastsweek; /* last seconds into GPS week */
+ time_t timecode; /* current ntp timecode */
+ u_int32 stime; /* used to detect firmware bug */
+ int wantid; /* don't reconfig on channel id msg */
+ u_int moving; /* mobile platform? */
+ u_char sloppyclockflag; /* fudge flags */
+ u_short sbuf[512]; /* local input buffer */
+ int ssize; /* space used in sbuf */
+};
+
+/*
+ * Function prototypes
+ */
+static void jupiter_canmsg P((struct instance *, u_int));
+static u_short jupiter_cksum P((u_short *, u_int));
+static int jupiter_config P((struct instance *));
+static void jupiter_debug P((struct peer *, char *, char *, ...))
+ __attribute__ ((format (printf, 3, 4)));
+static char * jupiter_parse_t P((struct instance *, u_short *));
+static char * jupiter_parse_gpos P((struct instance *, u_short *));
+static void jupiter_platform P((struct instance *, u_int));
+static void jupiter_poll P((int, struct peer *));
+static void jupiter_control P((int, struct refclockstat *, struct
+ refclockstat *, struct peer *));
+#ifdef HAVE_PPSAPI
+static int jupiter_ppsapi P((struct instance *, int, int));
+static int jupiter_pps P((struct instance *));
+#endif /* HAVE_PPSAPI */
+static int jupiter_recv P((struct instance *));
+static void jupiter_receive P((struct recvbuf *rbufp));
+static void jupiter_reqmsg P((struct instance *, u_int, u_int));
+static void jupiter_reqonemsg P((struct instance *, u_int));
+static char * jupiter_send P((struct instance *, struct jheader *));
+static void jupiter_shutdown P((int, struct peer *));
+static int jupiter_start P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_jupiter = {
+ jupiter_start, /* start up driver */
+ jupiter_shutdown, /* shut down driver */
+ jupiter_poll, /* transmit poll message */
+ jupiter_control, /* (clock control) */
+ noentry, /* (clock init) */
+ noentry, /* (clock buginfo) */
+ NOFLAGS /* not used */
+};
+
+/*
+ * jupiter_start - open the devices and initialize data for processing
+ */
+static int
+jupiter_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct instance *instance;
+ int fd = -1;
+ char gpsdev[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ fd = refclock_open(gpsdev, SPEED232, LDISC_RAW);
+ if (fd == 0) {
+ jupiter_debug(peer, "jupiter_start", "open %s: %s",
+ gpsdev, strerror(errno));
+ return (0);
+ }
+
+ /* Allocate unit structure */
+ if ((instance = (struct instance *)
+ emalloc(sizeof(struct instance))) == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)instance, 0, sizeof(struct instance));
+ instance->peer = peer;
+ pp = peer->procptr;
+ pp->io.clock_recv = jupiter_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(instance);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)instance;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+#ifdef HAVE_PPSAPI
+ /*
+ * Start the PPSAPI interface if it is there. Default to use
+ * the assert edge and do not enable the kernel hardpps.
+ */
+ if (time_pps_create(fd, &instance->pps_handle) < 0) {
+ instance->pps_handle = 0;
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_create failed: %m");
+ }
+ else if (!jupiter_ppsapi(instance, 0, 0))
+ goto clean_up;
+#endif /* HAVE_PPSAPI */
+
+ /* Ensure the receiver is properly configured */
+ if (!jupiter_config(instance))
+ goto clean_up;
+
+ return (1);
+
+clean_up:
+ jupiter_shutdown(unit, peer);
+ pp->unitptr = 0;
+ return (0);
+}
+
+/*
+ * jupiter_shutdown - shut down the clock
+ */
+static void
+jupiter_shutdown(int unit, struct peer *peer)
+{
+ struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = (struct instance *)pp->unitptr;
+ if(!instance)
+ return;
+
+#ifdef HAVE_PPSAPI
+ if (instance->pps_handle) {
+ time_pps_destroy(instance->pps_handle);
+ instance->pps_handle = 0;
+ }
+#endif /* HAVE_PPSAPI */
+
+ io_closeclock(&pp->io);
+ free(instance);
+}
+
+/*
+ * jupiter_config - Configure the receiver
+ */
+static int
+jupiter_config(struct instance *instance)
+{
+ jupiter_debug(instance->peer, "jupiter_config", "init receiver");
+
+ /*
+ * Initialize the unit variables
+ */
+ instance->sloppyclockflag = instance->peer->procptr->sloppyclockflag;
+ instance->moving = !!(instance->sloppyclockflag & CLK_FLAG2);
+ if (instance->moving)
+ jupiter_debug(instance->peer, "jupiter_config",
+ "mobile platform");
+
+ instance->pollcnt = 2;
+ instance->polled = 0;
+ instance->gpos_gweek = 0;
+ instance->gpos_sweek = 0;
+ instance->gweek = 0;
+ instance->lastsweek = 2 * WEEKSECS;
+ instance->timecode = 0;
+ instance->stime = 0;
+ instance->ssize = 0;
+
+ /* Stop outputting all messages */
+ jupiter_canmsg(instance, JUPITER_ALL);
+
+ /* Request the receiver id so we can syslog the firmware version */
+ jupiter_reqonemsg(instance, JUPITER_O_ID);
+
+ /* Flag that this the id was requested (so we don't get called again) */
+ instance->wantid = 1;
+
+ /* Request perodic time mark pulse messages */
+ jupiter_reqmsg(instance, JUPITER_O_PULSE, 1);
+
+ /* Request perodic geodetic position status */
+ jupiter_reqmsg(instance, JUPITER_O_GPOS, 1);
+
+ /* Set application platform type */
+ if (instance->moving)
+ jupiter_platform(instance, JUPITER_I_PLAT_MED);
+ else
+ jupiter_platform(instance, JUPITER_I_PLAT_LOW);
+
+ return (1);
+}
+
+#ifdef HAVE_PPSAPI
+/*
+ * Initialize PPSAPI
+ */
+int
+jupiter_ppsapi(
+ struct instance *instance, /* unit structure pointer */
+ int enb_clear, /* clear enable */
+ int enb_hardpps /* hardpps enable */
+ )
+{
+ int capability;
+
+ if (time_pps_getcap(instance->pps_handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&instance->pps_params, 0, sizeof(pps_params_t));
+ if (enb_clear)
+ instance->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ instance->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!(instance->pps_params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: invalid capture edge %d",
+ !enb_clear);
+ return (0);
+ }
+ instance->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(instance->pps_handle, &instance->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (enb_hardpps) {
+ if (time_pps_kcbind(instance->pps_handle, PPS_KC_HARDPPS,
+ instance->pps_params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR),
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_jupiter: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+/* instance->peer->precision = PPS_PRECISION; */
+
+#if DEBUG
+ if (debug) {
+ time_pps_getparams(instance->pps_handle, &instance->pps_params);
+ jupiter_debug(instance->peer, "refclock_jupiter",
+ "pps capability 0x%x version %d mode 0x%x kern %d",
+ capability, instance->pps_params.api_version,
+ instance->pps_params.mode, enb_hardpps);
+ }
+#endif
+
+ return (1);
+}
+
+/*
+ * Get PPSAPI timestamps.
+ *
+ * Return 0 on failure and 1 on success.
+ */
+static int
+jupiter_pps(struct instance *instance)
+{
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+ l_fp tstmp;
+
+ /*
+ * Convert the timespec nanoseconds field to ntp l_fp units.
+ */
+ if (instance->pps_handle == 0)
+ return 1;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &instance->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(instance->pps_handle, PPS_TSFMT_TSPEC, &instance->pps_info,
+ &timeout) < 0)
+ return 1;
+ if (instance->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ instance->pps_info.assert_sequence)
+ return 1;
+ ts = instance->pps_info.assert_timestamp;
+ } else if (instance->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ instance->pps_info.clear_sequence)
+ return 1;
+ ts = instance->pps_info.clear_timestamp;
+ } else {
+ return 1;
+ }
+ if ((instance->ts.tv_sec == ts.tv_sec) && (instance->ts.tv_nsec == ts.tv_nsec))
+ return 1;
+ instance->ts = ts;
+
+ tstmp.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ tstmp.l_uf = (u_int32)dtemp;
+ instance->peer->procptr->lastrec = tstmp;
+ return 0;
+}
+#endif /* HAVE_PPSAPI */
+
+/*
+ * jupiter_poll - jupiter watchdog routine
+ */
+static void
+jupiter_poll(int unit, struct peer *peer)
+{
+ struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = (struct instance *)pp->unitptr;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+
+ /*
+ * If we haven't had a response in a while, reset the receiver.
+ */
+ if (instance->pollcnt > 0) {
+ instance->pollcnt--;
+ } else {
+ refclock_report(peer, CEVNT_TIMEOUT);
+
+ /* Request the receiver id to trigger a reconfig */
+ jupiter_reqonemsg(instance, JUPITER_O_ID);
+ instance->wantid = 0;
+ }
+
+ /*
+ * polled every 64 seconds. Ask jupiter_receive to hand in
+ * a timestamp.
+ */
+ instance->polled = 1;
+ pp->polls++;
+}
+
+/*
+ * jupiter_control - fudge control
+ */
+static void
+jupiter_control(
+ int unit, /* unit (not used) */
+ struct refclockstat *in, /* input parameters (not used) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct instance *instance;
+ u_char sloppyclockflag;
+
+ pp = peer->procptr;
+ instance = (struct instance *)pp->unitptr;
+
+ DTOLFP(pp->fudgetime2, &instance->limit);
+ /* Force positive value. */
+ if (L_ISNEG(&instance->limit))
+ L_NEG(&instance->limit);
+
+#ifdef HAVE_PPSAPI
+ instance->assert = !(pp->sloppyclockflag & CLK_FLAG3);
+ jupiter_ppsapi(instance, !instance->assert, 0);
+#endif /* HAVE_PPSAPI */
+
+ sloppyclockflag = instance->sloppyclockflag;
+ instance->sloppyclockflag = pp->sloppyclockflag;
+ if ((instance->sloppyclockflag & CLK_FLAG2) !=
+ (sloppyclockflag & CLK_FLAG2)) {
+ jupiter_debug(peer,
+ "jupiter_control",
+ "mode switch: reset receiver");
+ jupiter_config(instance);
+ return;
+ }
+}
+
+/*
+ * jupiter_receive - receive gps data
+ * Gag me!
+ */
+static void
+jupiter_receive(struct recvbuf *rbufp)
+{
+ int bpcnt, cc, size, ppsret;
+ time_t last_timecode;
+ u_int32 laststime;
+ char *cp;
+ u_char *bp;
+ u_short *sp;
+ struct jid *ip;
+ struct jheader *hp;
+ struct peer *peer;
+ struct refclockproc *pp;
+ struct instance *instance;
+ l_fp tstamp;
+
+ /* Initialize pointers and read the timecode and timestamp */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ instance = (struct instance *)pp->unitptr;
+
+ bp = (u_char *)rbufp->recv_buffer;
+ bpcnt = rbufp->recv_length;
+
+ /* This shouldn't happen */
+ if (bpcnt > sizeof(instance->sbuf) - instance->ssize)
+ bpcnt = sizeof(instance->sbuf) - instance->ssize;
+
+ /* Append to input buffer */
+ memcpy((u_char *)instance->sbuf + instance->ssize, bp, bpcnt);
+ instance->ssize += bpcnt;
+
+ /* While there's at least a header and we parse an intact message */
+ while (instance->ssize > sizeof(*hp) && (cc = jupiter_recv(instance)) > 0) {
+ instance->pollcnt = 2;
+
+ tstamp = rbufp->recv_time;
+ hp = (struct jheader *)instance->sbuf;
+ sp = (u_short *)(hp + 1);
+ size = cc - sizeof(*hp);
+ switch (getshort(hp->id)) {
+
+ case JUPITER_O_PULSE:
+ if (size != sizeof(struct jpulse)) {
+ jupiter_debug(peer,
+ "jupiter_receive", "pulse: len %d != %u",
+ size, (int)sizeof(struct jpulse));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+
+ /*
+ * There appears to be a firmware bug related
+ * to the pulse message; in addition to the one
+ * per second messages, we get an extra pulse
+ * message once an hour (on the anniversary of
+ * the cold start). It seems to come 200 ms
+ * after the one requested. So if we've seen a
+ * pulse message in the last 210 ms, we skip
+ * this one.
+ */
+ laststime = instance->stime;
+ instance->stime = DS2UI(((struct jpulse *)sp)->stime);
+ if (laststime != 0 && instance->stime - laststime <= 21) {
+ jupiter_debug(peer, "jupiter_receive",
+ "avoided firmware bug (stime %.2f, laststime %.2f)",
+ (double)instance->stime * 0.01, (double)laststime * 0.01);
+ break;
+ }
+
+ /* Retrieve pps timestamp */
+ ppsret = jupiter_pps(instance);
+
+ /*
+ * Add one second if msg received early
+ * (i.e. before limit, a.k.a. fudgetime2) in
+ * the second.
+ */
+ L_SUB(&tstamp, &pp->lastrec);
+ if (!L_ISGEQ(&tstamp, &instance->limit))
+ ++pp->lastrec.l_ui;
+
+ /* Parse timecode (even when there's no pps) */
+ last_timecode = instance->timecode;
+ if ((cp = jupiter_parse_t(instance, sp)) != NULL) {
+ jupiter_debug(peer,
+ "jupiter_receive", "pulse: %s", cp);
+ break;
+ }
+
+ /* Bail if we didn't get a pps timestamp */
+ if (ppsret)
+ break;
+
+ /* Bail if we don't have the last timecode yet */
+ if (last_timecode == 0)
+ break;
+
+ /* Add the new sample to a median filter */
+ tstamp.l_ui = JAN_1970 + last_timecode;
+ tstamp.l_uf = 0;
+
+ refclock_process_offset(pp, tstamp, pp->lastrec, pp->fudgetime1);
+
+ /*
+ * The clock will blurt a timecode every second
+ * but we only want one when polled. If we
+ * havn't been polled, bail out.
+ */
+ if (!instance->polled)
+ break;
+ instance->polled = 0;
+
+ /*
+ * It's a live one! Remember this time.
+ */
+
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /*
+ * If we get here - what we got from the clock is
+ * OK, so say so
+ */
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ /*
+ * We have succeeded in answering the poll.
+ * Turn off the flag and return
+ */
+ instance->polled = 0;
+ break;
+
+ case JUPITER_O_GPOS:
+ if (size != sizeof(struct jgpos)) {
+ jupiter_debug(peer,
+ "jupiter_receive", "gpos: len %d != %u",
+ size, (int)sizeof(struct jgpos));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+
+ if ((cp = jupiter_parse_gpos(instance, sp)) != NULL) {
+ jupiter_debug(peer,
+ "jupiter_receive", "gpos: %s", cp);
+ break;
+ }
+ break;
+
+ case JUPITER_O_ID:
+ if (size != sizeof(struct jid)) {
+ jupiter_debug(peer,
+ "jupiter_receive", "id: len %d != %u",
+ size, (int)sizeof(struct jid));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ /*
+ * If we got this message because the Jupiter
+ * just powered instance, it needs to be reconfigured.
+ */
+ ip = (struct jid *)sp;
+ jupiter_debug(peer,
+ "jupiter_receive", "%s chan ver %s, %s (%s)",
+ ip->chans, ip->vers, ip->date, ip->opts);
+ msyslog(LOG_DEBUG,
+ "jupiter_receive: %s chan ver %s, %s (%s)\n",
+ ip->chans, ip->vers, ip->date, ip->opts);
+ if (instance->wantid)
+ instance->wantid = 0;
+ else {
+ jupiter_debug(peer,
+ "jupiter_receive", "reset receiver");
+ jupiter_config(instance);
+ /*
+ * Restore since jupiter_config() just
+ * zeroed it
+ */
+ instance->ssize = cc;
+ }
+ break;
+
+ default:
+ jupiter_debug(peer,
+ "jupiter_receive", "unknown message id %d",
+ getshort(hp->id));
+ break;
+ }
+ instance->ssize -= cc;
+ if (instance->ssize < 0) {
+ fprintf(stderr, "jupiter_recv: negative ssize!\n");
+ abort();
+ } else if (instance->ssize > 0)
+ memcpy(instance->sbuf, (u_char *)instance->sbuf + cc, instance->ssize);
+ }
+}
+
+static char *
+jupiter_parse_t(struct instance *instance, u_short *sp)
+{
+ struct tm *tm;
+ char *cp;
+ struct jpulse *jp;
+ u_int32 sweek;
+ time_t last_timecode;
+ u_short flags;
+
+ jp = (struct jpulse *)sp;
+
+ /* The timecode is presented as seconds into the current GPS week */
+ sweek = DS2UI(jp->sweek) % WEEKSECS;
+
+ /*
+ * If we don't know the current GPS week, calculate it from the
+ * current time. (It's too bad they didn't include this
+ * important value in the pulse message). We'd like to pick it
+ * up from one of the other messages like gpos or chan but they
+ * don't appear to be synchronous with time keeping and changes
+ * too soon (something like 10 seconds before the new GPS
+ * week).
+ *
+ * If we already know the current GPS week, increment it when
+ * we wrap into a new week.
+ */
+ if (instance->gweek == 0) {
+ if (!instance->gpos_gweek) {
+ return ("jupiter_parse_t: Unknown gweek");
+ }
+
+ instance->gweek = instance->gpos_gweek;
+
+ /*
+ * Fix warps. GPOS has GPS time and PULSE has UTC.
+ * Plus, GPOS need not be completely in synch with
+ * the PPS signal.
+ */
+ if (instance->gpos_sweek >= sweek) {
+ if ((instance->gpos_sweek - sweek) > WEEKSECS / 2)
+ ++instance->gweek;
+ }
+ else {
+ if ((sweek - instance->gpos_sweek) > WEEKSECS / 2)
+ --instance->gweek;
+ }
+ }
+ else if (sweek == 0 && instance->lastsweek == WEEKSECS - 1) {
+ ++instance->gweek;
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "NEW gps week %u", instance->gweek);
+ }
+
+ /*
+ * See if the sweek stayed the same (this happens when there is
+ * no pps pulse).
+ *
+ * Otherwise, look for time warps:
+ *
+ * - we have stored at least one lastsweek and
+ * - the sweek didn't increase by one and
+ * - we didn't wrap to a new GPS week
+ *
+ * Then we warped.
+ */
+ if (instance->lastsweek == sweek)
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "gps sweek not incrementing (%d)",
+ sweek);
+ else if (instance->lastsweek != 2 * WEEKSECS &&
+ instance->lastsweek + 1 != sweek &&
+ !(sweek == 0 && instance->lastsweek == WEEKSECS - 1))
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "gps sweek jumped (was %d, now %d)",
+ instance->lastsweek, sweek);
+ instance->lastsweek = sweek;
+
+ /* This timecode describes next pulse */
+ last_timecode = instance->timecode;
+ instance->timecode =
+ GPS_EPOCH + (instance->gweek * WEEKSECS) + sweek;
+
+ if (last_timecode == 0)
+ /* XXX debugging */
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "UTC <none> (gweek/sweek %u/%u)",
+ instance->gweek, sweek);
+ else {
+ /* XXX debugging */
+ tm = gmtime(&last_timecode);
+ cp = asctime(tm);
+
+ jupiter_debug(instance->peer,
+ "jupiter_parse_t", "UTC %.24s (gweek/sweek %u/%u)",
+ cp, instance->gweek, sweek);
+
+ /* Billboard last_timecode (which is now the current time) */
+ instance->peer->procptr->year = tm->tm_year + 1900;
+ instance->peer->procptr->day = tm->tm_yday + 1;
+ instance->peer->procptr->hour = tm->tm_hour;
+ instance->peer->procptr->minute = tm->tm_min;
+ instance->peer->procptr->second = tm->tm_sec;
+ }
+
+ flags = getshort(jp->flags);
+
+ /* Toss if not designated "valid" by the gps */
+ if ((flags & JUPITER_O_PULSE_VALID) == 0) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ return ("time mark not valid");
+ }
+
+ /* We better be sync'ed to UTC... */
+ if ((flags & JUPITER_O_PULSE_UTC) == 0) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ return ("time mark not sync'ed to UTC");
+ }
+
+ return (NULL);
+}
+
+static char *
+jupiter_parse_gpos(struct instance *instance, u_short *sp)
+{
+ struct jgpos *jg;
+ time_t t;
+ struct tm *tm;
+ char *cp;
+
+ jg = (struct jgpos *)sp;
+
+ if (jg->navval != 0) {
+ /*
+ * Solution not valid. Use caution and refuse
+ * to determine GPS week from this message.
+ */
+ instance->gpos_gweek = 0;
+ instance->gpos_sweek = 0;
+ return ("Navigation solution not valid");
+ }
+
+ instance->gpos_gweek = jg->gweek;
+ instance->gpos_sweek = DS2UI(jg->sweek);
+ while(instance->gpos_sweek >= WEEKSECS) {
+ instance->gpos_sweek -= WEEKSECS;
+ ++instance->gpos_gweek;
+ }
+ instance->gweek = 0;
+
+ t = GPS_EPOCH + (instance->gpos_gweek * WEEKSECS) + instance->gpos_sweek;
+ tm = gmtime(&t);
+ cp = asctime(tm);
+
+ jupiter_debug(instance->peer,
+ "jupiter_parse_g", "GPS %.24s (gweek/sweek %u/%u)",
+ cp, instance->gpos_gweek, instance->gpos_sweek);
+ return (NULL);
+}
+
+/*
+ * jupiter_debug - print debug messages
+ */
+#if defined(__STDC__) || defined(SYS_WINNT)
+static void
+jupiter_debug(struct peer *peer, char *function, char *fmt, ...)
+#else
+static void
+jupiter_debug(peer, function, fmt, va_alist)
+ struct peer *peer;
+ char *function;
+ char *fmt;
+#endif /* __STDC__ */
+{
+ char buffer[200];
+ va_list ap;
+
+#if defined(__STDC__) || defined(SYS_WINNT)
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif /* __STDC__ */
+ /*
+ * Print debug message to stdout
+ * In the future, we may want to get get more creative...
+ */
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ record_clock_stats(&(peer->srcadr), buffer);
+ if (debug) {
+ fprintf(stdout, "%s: ", function);
+ fprintf(stdout, buffer);
+ fprintf(stdout, "\n");
+ fflush(stdout);
+ }
+
+ va_end(ap);
+}
+
+/* Checksum and transmit a message to the Jupiter */
+static char *
+jupiter_send(struct instance *instance, struct jheader *hp)
+{
+ u_int len, size;
+ int cc;
+ u_short *sp;
+ static char errstr[132];
+
+ size = sizeof(*hp);
+ hp->hsum = putshort(jupiter_cksum((u_short *)hp,
+ (size / sizeof(u_short)) - 1));
+ len = getshort(hp->len);
+ if (len > 0) {
+ sp = (u_short *)(hp + 1);
+ sp[len] = putshort(jupiter_cksum(sp, len));
+ size += (len + 1) * sizeof(u_short);
+ }
+
+ if ((cc = write(instance->peer->procptr->io.fd, (char *)hp, size)) < 0) {
+ (void)sprintf(errstr, "write: %s", strerror(errno));
+ return (errstr);
+ } else if (cc != size) {
+ (void)sprintf(errstr, "short write (%d != %d)", cc, size);
+ return (errstr);
+ }
+ return (NULL);
+}
+
+/* Request periodic message output */
+static struct {
+ struct jheader jheader;
+ struct jrequest jrequest;
+} reqmsg = {
+ { putshort(JUPITER_SYNC), 0,
+ putshort((sizeof(struct jrequest) / sizeof(u_short)) - 1),
+ 0, (u_char)putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK |
+ JUPITER_FLAG_CONN | JUPITER_FLAG_LOG), 0 },
+ { 0, 0, 0, 0 }
+};
+
+/* An interval of zero means to output on trigger */
+static void
+jupiter_reqmsg(struct instance *instance, u_int id,
+ u_int interval)
+{
+ struct jheader *hp;
+ struct jrequest *rp;
+ char *cp;
+
+ hp = &reqmsg.jheader;
+ hp->id = putshort(id);
+ rp = &reqmsg.jrequest;
+ rp->trigger = putshort(interval == 0);
+ rp->interval = putshort(interval);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_reqmsg", "%u: %s", id, cp);
+}
+
+/* Cancel periodic message output */
+static struct jheader canmsg = {
+ putshort(JUPITER_SYNC), 0, 0, 0,
+ (u_char)putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC),
+ 0
+};
+
+static void
+jupiter_canmsg(struct instance *instance, u_int id)
+{
+ struct jheader *hp;
+ char *cp;
+
+ hp = &canmsg;
+ hp->id = putshort(id);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_canmsg", "%u: %s", id, cp);
+}
+
+/* Request a single message output */
+static struct jheader reqonemsg = {
+ putshort(JUPITER_SYNC), 0, 0, 0,
+ (u_char)putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY),
+ 0
+};
+
+static void
+jupiter_reqonemsg(struct instance *instance, u_int id)
+{
+ struct jheader *hp;
+ char *cp;
+
+ hp = &reqonemsg;
+ hp->id = putshort(id);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_reqonemsg", "%u: %s", id, cp);
+}
+
+/* Set the platform dynamics */
+static struct {
+ struct jheader jheader;
+ struct jplat jplat;
+} platmsg = {
+ { putshort(JUPITER_SYNC), putshort(JUPITER_I_PLAT),
+ putshort((sizeof(struct jplat) / sizeof(u_short)) - 1), 0,
+ (u_char)putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK), 0 },
+ { 0, 0, 0 }
+};
+
+static void
+jupiter_platform(struct instance *instance, u_int platform)
+{
+ struct jheader *hp;
+ struct jplat *pp;
+ char *cp;
+
+ hp = &platmsg.jheader;
+ pp = &platmsg.jplat;
+ pp->platform = putshort(platform);
+ if ((cp = jupiter_send(instance, hp)) != NULL)
+ jupiter_debug(instance->peer, "jupiter_platform", "%u: %s", platform, cp);
+}
+
+/* Checksum "len" shorts */
+static u_short
+jupiter_cksum(u_short *sp, u_int len)
+{
+ u_short sum, x;
+
+ sum = 0;
+ while (len-- > 0) {
+ x = *sp++;
+ sum += getshort(x);
+ }
+ return (~sum + 1);
+}
+
+/* Return the size of the next message (or zero if we don't have it all yet) */
+static int
+jupiter_recv(struct instance *instance)
+{
+ int n, len, size, cc;
+ struct jheader *hp;
+ u_char *bp;
+ u_short *sp;
+
+ /* Must have at least a header's worth */
+ cc = sizeof(*hp);
+ size = instance->ssize;
+ if (size < cc)
+ return (0);
+
+ /* Search for the sync short if missing */
+ sp = instance->sbuf;
+ hp = (struct jheader *)sp;
+ if (getshort(hp->sync) != JUPITER_SYNC) {
+ /* Wasn't at the front, sync up */
+ jupiter_debug(instance->peer, "jupiter_recv", "syncing");
+ bp = (u_char *)sp;
+ n = size;
+ while (n >= 2) {
+ if (bp[0] != (JUPITER_SYNC & 0xff)) {
+ /*
+ jupiter_debug(instance->peer, "{0x%x}", bp[0]);
+ */
+ ++bp;
+ --n;
+ continue;
+ }
+ if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff))
+ break;
+ /*
+ jupiter_debug(instance->peer, "{0x%x 0x%x}", bp[0], bp[1]);
+ */
+ bp += 2;
+ n -= 2;
+ }
+ /*
+ jupiter_debug(instance->peer, "\n");
+ */
+ /* Shuffle data to front of input buffer */
+ if (n > 0)
+ memcpy(sp, bp, n);
+ size = n;
+ instance->ssize = size;
+ if (size < cc || hp->sync != JUPITER_SYNC)
+ return (0);
+ }
+
+ if (jupiter_cksum(sp, (cc / sizeof(u_short) - 1)) !=
+ getshort(hp->hsum)) {
+ jupiter_debug(instance->peer, "jupiter_recv", "bad header checksum!");
+ /* This is drastic but checksum errors should be rare */
+ instance->ssize = 0;
+ return (0);
+ }
+
+ /* Check for a payload */
+ len = getshort(hp->len);
+ if (len > 0) {
+ n = (len + 1) * sizeof(u_short);
+ /* Not enough data yet */
+ if (size < cc + n)
+ return (0);
+
+ /* Check payload checksum */
+ sp = (u_short *)(hp + 1);
+ if (jupiter_cksum(sp, len) != getshort(sp[len])) {
+ jupiter_debug(instance->peer,
+ "jupiter_recv", "bad payload checksum!");
+ /* This is drastic but checksum errors should be rare */
+ instance->ssize = 0;
+ return (0);
+ }
+ cc += n;
+ }
+ return (cc);
+}
+
+#else /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
+int refclock_jupiter_bs;
+#endif /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */
diff --git a/ntpd/refclock_leitch.c b/ntpd/refclock_leitch.c
new file mode 100644
index 0000000..d7cd9bb
--- /dev/null
+++ b/ntpd/refclock_leitch.c
@@ -0,0 +1,620 @@
+/*
+ * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_LEITCH)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef STREAM
+#include <stropts.h>
+#if defined(LEITCHCLK)
+#include <sys/clkdefs.h>
+#endif /* LEITCHCLK */
+#endif /* STREAM */
+
+#include "ntp_stdlib.h"
+
+
+/*
+ * Driver for Leitch CSD-5300 Master Clock System
+ *
+ * COMMANDS:
+ * DATE: D <CR>
+ * TIME: T <CR>
+ * STATUS: S <CR>
+ * LOOP: L <CR>
+ *
+ * FORMAT:
+ * DATE: YYMMDD<CR>
+ * TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/
+ * second bondaried on the stop bit of the <CR>
+ * second boundaries at '/' above.
+ * STATUS: G (good), D (diag fail), T (time not provided) or
+ * P (last phone update failed)
+ */
+#define MAXUNITS 1 /* max number of LEITCH units */
+#define LEITCHREFID "ATOM" /* reference id */
+#define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver"
+#define LEITCH232 "/dev/leitch%d" /* name of radio device */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#define leitch_send(A,M) \
+if (debug) fprintf(stderr,"write leitch %s\n",M); \
+if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
+ if (debug) \
+ fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
+ else \
+ msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
+
+#define STATE_IDLE 0
+#define STATE_DATE 1
+#define STATE_TIME1 2
+#define STATE_TIME2 3
+#define STATE_TIME3 4
+
+/*
+ * LEITCH unit control structure
+ */
+struct leitchunit {
+ struct peer *peer;
+ struct refclockio leitchio;
+ u_char unit;
+ short year;
+ short yearday;
+ short month;
+ short day;
+ short hour;
+ short second;
+ short minute;
+ short state;
+ u_short fudge1;
+ l_fp reftime1;
+ l_fp reftime2;
+ l_fp reftime3;
+ l_fp codetime1;
+ l_fp codetime2;
+ l_fp codetime3;
+ u_long yearstart;
+};
+
+/*
+ * Function prototypes
+ */
+static void leitch_init P((void));
+static int leitch_start P((int, struct peer *));
+static void leitch_shutdown P((int, struct peer *));
+static void leitch_poll P((int, struct peer *));
+static void leitch_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));
+#define leitch_buginfo noentry
+static void leitch_receive P((struct recvbuf *));
+static void leitch_process P((struct leitchunit *));
+#if 0
+static void leitch_timeout P((struct peer *));
+#endif
+static int leitch_get_date P((struct recvbuf *, struct leitchunit *));
+static int leitch_get_time P((struct recvbuf *, struct leitchunit *, int));
+static int days_per_year P((int));
+
+static struct leitchunit leitchunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_int32 refid[MAXUNITS];
+
+static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_leitch = {
+ leitch_start, leitch_shutdown, leitch_poll,
+ leitch_control, leitch_init, leitch_buginfo, NOFLAGS
+};
+
+/*
+ * leitch_init - initialize internal leitch driver data
+ */
+static void
+leitch_init(void)
+{
+ int i;
+
+ memset((char*)leitchunits, 0, sizeof(leitchunits));
+ memset((char*)unitinuse, 0, sizeof(unitinuse));
+ for (i = 0; i < MAXUNITS; i++)
+ memcpy((char *)&refid[i], LEITCHREFID, 4);
+}
+
+/*
+ * leitch_shutdown - shut down a LEITCH clock
+ */
+static void
+leitch_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_shutdown()\n");
+#endif
+}
+
+/*
+ * leitch_poll - called by the transmit procedure
+ */
+static void
+leitch_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct leitchunit *leitch;
+
+ /* start the state machine rolling */
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_poll()\n");
+#endif
+ if (unit > MAXUNITS) {
+ /* XXXX syslog it */
+ return;
+ }
+
+ leitch = &leitchunits[unit];
+
+ if (leitch->state != STATE_IDLE) {
+ /* reset and wait for next poll */
+ /* XXXX syslog it */
+ leitch->state = STATE_IDLE;
+ } else {
+ leitch_send(leitch,"D\r");
+ leitch->state = STATE_DATE;
+ }
+}
+
+static void
+leitch_control(
+ int unit,
+ struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *passed_peer
+ )
+{
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR,
+ "leitch_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in) {
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = (&leitchunits[unit])->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out) {
+ memset((char *)out, 0, sizeof (struct refclockstat));
+ out->type = REFCLK_ATOM_LEITCH;
+ out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2;
+ out->fudgeval1 = (int32)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];
+ out->p_lastcode = "";
+ out->clockdesc = LEITCH_DESCRIPTION;
+ }
+}
+
+/*
+ * leitch_start - open the LEITCH devices and initialize data for processing
+ */
+static int
+leitch_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct leitchunit *leitch;
+ int fd232;
+ char leitchdev[20];
+
+ /*
+ * Check configuration info.
+ */
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
+ return (0);
+ }
+
+ if (unitinuse[unit]) {
+ msyslog(LOG_ERR, "leitch_start: unit %d in use", unit);
+ return (0);
+ }
+
+ /*
+ * Open serial port.
+ */
+ (void) sprintf(leitchdev, LEITCH232, unit);
+ fd232 = open(leitchdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ msyslog(LOG_ERR,
+ "leitch_start: open of %s: %m", leitchdev);
+ return (0);
+ }
+
+ leitch = &leitchunits[unit];
+ memset((char*)leitch, 0, sizeof(*leitch));
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCGETA): %m", leitchdev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCSETA): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The LEITCHCLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ */
+ { struct termios ttyb, *ttyp;
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: tcgetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: tcsetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: tcflush(%s): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_TERMIOS */
+#ifdef STREAM
+#if defined(LEITCHCLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev);
+#endif /* LEITCHCLK */
+#endif /* STREAM */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The LEITCHCLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(LEITCHCLK)
+ int ldisc = CLKLDISC;
+#endif /* LEITCHCLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(LEITCHCLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* LEITCHCLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev);
+ goto screwed;
+ }
+#if defined(LEITCHCLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ msyslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCSETD): %m",leitchdev);
+ goto screwed;
+ }
+#endif /* LEITCHCLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Set up the structures
+ */
+ leitch->peer = peer;
+ leitch->unit = unit;
+ leitch->state = STATE_IDLE;
+ leitch->fudge1 = 15; /* 15ms */
+
+ leitch->leitchio.clock_recv = leitch_receive;
+ leitch->leitchio.srcclock = (caddr_t) leitch;
+ leitch->leitchio.datalen = 0;
+ leitch->leitchio.fd = fd232;
+ if (!io_addclock(&leitch->leitchio)) {
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+ return(1);
+
+ /*
+ * Something broke; abandon ship.
+ */
+ screwed:
+ close(fd232);
+ return(0);
+}
+
+/*
+ * leitch_receive - receive data from the serial interface on a leitch
+ * clock
+ */
+static void
+leitch_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock;
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_recieve(%*.*s)\n",
+ rbufp->recv_length, rbufp->recv_length,
+ rbufp->recv_buffer);
+#endif
+ if (rbufp->recv_length != 7)
+ return; /* The date is return with a trailing newline,
+ discard it. */
+
+ switch (leitch->state) {
+ case STATE_IDLE: /* unexpected, discard and resync */
+ return;
+ case STATE_DATE:
+ if (!leitch_get_date(rbufp,leitch)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+ leitch_send(leitch,"T\r");
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n",leitch->yearday);
+#endif
+ leitch->state = STATE_TIME1;
+ break;
+ case STATE_TIME1:
+ if (!leitch_get_time(rbufp,leitch,1)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 1, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime1.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+ leitch->reftime1.l_uf = 0;
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%lu\n", (u_long)leitch->reftime1.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf);
+ leitch->codetime1 = rbufp->recv_time;
+ leitch->state = STATE_TIME2;
+ break;
+ case STATE_TIME2:
+ if (!leitch_get_time(rbufp,leitch,2)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 1, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime2.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%lu\n", (u_long)leitch->reftime2.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf);
+ leitch->codetime2 = rbufp->recv_time;
+ leitch->state = STATE_TIME3;
+ break;
+ case STATE_TIME3:
+ if (!leitch_get_time(rbufp,leitch,3)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, GMT, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime3.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%lu\n", (u_long)leitch->reftime3.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf);
+ leitch->codetime3 = rbufp->recv_time;
+ leitch_process(leitch);
+ leitch->state = STATE_IDLE;
+ break;
+ default:
+ msyslog(LOG_ERR,
+ "leitech_receive: invalid state %d unit %d",
+ leitch->state, leitch->unit);
+ }
+}
+
+/*
+ * leitch_process - process a pile of samples from the clock
+ *
+ * This routine uses a three-stage median filter to calculate offset and
+ * dispersion. reduce jitter. The dispersion is calculated as the span
+ * of the filter (max - min), unless the quality character (format 2) is
+ * non-blank, in which case the dispersion is calculated on the basis of
+ * the inherent tolerance of the internal radio oscillator, which is
+ * +-2e-5 according to the radio specifications.
+ */
+static void
+leitch_process(
+ struct leitchunit *leitch
+ )
+{
+ l_fp off;
+ l_fp tmp_fp;
+ /*double doffset;*/
+
+ off = leitch->reftime1;
+ L_SUB(&off,&leitch->codetime1);
+ tmp_fp = leitch->reftime2;
+ L_SUB(&tmp_fp,&leitch->codetime2);
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+ tmp_fp = leitch->reftime3;
+ L_SUB(&tmp_fp,&leitch->codetime3);
+
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+ /*LFPTOD(&off, doffset);*/
+ refclock_receive(leitch->peer);
+}
+
+/*
+ * days_per_year
+ */
+static int
+days_per_year(
+ int year
+ )
+{
+ if (year%4) { /* not a potential leap year */
+ return (365);
+ } else {
+ if (year % 100) { /* is a leap year */
+ return (366);
+ } else {
+ if (year % 400) {
+ return (365);
+ } else {
+ return (366);
+ }
+ }
+ }
+}
+
+static int
+leitch_get_date(
+ struct recvbuf *rbufp,
+ struct leitchunit *leitch
+ )
+{
+ int i;
+
+ if (rbufp->recv_length < 6)
+ return(0);
+#undef BAD /* confict: defined as (-1) in AIX sys/param.h */
+#define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+#define ATOB(A) ((rbufp->recv_buffer[A])-'0')
+ leitch->year = ATOB(0)*10 + ATOB(1);
+ leitch->month = ATOB(2)*10 + ATOB(3);
+ leitch->day = ATOB(4)*10 + ATOB(5);
+
+ /* sanity checks */
+ if (leitch->month > 12)
+ return(0);
+ if (leitch->day > days_in_month[leitch->month-1])
+ return(0);
+
+ /* calculate yearday */
+ i = 0;
+ leitch->yearday = leitch->day;
+
+ while ( i < (leitch->month-1) )
+ leitch->yearday += days_in_month[i++];
+
+ if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) &&
+ leitch->month > 2)
+ leitch->yearday--;
+
+ return(1);
+}
+
+/*
+ * leitch_get_time
+ */
+static int
+leitch_get_time(
+ struct recvbuf *rbufp,
+ struct leitchunit *leitch,
+ int which
+ )
+{
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+ leitch->hour = ATOB(0)*10 +ATOB(1);
+ leitch->minute = ATOB(2)*10 +ATOB(3);
+ leitch->second = ATOB(4)*10 +ATOB(5);
+
+ if ((leitch->hour > 23) || (leitch->minute > 60) ||
+ (leitch->second > 60))
+ return(0);
+ return(1);
+}
+
+#else
+int refclock_leitch_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_local.c b/ntpd/refclock_local.c
new file mode 100644
index 0000000..3478f43
--- /dev/null
+++ b/ntpd/refclock_local.c
@@ -0,0 +1,264 @@
+
+/*
+ * refclock_local - local pseudo-clock driver
+ *
+ * wjm 17-aug-1995: add a hook for special treatment of VMS_LOCALUNIT
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef REFCLOCK
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef KERNEL_PLL
+#include "ntp_syscall.h"
+#endif
+
+/*
+ * This is a hack to allow a machine to use its own system clock as a
+ * reference clock, i.e., to free-run using no outside clock discipline
+ * source. This is useful if you want to use NTP in an isolated
+ * environment with no radio clock or NIST modem available. Pick a
+ * machine that you figure has a good clock oscillator and configure it
+ * with this driver. Set the clock using the best means available, like
+ * eyeball-and-wristwatch. Then, point all the other machines at this
+ * one or use broadcast (not multicast) mode to distribute time.
+ *
+ * Another application for this driver is if you want to use a
+ * particular server's clock as the clock of last resort when all other
+ * normal synchronization sources have gone away. This is especially
+ * useful if that server has an ovenized oscillator. For this you would
+ * configure this driver at a higher stratum (say 5) to prevent the
+ * server's stratum from falling below that.
+ *
+ * A third application for this driver is when an external discipline
+ * source is available, such as the NIST "lockclock" program, which
+ * synchronizes the local clock via a telephone modem and the NIST
+ * Automated Computer Time Service (ACTS), or the Digital Time
+ * Synchronization Service (DTSS), which runs on DCE machines. In this
+ * case the stratum should be set at zero, indicating a bona fide
+ * stratum-1 source. Exercise some caution with this, since there is no
+ * easy way to telegraph via NTP that something might be wrong in the
+ * discipline source itself. In the case of DTSS, the local clock can
+ * have a rather large jitter, depending on the interval between
+ * corrections and the intrinsic frequency error of the clock
+ * oscillator. In extreme cases, this can cause clients to exceed the
+ * 128-ms slew window and drop off the NTP subnet.
+ *
+ * THis driver includes provisions to telegraph synchronization state
+ * and related variables by means of kernel variables with specially
+ * modified kernels. This is done using the ntp_adjtime() syscall.
+ * In the cases where another protocol or device synchronizes the local
+ * host, the data given to the kernel can be slurped up by this driver
+ * and distributed to clients by ordinary NTP messaging.
+ *
+ * In the default mode the behavior of the clock selection algorithm is
+ * modified when this driver is in use. The algorithm is designed so
+ * that this driver will never be selected unless no other discipline
+ * source is available. This can be overriden with the prefer keyword of
+ * the server configuration command, in which case only this driver will
+ * be selected for synchronization and all other discipline sources will
+ * be ignored. This behavior is intended for use when an external
+ * discipline source controls the system clock.
+ *
+ * Fudge Factors
+ *
+ * The stratum for this driver set at 5 by default, but it can be
+ * changed by the fudge command and/or the ntpdc utility. The reference
+ * ID is "LCL" by default, but can be changed using the same mechanism.
+ * *NEVER* configure this driver to operate at a stratum which might
+ * possibly disrupt a client with access to a bona fide primary server,
+ * unless the local clock oscillator is reliably disciplined by another
+ * source. *NEVER NEVER* configure a server which might devolve to an
+ * undisciplined local clock to use multicast mode. Always remember that
+ * an improperly configured local clock driver let loose in the Internet
+ * can cause very serious disruption. This is why most of us who care
+ * about good time use cryptographic authentication.
+ *
+ * This driver provides a mechanism to trim the local clock in both time
+ * and frequency, as well as a way to manipulate the leap bits. The
+ * fudge time1 parameter adjusts the time, in seconds, and the fudge
+ * time2 parameter adjusts the frequency, in ppm. The fudge time1
+ * parameter is additive; that is, it adds an increment to the current
+ * time. The fudge time2 parameter directly sets the frequency.
+ */
+/*
+ * Local interface definitions
+ */
+#define PRECISION (-7) /* about 10 ms precision */
+#if defined(VMS) && defined(VMS_LOCALUNIT)
+#define REFID "LCLv" /* reference ID */
+#else /* VMS VMS_LOCALUNIT */
+#define REFID "LCL\0" /* reference ID */
+#endif /* VMS VMS_LOCALUNIT */
+#define DESCRIPTION "Undisciplined local clock" /* WRU */
+
+#define STRATUM 5 /* default stratum */
+#define DISPERSION .01 /* default dispersion (10 ms) */
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * Imported from ntp_proto
+ */
+extern s_char sys_precision;
+
+#ifdef KERNEL_PLL
+/*
+ * Imported from ntp_loopfilter
+ */
+extern int pll_control; /* kernel pll control */
+extern int kern_enable; /* kernel pll enabled */
+extern int ext_enable; /* external clock enable */
+#endif /* KERNEL_PLL */
+
+/*
+ * Function prototypes
+ */
+static int local_start P((int, struct peer *));
+static void local_poll P((int, struct peer *));
+
+/*
+ * Local variables
+ */
+static u_long poll_time; /* last time polled */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_local = {
+ local_start, /* start up driver */
+ noentry, /* shut down driver (not used) */
+ local_poll, /* transmit poll message */
+ noentry, /* not used (old lcl_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old lcl_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * local_start - start up the clock
+ */
+static int
+local_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = sys_precision;
+ pp->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM;
+ pp->stratum = STRATUM;
+ pp->clockdesc = DESCRIPTION;
+ memcpy(&pp->refid, "INIT", 4);
+ poll_time = current_time;
+ return (1);
+}
+
+
+/*
+ * local_poll - called by the transmit procedure
+ *
+ * LOCKCLOCK: If the kernel supports the nanokernel or microkernel
+ * system calls, the leap bits are extracted from the kernel. If there
+ * is a kernel error or the kernel leap bits are set to 11, the NTP leap
+ * bits are set to 11 and the stratum is set to infinity. Otherwise, the
+ * NTP leap bits are set to the kernel leap bits and the stratum is set
+ * as fudged. This behavior does not faithfully follow the
+ * specification, but is probably more appropriate in a multiple-server
+ * national laboratory network.
+ */
+static void
+local_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+#if defined(KERNEL_PLL) && defined(LOCKCLOCK)
+ struct timex ntv;
+#endif /* KERNEL_PLL LOCKCLOCK */
+ struct refclockproc *pp;
+
+#if defined(VMS) && defined(VMS_LOCALUNIT)
+ if (unit == VMS_LOCALUNIT) {
+ extern void vms_local_poll(struct peer *);
+
+ vms_local_poll(peer);
+ return;
+ }
+#endif /* VMS && VMS_LOCALUNIT */
+ pp = peer->procptr;
+ pp->polls++;
+
+ /*
+ * Ramble through the usual filtering and grooming code, which
+ * is essentially a no-op and included mostly for pretty
+ * billboards. We allow a one-time time adjustment using fudge
+ * time1 (s) and a continuous frequency adjustment using fudge
+ * time 2 (ppm).
+ */
+ get_systime(&pp->lastrec);
+ pp->fudgetime1 += pp->fudgetime2 * 1e-6 * (current_time -
+ poll_time);
+ poll_time = current_time;
+ refclock_process_offset(pp, pp->lastrec, pp->lastrec,
+ pp->fudgetime1);
+
+ /*
+ * If another process is disciplining the system clock, we set
+ * the leap bits and quality indicators from the kernel.
+ */
+#if defined(KERNEL_PLL) && defined(LOCKCLOCK)
+ memset(&ntv, 0, sizeof ntv);
+ switch (ntp_adjtime(&ntv)) {
+ case TIME_OK:
+ pp->leap = LEAP_NOWARNING;
+ peer->stratum = pp->stratum;
+ break;
+
+ case TIME_INS:
+ pp->leap = LEAP_ADDSECOND;
+ peer->stratum = pp->stratum;
+ break;
+
+ case TIME_DEL:
+ pp->leap = LEAP_DELSECOND;
+ peer->stratum = pp->stratum;
+ break;
+
+ default:
+ pp->leap = LEAP_NOTINSYNC;
+ peer->stratum = STRATUM_UNSPEC;
+ }
+ pp->disp = 0;
+ pp->jitter = 0;
+#else /* KERNEL_PLL LOCKCLOCK */
+ pp->leap = LEAP_NOWARNING;
+ pp->disp = DISPERSION;
+ pp->jitter = 0;
+#endif /* KERNEL_PLL LOCKCLOCK */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ pp->fudgetime1 = 0;
+}
+#else
+int refclock_local_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_msfees.c b/ntpd/refclock_msfees.c
new file mode 100644
index 0000000..ebfb983
--- /dev/null
+++ b/ntpd/refclock_msfees.c
@@ -0,0 +1,1455 @@
+/* refclock_ees - clock driver for the EES M201 receiver */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_MSFEES) && defined(PPS)
+
+/* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes
+ * were removed as the code was overly hairy, they weren't in use
+ * (hence probably didn't work). Still in RCS file at cl.cam.ac.uk
+ */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include <ctype.h>
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#endif
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+#include "ntp_stdlib.h"
+
+/*
+ fudgefactor = fudgetime1;
+ os_delay = fudgetime2;
+ offset_fudge = os_delay + fudgefactor + inherent_delay;
+ stratumtouse = fudgeval1 & 0xf
+ debug = fudgeval2;
+ sloppyclockflag = flags & CLK_FLAG1;
+ 1 log smoothing summary when processing sample
+ 4 dump the buffer from the clock
+ 8 EIOGETKD the last n uS time stamps
+ if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0;
+ ees->dump_vals = flags & CLK_FLAG3;
+ ees->usealldata = flags & CLK_FLAG4;
+
+
+ bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
+ bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
+ bug->values[2] = (u_long)ees->status;
+ bug->values[3] = (u_long)ees->lastevent;
+ bug->values[4] = (u_long)ees->reason;
+ bug->values[5] = (u_long)ees->nsamples;
+ bug->values[6] = (u_long)ees->codestate;
+ bug->values[7] = (u_long)ees->day;
+ bug->values[8] = (u_long)ees->hour;
+ bug->values[9] = (u_long)ees->minute;
+ bug->values[10] = (u_long)ees->second;
+ bug->values[11] = (u_long)ees->tz;
+ bug->values[12] = ees->yearstart;
+ bug->values[13] = (ees->leaphold > current_time) ?
+ ees->leaphold - current_time : 0;
+ bug->values[14] = inherent_delay[unit].l_uf;
+ bug->values[15] = offset_fudge[unit].l_uf;
+
+ bug->times[0] = ees->reftime;
+ bug->times[1] = ees->arrvtime;
+ bug->times[2] = ees->lastsampletime;
+ bug->times[3] = ees->offset;
+ bug->times[4] = ees->lowoffset;
+ bug->times[5] = ees->highoffset;
+ bug->times[6] = inherent_delay[unit];
+ bug->times[8] = os_delay[unit];
+ bug->times[7] = fudgefactor[unit];
+ bug->times[9] = offset_fudge[unit];
+ bug->times[10]= ees->yearstart, 0;
+ */
+
+/* This should support the use of an EES M201 receiver with RS232
+ * output (modified to transmit time once per second).
+ *
+ * For the format of the message sent by the clock, see the EESM_
+ * definitions below.
+ *
+ * It appears to run free for an integral number of minutes, until the error
+ * reaches 4mS, at which point it steps at second = 01.
+ * It appears that sometimes it steps 4mS (say at 7 min interval),
+ * then the next minute it decides that it was an error, so steps back.
+ * On the next minute it steps forward again :-(
+ * This is typically 16.5uS/S then 3975uS at the 4min re-sync,
+ * or 9.5uS/S then 3990.5uS at a 7min re-sync,
+ * at which point it may lose the "00" second time stamp.
+ * I assume that the most accurate time is just AFTER the re-sync.
+ * Hence remember the last cycle interval,
+ *
+ * Can run in any one of:
+ *
+ * PPSCD PPS signal sets CD which interupts, and grabs the current TOD
+ * (sun) *in the interupt code*, so as to avoid problems with
+ * the STREAMS scheduling.
+ *
+ * It appears that it goes 16.5 uS slow each second, then every 4 mins it
+ * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7)
+ */
+
+/* Definitions */
+#ifndef MAXUNITS
+#define MAXUNITS 4 /* maximum number of EES units permitted */
+#endif
+
+#ifndef EES232
+#define EES232 "/dev/ees%d" /* Device to open to read the data */
+#endif
+
+/* Other constant stuff */
+#ifndef EESPRECISION
+#define EESPRECISION (-10) /* what the heck - 2**-10 = 1ms */
+#endif
+#ifndef EESREFID
+#define EESREFID "MSF\0" /* String to identify the clock */
+#endif
+#ifndef EESHSREFID
+#define EESHSREFID (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */
+#endif
+
+/* Description of clock */
+#define EESDESCRIPTION "EES M201 MSF Receiver"
+
+/* Speed we run the clock port at. If this is changed the UARTDELAY
+ * value should be recomputed to suit.
+ */
+#ifndef SPEED232
+#define SPEED232 B9600 /* 9600 baud */
+#endif
+
+/* What is the inherent delay for this mode of working, i.e. when is the
+ * data time stamped.
+ */
+#define SAFETY_SHIFT 10 /* Split the shift to avoid overflow */
+#define BITS_TO_L_FP(bits, baud) \
+(((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT)
+#define INH_DELAY_CBREAK BITS_TO_L_FP(119, 9600)
+#define INH_DELAY_PPS BITS_TO_L_FP( 0, 9600)
+
+#ifndef STREAM_PP1
+#define STREAM_PP1 "ppsclocd\0<-- patch space for module name1 -->"
+#endif
+#ifndef STREAM_PP2
+#define STREAM_PP2 "ppsclock\0<-- patch space for module name2 -->"
+#endif
+
+ /* Offsets of the bytes of the serial line code. The clock gives
+ * local time with a GMT/BST indication. The EESM_ definitions
+ * give offsets into ees->lastcode.
+ */
+#define EESM_CSEC 0 /* centiseconds - always zero in our clock */
+#define EESM_SEC 1 /* seconds in BCD */
+#define EESM_MIN 2 /* minutes in BCD */
+#define EESM_HOUR 3 /* hours in BCD */
+#define EESM_DAYWK 4 /* day of week (Sun = 0 etc) */
+#define EESM_DAY 5 /* day of month in BCD */
+#define EESM_MON 6 /* month in BCD */
+#define EESM_YEAR 7 /* year MOD 100 in BCD */
+#define EESM_LEAP 8 /* 0x0f if leap year, otherwise zero */
+#define EESM_BST 9 /* 0x03 if BST, 0x00 if GMT */
+#define EESM_MSFOK 10 /* 0x3f if radio good, otherwise zero */
+ /* followed by a frame alignment byte (0xff) /
+ / which is not put into the lastcode buffer*/
+
+/* Length of the serial time code, in characters. The first length
+ * is less the frame alignment byte.
+ */
+#define LENEESPRT (EESM_MSFOK+1)
+#define LENEESCODE (LENEESPRT+1)
+
+ /* Code state. */
+#define EESCS_WAIT 0 /* waiting for start of timecode */
+#define EESCS_GOTSOME 1 /* have an incomplete time code buffered */
+
+ /* Default fudge factor and character to receive */
+#define DEFFUDGETIME 0 /* Default user supplied fudge factor */
+#ifndef DEFOSTIME
+#define DEFOSTIME 0 /* Default OS delay -- passed by Make ? */
+#endif
+#define DEFINHTIME INH_DELAY_PPS /* inherent delay due to sample point*/
+
+ /* Limits on things. Reduce the number of samples to SAMPLEREDUCE by median
+ * elimination. If we're running with an accurate clock, chose the BESTSAMPLE
+ * as the estimated offset, otherwise average the remainder.
+ */
+#define FULLSHIFT 6 /* NCODES root 2 */
+#define NCODES (1<< FULLSHIFT) /* 64 */
+#define REDUCESHIFT (FULLSHIFT -1) /* SAMPLEREDUCE root 2 */
+
+ /* Towards the high ( Why ?) end of half */
+#define BESTSAMPLE ((samplereduce * 3) /4) /* 24 */
+
+ /* Leap hold time. After a leap second the clock will no longer be
+ * reliable until it resynchronizes. Hope 40 minutes is enough. */
+#define EESLEAPHOLD (40 * 60)
+
+#define EES_STEP_F (1 << 24) /* the receiver steps in units of about 4ms */
+#define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/
+#define EES_STEP_NOTE (1 << 21)/* Log any unexpected jumps, say .5 ms .... */
+#define EES_STEP_NOTES 50 /* Only do a limited number */
+#define MAX_STEP 16 /* Max number of steps to remember */
+
+ /* debug is a bit mask of debugging that is wanted */
+#define DB_SYSLOG_SMPLI 0x0001
+#define DB_SYSLOG_SMPLE 0x0002
+#define DB_SYSLOG_SMTHI 0x0004
+#define DB_SYSLOG_NSMTHE 0x0008
+#define DB_SYSLOG_NSMTHI 0x0010
+#define DB_SYSLOG_SMTHE 0x0020
+#define DB_PRINT_EV 0x0040
+#define DB_PRINT_CDT 0x0080
+#define DB_PRINT_CDTC 0x0100
+#define DB_SYSLOG_KEEPD 0x0800
+#define DB_SYSLOG_KEEPE 0x1000
+#define DB_LOG_DELTAS 0x2000
+#define DB_PRINT_DELTAS 0x4000
+#define DB_LOG_AWAITMORE 0x8000
+#define DB_LOG_SAMPLES 0x10000
+#define DB_NO_PPS 0x20000
+#define DB_INC_PPS 0x40000
+#define DB_DUMP_DELTAS 0x80000
+
+ struct eesunit { /* EES unit control structure. */
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ l_fp reftime; /* reference time */
+ l_fp lastsampletime; /* time as in txt from last EES msg */
+ l_fp arrvtime; /* Time at which pkt arrived */
+ l_fp codeoffsets[NCODES]; /* the time of arrival of 232 codes */
+ l_fp offset; /* chosen offset (for clkbug) */
+ l_fp lowoffset; /* lowest sample offset (for clkbug) */
+ l_fp highoffset; /* highest " " (for clkbug) */
+ char lastcode[LENEESCODE+6]; /* last time code we received */
+ u_long lasttime; /* last time clock heard from */
+ u_long clocklastgood; /* last time good radio seen */
+ u_char lencode; /* length of code in buffer */
+ u_char nsamples; /* number of samples we've collected */
+ u_char codestate; /* state of 232 code reception */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last abort */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ char tz; /* timezone from clock */
+ u_char ttytype; /* method used */
+ u_char dump_vals; /* Should clock values be dumped */
+ u_char usealldata; /* Use ALL samples */
+ u_short day; /* day of year from last code */
+ u_long yearstart; /* start of current year */
+ u_long leaphold; /* time of leap hold expiry */
+ u_long badformat; /* number of bad format codes */
+ u_long baddata; /* number of invalid time codes */
+ u_long timestarted; /* time we started this */
+ long last_pps_no; /* The serial # of the last PPS */
+ char fix_pending; /* Is a "sync to time" pending ? */
+ /* Fine tuning - compensate for 4 mS ramping .... */
+ l_fp last_l; /* last time stamp */
+ u_char last_steps[MAX_STEP]; /* Most recent n steps */
+ int best_av_step; /* Best guess at average step */
+ char best_av_step_count; /* # of steps over used above */
+ char this_step; /* Current pos in buffer */
+ int last_step_late; /* How late the last step was (0-59) */
+ long jump_fsecs; /* # of fractions of a sec last jump */
+ u_long last_step; /* time of last step */
+ int last_step_secs; /* Number of seconds in last step */
+ int using_ramp; /* 1 -> noemal, -1 -> over stepped */
+ };
+#define last_sec last_l.l_ui
+#define last_sfsec last_l.l_f
+#define this_uisec ((ees->arrvtime).l_ui)
+#define this_sfsec ((ees->arrvtime).l_f)
+#define msec(x) ((x) / (1<<22))
+#define LAST_STEPS (sizeof ees->last_steps / sizeof ees->last_steps[0])
+#define subms(x) ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5)))
+
+/* Bitmask for what methods to try to use -- currently only PPS enabled */
+#define T_CBREAK 1
+#define T_PPS 8
+/* macros to test above */
+#define is_cbreak(x) ((x)->ttytype & T_CBREAK)
+#define is_pps(x) ((x)->ttytype & T_PPS)
+#define is_any(x) ((x)->ttytype)
+
+#define CODEREASON 20 /* reason codes */
+
+/* Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back. */
+static struct eesunit *eesunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/* Keep the fudge factors separately so they can be set even
+ * when no clock is configured. */
+static l_fp inherent_delay[MAXUNITS]; /* when time stamp is taken */
+static l_fp fudgefactor[MAXUNITS]; /* fudgetime1 */
+static l_fp os_delay[MAXUNITS]; /* fudgetime2 */
+static l_fp offset_fudge[MAXUNITS]; /* Sum of above */
+static u_char stratumtouse[MAXUNITS];
+static u_char sloppyclockflag[MAXUNITS];
+
+static int deltas[60];
+
+static l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */
+static l_fp onesec; /* = { 1, 0 }; */
+
+#ifndef DUMP_BUF_SIZE /* Size of buffer to be used by dump_buf */
+#define DUMP_BUF_SIZE 10112
+#endif
+
+/* ees_reset - reset the count back to zero */
+#define ees_reset(ees) (ees)->nsamples = 0; \
+(ees)->codestate = EESCS_WAIT
+
+/* ees_event - record and report an event */
+#define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \
+ees_report_event((ees), (evcode))
+
+ /* Find the precision of the system clock by reading it */
+#define USECS 1000000
+#define MINSTEP 5 /* some systems increment uS on each call */
+#define MAXLOOPS (USECS/9)
+
+/*
+ * Function prototypes
+ */
+
+static int msfees_start P((int unit, struct peer *peer));
+static void msfees_shutdown P((int unit, struct peer *peer));
+static void msfees_poll P((int unit, struct peer *peer));
+static void msfees_init P((void));
+static void dump_buf P((l_fp *coffs, int from, int to, char *text));
+static void ees_report_event P((struct eesunit *ees, int code));
+static void ees_receive P((struct recvbuf *rbufp));
+static void ees_process P((struct eesunit *ees));
+#ifdef QSORT_USES_VOID_P
+static int offcompare P((const void *va, const void *vb));
+#else
+static int offcompare P((const l_fp *a, const l_fp *b));
+#endif /* QSORT_USES_VOID_P */
+
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_msfees = {
+ msfees_start, /* start up driver */
+ msfees_shutdown, /* shut down driver */
+ msfees_poll, /* transmit poll message */
+ noentry, /* not used */
+ msfees_init, /* initialize driver */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+static void
+dump_buf(
+ l_fp *coffs,
+ int from,
+ int to,
+ char *text
+ )
+{
+ char buff[DUMP_BUF_SIZE + 80];
+ int i;
+ register char *ptr = buff;
+
+ sprintf(ptr, text);
+ for (i=from; i<to; i++)
+ { while (*ptr) ptr++;
+ if ((ptr-buff) > DUMP_BUF_SIZE) msyslog(LOG_DEBUG, "D: %s", ptr=buff);
+ sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295);
+ }
+ msyslog(LOG_DEBUG, "D: %s", buff);
+}
+
+/* msfees_init - initialize internal ees driver data */
+static void
+msfees_init(void)
+{
+ register int i;
+ /* Just zero the data arrays */
+ memset((char *)eesunits, 0, sizeof eesunits);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ acceptable_slop.l_ui = 0;
+ acceptable_slop.l_uf = 1 << (FRACTION_PREC -2);
+
+ onesec.l_ui = 1;
+ onesec.l_uf = 0;
+
+ /* Initialize fudge factors to default. */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor[i].l_ui = 0;
+ fudgefactor[i].l_uf = DEFFUDGETIME;
+ os_delay[i].l_ui = 0;
+ os_delay[i].l_uf = DEFOSTIME;
+ inherent_delay[i].l_ui = 0;
+ inherent_delay[i].l_uf = DEFINHTIME;
+ offset_fudge[i] = os_delay[i];
+ L_ADD(&offset_fudge[i], &fudgefactor[i]);
+ L_ADD(&offset_fudge[i], &inherent_delay[i]);
+ stratumtouse[i] = 0;
+ sloppyclockflag[i] = 0;
+ }
+}
+
+
+/* msfees_start - open the EES devices and initialize data for processing */
+static int
+msfees_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct eesunit *ees;
+ register int i;
+ int fd232 = -1;
+ char eesdev[20];
+ struct termios ttyb, *ttyp;
+ struct refclockproc *pp;
+ pp = peer->procptr;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)",
+ unit, MAXUNITS-1);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ msyslog(LOG_ERR, "ees clock: unit number %d in use", unit);
+ return 0;
+ }
+
+ /* Unit okay, attempt to open the devices. We do them both at
+ * once to make sure we can */
+ (void) sprintf(eesdev, EES232, unit);
+
+ fd232 = open(eesdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ msyslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev);
+ return 0;
+ }
+
+#ifdef TIOCEXCL
+ /* Set for exclusive use */
+ if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
+ msyslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev);
+ goto screwed;
+ }
+#endif
+
+ /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ /* Set port characteristics. If we don't have a STREAMS module or
+ * a clock line discipline, cooked mode is just usable, even though it
+ * strips the top bit. The only EES byte which uses the top
+ * bit is the year, and we don't use that anyway. If we do
+ * have the line discipline, we choose raw mode, and the
+ * line discipline code will block up the messages.
+ */
+
+ /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ msyslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_oflag = 0;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ msyslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ msyslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ inherent_delay[unit].l_uf = INH_DELAY_PPS;
+
+ /* offset fudge (how *late* the timestamp is) = fudge + os delays */
+ offset_fudge[unit] = os_delay[unit];
+ L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
+ L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
+
+ /* Looks like this might succeed. Find memory for the structure.
+ * Look to see if there are any unused ones, if not we malloc() one.
+ */
+ if (eesunits[unit] != 0) /* The one we want is okay */
+ ees = eesunits[unit];
+ else {
+ /* Look for an unused, but allocated struct */
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && eesunits[i] != 0)
+ break;
+ }
+
+ if (i < MAXUNITS) { /* Reclaim this one */
+ ees = eesunits[i];
+ eesunits[i] = 0;
+ } /* no spare -- make a new one */
+ else ees = (struct eesunit *) emalloc(sizeof(struct eesunit));
+ }
+ memset((char *)ees, 0, sizeof(struct eesunit));
+ eesunits[unit] = ees;
+
+ /* Set up the structures */
+ ees->peer = peer;
+ ees->unit = (u_char)unit;
+ ees->timestarted= current_time;
+ ees->ttytype = 0;
+ ees->io.clock_recv= ees_receive;
+ ees->io.srcclock= (caddr_t)ees;
+ ees->io.datalen = 0;
+ ees->io.fd = fd232;
+
+ /* Okay. Push one of the two (linked into the kernel, or dynamically
+ * loaded) STREAMS module, and give it to the I/O code to start
+ * receiving stuff.
+ */
+
+#ifdef STREAM
+ {
+ int rc1;
+ /* Pop any existing onews first ... */
+ while (ioctl(fd232, I_POP, 0 ) >= 0) ;
+
+ /* Now try pushing either of the possible modules */
+ if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 &&
+ ioctl(fd232, I_PUSH, STREAM_PP2) < 0) {
+ msyslog(LOG_ERR,
+ "ees clock: Push of `%s' and `%s' to %s failed %m",
+ STREAM_PP1, STREAM_PP2, eesdev);
+ goto screwed;
+ }
+ else {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "I: ees clock: PUSHed %s on %s",
+ (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev);
+ ees->ttytype |= T_PPS;
+ }
+ }
+#endif /* STREAM */
+
+ /* Add the clock */
+ if (!io_addclock(&ees->io)) {
+ /* Oh shit. Just close and return. */
+ msyslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev);
+ goto screwed;
+ }
+
+
+ /* All done. Initialize a few random peer variables, then
+ * return success. */
+ peer->precision = sys_precision;
+ peer->stratum = stratumtouse[unit];
+ if (stratumtouse[unit] <= 1) {
+ memcpy((char *)&pp->refid, EESREFID, 4);
+ if (unit > 0 && unit < 10)
+ ((char *)&pp->refid)[3] = '0' + unit;
+ } else {
+ peer->refid = htonl(EESHSREFID);
+ }
+ unitinuse[unit] = 1;
+ pp->unitptr = (caddr_t) &eesunits[unit];
+ pp->clockdesc = EESDESCRIPTION;
+ msyslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit);
+ return (1);
+
+ screwed:
+ if (fd232 != -1)
+ (void) close(fd232);
+ return (0);
+}
+
+
+/* msfees_shutdown - shut down a EES clock */
+static void
+msfees_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct eesunit *ees;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
+ unit, MAXUNITS);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ msyslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d not in use", unit);
+ return;
+ }
+
+ /* Tell the I/O module to turn us off. We're history. */
+ ees = eesunits[unit];
+ io_closeclock(&ees->io);
+ unitinuse[unit] = 0;
+}
+
+
+/* ees_report_event - note the occurance of an event */
+static void
+ees_report_event(
+ struct eesunit *ees,
+ int code
+ )
+{
+ if (ees->status != (u_char)code) {
+ ees->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ ees->lastevent = (u_char)code;
+ /* Should report event to trap handler in here.
+ * Soon...
+ */
+ }
+}
+
+
+/* ees_receive - receive data from the serial interface on an EES clock */
+static void
+ees_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register int n_sample;
+ register int day;
+ register struct eesunit *ees;
+ register u_char *dpt; /* Data PoinTeR: move along ... */
+ register u_char *dpend; /* Points just *after* last data char */
+ register char *cp;
+ l_fp tmp;
+ int call_pps_sample = 0;
+ l_fp pps_arrvstamp;
+ int sincelast;
+ int pps_step = 0;
+ int suspect_4ms_step = 0;
+ struct ppsclockev ppsclockev;
+ long *ptr = (long *) &ppsclockev;
+ int rc;
+ int request;
+#ifdef HAVE_CIOGETEV
+ request = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ request = TIOCGPPSEV;
+#endif
+
+ /* Get the clock this applies to and a pointer to the data */
+ ees = (struct eesunit *)rbufp->recv_srcclock;
+ dpt = (u_char *)&rbufp->recv_space;
+ dpend = dpt + rbufp->recv_length;
+ if ((debug & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE))
+ printf("[%d] ", rbufp->recv_length);
+
+ /* Check out our state and process appropriately */
+ switch (ees->codestate) {
+ case EESCS_WAIT:
+ /* Set an initial guess at the timestamp as the recv time.
+ * If just running in CBREAK mode, we can't improve this.
+ * If we have the CLOCK Line Discipline, PPSCD, or sime such,
+ * then we will do better later ....
+ */
+ ees->arrvtime = rbufp->recv_time;
+ ees->codestate = EESCS_GOTSOME;
+ ees->lencode = 0;
+ /*FALLSTHROUGH*/
+
+ case EESCS_GOTSOME:
+ cp = &(ees->lastcode[ees->lencode]);
+
+ /* Gobble the bytes until the final (possibly stripped) 0xff */
+ while (dpt < dpend && (*dpt & 0x7f) != 0x7f) {
+ *cp++ = (char)*dpt++;
+ ees->lencode++;
+ /* Oh dear -- too many bytes .. */
+ if (ees->lencode > LENEESPRT) {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO,
+ "I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]",
+ ees->lencode, dpend - dpt, LENEESPRT,
+#define D(x) (ees->lastcode[x])
+ D(0), D(1), D(2), D(3), D(4), D(5), D(6),
+ D(7), D(8), D(9), D(10), D(11), D(12));
+#undef D
+ ees->badformat++;
+ ees->reason = CODEREASON + 1;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+ }
+ /* Gave up because it was end of the buffer, rather than ff */
+ if (dpt == dpend) {
+ /* Incomplete. Wait for more. */
+ if (debug & DB_LOG_AWAITMORE)
+ msyslog(LOG_INFO,
+ "I: ees clock %d: %p == %p: await more",
+ ees->unit, dpt, dpend);
+ return;
+ }
+
+ /* This shouldn't happen ... ! */
+ if ((*dpt & 0x7f) != 0x7f) {
+ msyslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt);
+ ees->badformat++;
+ ees->reason = CODEREASON + 2;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Skip the 0xff */
+ dpt++;
+
+ /* Finally, got a complete buffer. Mainline code will
+ * continue on. */
+ cp = ees->lastcode;
+ break;
+
+ default:
+ msyslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d",
+ ees->unit, ees->codestate);
+ ees->reason = CODEREASON + 5;
+ ees_event(ees, CEVNT_FAULT);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Boy! After all that crap, the lastcode buffer now contains
+ * something we hope will be a valid time code. Do length
+ * checks and sanity checks on constant data.
+ */
+ ees->codestate = EESCS_WAIT;
+ ees->lasttime = current_time;
+ if (ees->lencode != LENEESPRT) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 6;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ cp = ees->lastcode;
+
+ /* Check that centisecond is zero */
+ if (cp[EESM_CSEC] != 0) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 7;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Check flag formats */
+ if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 8;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 9;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 10;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* So far, so good. Compute day, hours, minutes, seconds,
+ * time zone. Do range checks on these.
+ */
+
+#define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
+#define istrue(x) ((x)?1:0)
+
+ ees->second = bcdunpack(cp[EESM_SEC]); /* second */
+ ees->minute = bcdunpack(cp[EESM_MIN]); /* minute */
+ ees->hour = bcdunpack(cp[EESM_HOUR]); /* hour */
+
+ day = bcdunpack(cp[EESM_DAY]); /* day of month */
+
+ switch (bcdunpack(cp[EESM_MON])) { /* month */
+
+ /* Add in lengths of all previous months. Add one more
+ if it is a leap year and after February.
+ */
+ case 12: day += NOV; /*FALLSTHROUGH*/
+ case 11: day += OCT; /*FALLSTHROUGH*/
+ case 10: day += SEP; /*FALLSTHROUGH*/
+ case 9: day += AUG; /*FALLSTHROUGH*/
+ case 8: day += JUL; /*FALLSTHROUGH*/
+ case 7: day += JUN; /*FALLSTHROUGH*/
+ case 6: day += MAY; /*FALLSTHROUGH*/
+ case 5: day += APR; /*FALLSTHROUGH*/
+ case 4: day += MAR; /*FALLSTHROUGH*/
+ case 3: day += FEB;
+ if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/
+ case 2: day += JAN; /*FALLSTHROUGH*/
+ case 1: break;
+ default: ees->baddata++;
+ ees->reason = CODEREASON + 11;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ ees->day = day;
+
+ /* Get timezone. The clocktime routine wants the number
+ * of hours to add to the delivered time to get UT.
+ * Currently -1 if BST flag set, 0 otherwise. This
+ * is the place to tweak things if double summer time
+ * ever happens.
+ */
+ ees->tz = istrue(cp[EESM_BST]) ? -1 : 0;
+
+ if (ees->day > 366 || ees->day < 1 ||
+ ees->hour > 23 || ees->minute > 59 || ees->second > 59) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 12;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ n_sample = ees->nsamples;
+
+ /* Now, compute the reference time value: text -> tmp.l_ui */
+ if (!clocktime(ees->day, ees->hour, ees->minute, ees->second,
+ ees->tz, rbufp->recv_time.l_ui, &ees->yearstart,
+ &tmp.l_ui)) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 13;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+ tmp.l_uf = 0;
+
+ /* DON'T use ees->arrvtime -- it may be < reftime */
+ ees->lastsampletime = tmp;
+
+ /* If we are synchronised to the radio, update the reference time.
+ * Also keep a note of when clock was last good.
+ */
+ if (istrue(cp[EESM_MSFOK])) {
+ ees->reftime = tmp;
+ ees->clocklastgood = current_time;
+ }
+
+
+ /* Compute the offset. For the fractional part of the
+ * offset we use the expected delay for the message.
+ */
+ ees->codeoffsets[n_sample].l_ui = tmp.l_ui;
+ ees->codeoffsets[n_sample].l_uf = 0;
+
+ /* Number of seconds since the last step */
+ sincelast = this_uisec - ees->last_step;
+
+ memset((char *) &ppsclockev, 0, sizeof ppsclockev);
+
+ rc = ioctl(ees->io.fd, request, (char *) &ppsclockev);
+ if (debug & DB_PRINT_EV) fprintf(stderr,
+ "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08lx %08lx %ld\n",
+ DB_PRINT_EV, ees->unit, ees->io.fd, request, is_pps(ees),
+ rc, errno, ptr[0], ptr[1], ptr[2]);
+
+ /* If we managed to get the time of arrival, process the info */
+ if (rc >= 0) {
+ int conv = -1;
+ pps_step = ppsclockev.serial - ees->last_pps_no;
+
+ /* Possible that PPS triggered, but text message didn't */
+ if (pps_step == 2) msyslog(LOG_ERR, "pps step = 2 @ %02d", ees->second);
+ if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1;
+ if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4;
+
+ /* allow for single loss of PPS only */
+ if (pps_step != 1 && pps_step != 2)
+ fprintf(stderr, "PPS step: %d too far off %ld (%d)\n",
+ ppsclockev.serial, ees->last_pps_no, pps_step);
+ else if (!buftvtots((char *) &(ppsclockev.tv), &pps_arrvstamp))
+ fprintf(stderr, "buftvtots failed\n");
+ else { /* if ((ABS(time difference) - 0.25) < 0)
+ * then believe it ...
+ */
+ l_fp diff;
+ diff = pps_arrvstamp;
+ conv = 0;
+ L_SUB(&diff, &ees->arrvtime);
+ if (debug & DB_PRINT_CDT)
+ printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s",
+ DB_PRINT_CDT, (long)ees->arrvtime.l_ui, (long)ees->arrvtime.l_uf,
+ (long)pps_arrvstamp.l_ui, (long)pps_arrvstamp.l_uf,
+ (long)diff.l_ui, (long)diff.l_uf,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ ees->arrvtime = pps_arrvstamp;
+ conv++;
+ call_pps_sample++;
+ }
+ /* Some loss of some signals around sec = 1 */
+ else if (ees->second == 1) {
+ diff = pps_arrvstamp;
+ L_ADD(&diff, &onesec);
+ L_SUB(&diff, &ees->arrvtime);
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+ msyslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s",
+ pps_arrvstamp.l_ui - ees->arrvtime.l_ui,
+ pps_arrvstamp.l_uf,
+ ees->arrvtime.l_uf,
+ diff.l_ui, diff.l_uf,
+ (int)ppsclockev.tv.tv_usec,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ suspect_4ms_step |= 2;
+ ees->arrvtime = pps_arrvstamp;
+ L_ADD(&ees->arrvtime, &onesec);
+ conv++;
+ call_pps_sample++;
+ }
+ }
+ }
+ ees->last_pps_no = ppsclockev.serial;
+ if (debug & DB_PRINT_CDTC)
+ printf(
+ "[%x] %08lx %08lx %d u%d (%d %d)\n",
+ DB_PRINT_CDTC, (long)pps_arrvstamp.l_ui,
+ (long)pps_arrvstamp.l_uf, conv, ees->unit,
+ call_pps_sample, pps_step);
+ }
+
+ /* See if there has been a 4ms jump at a minute boundry */
+ { l_fp delta;
+#define delta_isec delta.l_ui
+#define delta_ssec delta.l_i
+#define delta_sfsec delta.l_f
+ long delta_f_abs;
+
+ delta.l_i = ees->arrvtime.l_i;
+ delta.l_f = ees->arrvtime.l_f;
+
+ L_SUB(&delta, &ees->last_l);
+ delta_f_abs = delta_sfsec;
+ if (delta_f_abs < 0) delta_f_abs = -delta_f_abs;
+
+ /* Dump the deltas each minute */
+ if (debug & DB_DUMP_DELTAS)
+ { if (/*0 <= ees->second && */
+ ees->second < ((sizeof deltas) / (sizeof deltas[0]))) deltas[ees->second] = delta_sfsec;
+ /* Dump on second 1, as second 0 sometimes missed */
+ if (ees->second == 1) {
+ char text[16 * ((sizeof deltas) / (sizeof deltas[0]))];
+ char *cptr=text;
+ int i;
+ for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) {
+ sprintf(cptr, " %d.%04d",
+ msec(deltas[i]), subms(deltas[i]));
+ while (*cptr) cptr++;
+ }
+ msyslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s",
+ msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE),
+ msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE),
+ text+1);
+ for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0;
+ }
+ }
+
+ /* Lets see if we have a 4 mS step at a minute boundaary */
+ if ( ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) &&
+ (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) &&
+ (ees->second == 0 || ees->second == 1 || ees->second == 2) &&
+ (sincelast < 0 || sincelast > 122)
+ ) { /* 4ms jump at min boundry */
+ int old_sincelast;
+ int count=0;
+ int sum = 0;
+ /* Yes -- so compute the ramp time */
+ if (ees->last_step == 0) sincelast = 0;
+ old_sincelast = sincelast;
+
+ /* First time in, just set "ees->last_step" */
+ if(ees->last_step) {
+ int other_step = 0;
+ int third_step = 0;
+ int this_step = (sincelast + (60 /2)) / 60;
+ int p_step = ees->this_step;
+ int p;
+ ees->last_steps[p_step] = this_step;
+ p= p_step;
+ p_step++;
+ if (p_step >= LAST_STEPS) p_step = 0;
+ ees->this_step = p_step;
+ /* Find the "average" interval */
+ while (p != p_step) {
+ int this = ees->last_steps[p];
+ if (this == 0) break;
+ if (this != this_step) {
+ if (other_step == 0 && (
+ this== (this_step +2) ||
+ this== (this_step -2) ||
+ this== (this_step +1) ||
+ this== (this_step -1)))
+ other_step = this;
+ if (other_step != this) {
+ int idelta = (this_step - other_step);
+ if (idelta < 0) idelta = - idelta;
+ if (third_step == 0 && (
+ (idelta == 1) ? (
+ this == (other_step +1) ||
+ this == (other_step -1) ||
+ this == (this_step +1) ||
+ this == (this_step -1))
+ :
+ (
+ this == (this_step + other_step)/2
+ )
+ )) third_step = this;
+ if (third_step != this) break;
+ }
+ }
+ sum += this;
+ p--;
+ if (p < 0) p += LAST_STEPS;
+ count++;
+ }
+ msyslog(LOG_ERR, "MSF%d: %d: This=%d (%d), other=%d/%d, sum=%d, count=%d, pps_step=%d, suspect=%x", ees->unit, p, ees->last_steps[p], this_step, other_step, third_step, sum, count, pps_step, suspect_4ms_step);
+ if (count != 0) sum = ((sum * 60) + (count /2)) / count;
+#define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS])
+ msyslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+ printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+#undef SV
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ if (sincelast > 170)
+ ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs);
+ else ees->last_step_late = 30;
+ if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30;
+ if (ees->last_step_late < 0) ees->last_step_late = 0;
+ if (ees->last_step_late >= 60) ees->last_step_late = 59;
+ sincelast = 0;
+ }
+ else { /* First time in -- just save info */
+ ees->last_step_late = 30;
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ sum = 4 * 60;
+ }
+ ees->last_step = this_uisec;
+ printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n",
+ ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
+ ees->second, old_sincelast, ees->last_step_late, count, sum,
+ ees->last_step_secs);
+ msyslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d",
+ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second,
+ old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
+ if (sum) ees->last_step_secs = sum;
+ }
+ /* OK, so not a 4ms step at a minute boundry */
+ else {
+ if (suspect_4ms_step) msyslog(LOG_ERR,
+ "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]",
+ ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec),
+ msec(EES_STEP_F - EES_STEP_F_GRACE),
+ subms(EES_STEP_F - EES_STEP_F_GRACE),
+ (int)msec(delta_f_abs),
+ (int)subms(delta_f_abs),
+ msec(EES_STEP_F + EES_STEP_F_GRACE),
+ subms(EES_STEP_F + EES_STEP_F_GRACE),
+ ees->second,
+ sincelast);
+ if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) {
+ static int ees_step_notes = EES_STEP_NOTES;
+ if (ees_step_notes > 0) {
+ ees_step_notes--;
+ printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n",
+ ees->unit, (long)msec(delta_sfsec), (long)subms(delta_sfsec),
+ ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !");
+ msyslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s",
+ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !");
+ }
+ }
+ }
+ }
+ ees->last_l = ees->arrvtime;
+
+ /* IF we have found that it's ramping
+ * && it's within twice the expected ramp period
+ * && there is a non zero step size (avoid /0 !)
+ * THEN we twiddle things
+ */
+ if (ees->using_ramp &&
+ sincelast < (ees->last_step_secs)*2 &&
+ ees->last_step_secs)
+ { long sec_of_ramp = sincelast + ees->last_step_late;
+ long fsecs;
+ l_fp inc;
+
+ /* Ramp time may vary, so may ramp for longer than last time */
+ if (sec_of_ramp > (ees->last_step_secs + 120))
+ sec_of_ramp = ees->last_step_secs;
+
+ /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
+ fsecs = sec_of_ramp * (ees->jump_fsecs / ees->last_step_secs);
+
+ if (debug & DB_LOG_DELTAS) msyslog(LOG_ERR,
+ "[%x] MSF%d: %3ld/%03d -> d=%11ld (%d|%ld)",
+ DB_LOG_DELTAS,
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+ if (debug & DB_PRINT_DELTAS) printf(
+ "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n",
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ (long)pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+
+ /* Must sign extend the result */
+ inc.l_i = (fsecs < 0) ? -1 : 0;
+ inc.l_f = fsecs;
+ if (debug & DB_INC_PPS)
+ { L_SUB(&pps_arrvstamp, &inc);
+ L_SUB(&ees->arrvtime, &inc);
+ }
+ else
+ { L_ADD(&pps_arrvstamp, &inc);
+ L_ADD(&ees->arrvtime, &inc);
+ }
+ }
+ else {
+ if (debug & DB_LOG_DELTAS) msyslog(LOG_ERR,
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ if (debug & DB_PRINT_DELTAS) printf(
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ }
+
+ L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]);
+ L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]);
+
+ if (call_pps_sample && !(debug & DB_NO_PPS)) {
+ /* Sigh -- it expects its args negated */
+ L_NEG(&pps_arrvstamp);
+ /*
+ * I had to disable this here, since it appears there is no pointer to the
+ * peer structure.
+ *
+ (void) pps_sample(peer, &pps_arrvstamp);
+ */
+ }
+
+ /* Subtract off the local clock time stamp */
+ L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime);
+ if (debug & DB_LOG_SAMPLES) msyslog(LOG_ERR,
+ "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s",
+ ees->unit, DB_LOG_DELTAS, n_sample,
+ ees->codeoffsets[n_sample].l_f,
+ ees->codeoffsets[n_sample].l_f / 4295,
+ pps_arrvstamp.l_f,
+ pps_arrvstamp.l_f /4295,
+ (debug & DB_NO_PPS) ? " [no PPS]" : "");
+
+ if (ees->nsamples++ == NCODES-1) ees_process(ees);
+
+ /* Done! */
+}
+
+
+/* offcompare - auxiliary comparison routine for offset sort */
+
+#ifdef QSORT_USES_VOID_P
+static int
+offcompare(
+ const void *va,
+ const void *vb
+ )
+{
+ const l_fp *a = (const l_fp *)va;
+ const l_fp *b = (const l_fp *)vb;
+ return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
+}
+#else
+static int
+offcompare(
+ const l_fp *a,
+ const l_fp *b
+ )
+{
+ return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
+}
+#endif /* QSORT_USES_VOID_P */
+
+
+/* ees_process - process a pile of samples from the clock */
+static void
+ees_process(
+ struct eesunit *ees
+ )
+{
+ static int last_samples = -1;
+ register int i, j;
+ register int noff;
+ register l_fp *coffs = ees->codeoffsets;
+ l_fp offset, tmp;
+ double dispersion; /* ++++ */
+ int lostsync, isinsync;
+ int samples = ees->nsamples;
+ int samplelog = 0; /* keep "gcc -Wall" happy ! */
+ int samplereduce = (samples + 1) / 2;
+ double doffset;
+
+ /* Reset things to zero so we don't have to worry later */
+ ees_reset(ees);
+
+ if (sloppyclockflag[ees->unit]) {
+ samplelog = (samples < 2) ? 0 :
+ (samples < 5) ? 1 :
+ (samples < 9) ? 2 :
+ (samples < 17) ? 3 :
+ (samples < 33) ? 4 : 5;
+ samplereduce = (1 << samplelog);
+ }
+
+ if (samples != last_samples &&
+ ((samples != (last_samples-1)) || samples < 3)) {
+ msyslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....",
+ samples, last_samples, samplereduce);
+ last_samples = samples;
+ }
+ if (samples < 1) return;
+
+ /* If requested, dump the raw data we have in the buffer */
+ if (ees->dump_vals) dump_buf(coffs, 0, samples, "Raw data is:");
+
+ /* Sort the offsets, trim off the extremes, then choose one. */
+ qsort((char *) coffs, (size_t)samples, sizeof(l_fp), offcompare);
+
+ noff = samples;
+ i = 0;
+ while ((noff - i) > samplereduce) {
+ /* Trim off the sample which is further away
+ * from the median. We work this out by doubling
+ * the median, subtracting off the end samples, and
+ * looking at the sign of the answer, using the
+ * identity (c-b)-(b-a) == 2*b-a-c
+ */
+ tmp = coffs[(noff + i)/2];
+ L_ADD(&tmp, &tmp);
+ L_SUB(&tmp, &coffs[i]);
+ L_SUB(&tmp, &coffs[noff-1]);
+ if (L_ISNEG(&tmp)) noff--; else i++;
+ }
+
+ /* If requested, dump the reduce data we have in the buffer */
+ if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced to:");
+
+ /* What we do next depends on the setting of the sloppy clock flag.
+ * If it is on, average the remainder to derive our estimate.
+ * Otherwise, just pick a representative value from the remaining stuff
+ */
+ if (sloppyclockflag[ees->unit]) {
+ offset.l_ui = offset.l_uf = 0;
+ for (j = i; j < noff; j++)
+ L_ADD(&offset, &coffs[j]);
+ for (j = samplelog; j > 0; j--)
+ L_RSHIFTU(&offset);
+ }
+ else offset = coffs[i+BESTSAMPLE];
+
+ /* Compute the dispersion as the difference between the
+ * lowest and highest offsets that remain in the
+ * consideration list.
+ *
+ * It looks like MOST clocks have MOD (max error), so halve it !
+ */
+ tmp = coffs[noff-1];
+ L_SUB(&tmp, &coffs[i]);
+#define FRACT_SEC(n) ((1 << 30) / (n/2))
+ dispersion = LFPTOFP(&tmp) / 2; /* ++++ */
+ if (debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) msyslog(
+ (debug & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Offset=%06d (%d), disp=%f%s [%d], %d %d=%d %d:%d %d=%d %d",
+ debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE),
+ offset.l_f / 4295, offset.l_f,
+ (dispersion * 1526) / 100,
+ (sloppyclockflag[ees->unit]) ? " by averaging" : "",
+ FRACT_SEC(10) / 4295,
+ (coffs[0].l_f) / 4295,
+ i,
+ (coffs[i].l_f) / 4295,
+ (coffs[samples/2].l_f) / 4295,
+ (coffs[i+BESTSAMPLE].l_f) / 4295,
+ noff-1,
+ (coffs[noff-1].l_f) / 4295,
+ (coffs[samples-1].l_f) / 4295);
+
+ /* Are we playing silly wotsits ?
+ * If we are using all data, see if there is a "small" delta,
+ * and if so, blurr this with 3/4 of the delta from the last value
+ */
+ if (ees->usealldata && ees->offset.l_uf) {
+ long diff = (long) (ees->offset.l_uf - offset.l_uf);
+
+ /* is the delta small enough ? */
+ if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) {
+ int samd = (64 * 4) / samples;
+ long new;
+ if (samd < 2) samd = 2;
+ new = offset.l_uf + ((diff * (samd -1)) / samd);
+
+ /* Sign change -> need to fix up int part */
+ if ((new & 0x80000000) !=
+ (((long) offset.l_uf) & 0x80000000))
+ { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d",
+ new & 0x80000000,
+ ((long) offset.l_uf) & 0x80000000,
+ new, (long) offset.l_uf,
+ (new < 0) ? -1 : 1);
+ offset.l_ui += (new < 0) ? -1 : 1;
+ }
+ dispersion /= 4;
+ if (debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) msyslog(
+ (debug & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Smooth data: %ld -> %ld, dispersion now %f",
+ debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE),
+ ((long) offset.l_uf) / 4295, new / 4295,
+ (dispersion * 1526) / 100);
+ offset.l_uf = (unsigned long) new;
+ }
+ else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
+ (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "[%x] No smooth as delta not %d < %ld < %d",
+ debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ - FRACT_SEC(100), diff, FRACT_SEC(100));
+ }
+ else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) msyslog(
+ (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
+ debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ ees->usealldata, ees->offset.l_f, ees->offset.l_uf,
+ offset.l_f, ees->offset.l_f - offset.l_f);
+
+ /* Collect offset info for debugging info */
+ ees->offset = offset;
+ ees->lowoffset = coffs[i];
+ ees->highoffset = coffs[noff-1];
+
+ /* Determine synchronization status. Can be unsync'd either
+ * by a report from the clock or by a leap hold.
+ *
+ * Loss of the radio signal for a short time does not cause
+ * us to go unsynchronised, since the receiver keeps quite
+ * good time on its own. The spec says 20ms in 4 hours; the
+ * observed drift in our clock (Cambridge) is about a second
+ * a day, but even that keeps us within the inherent tolerance
+ * of the clock for about 15 minutes. Observation shows that
+ * the typical "short" outage is 3 minutes, so to allow us
+ * to ride out those, we will give it 5 minutes.
+ */
+ lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0;
+ isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1;
+
+ /* Done. Use time of last good, synchronised code as the
+ * reference time, and lastsampletime as the receive time.
+ */
+ if (ees->fix_pending) {
+ msyslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n",
+ ees->fix_pending, ees->unit, offset.l_i, offset.l_f);
+ ees->fix_pending = 0;
+ }
+ LFPTOD(&offset, doffset);
+ refclock_receive(ees->peer);
+ ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL);
+}
+
+/* msfees_poll - called by the transmit procedure */
+static void
+msfees_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid",
+ unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ msyslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused",
+ unit);
+ return;
+ }
+
+ ees_process(eesunits[unit]);
+
+ if ((current_time - eesunits[unit]->lasttime) > 150)
+ ees_event(eesunits[unit], CEVNT_FAULT);
+}
+
+
+#else
+int refclock_msfees_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_mx4200.c b/ntpd/refclock_mx4200.c
new file mode 100644
index 0000000..bc694ad
--- /dev/null
+++ b/ntpd/refclock_mx4200.c
@@ -0,0 +1,1654 @@
+/*
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
+ *
+ * Copyright (c) 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. The name of the University may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Modified: Marc Brett <marc.brett@westgeo.com> Sept, 1999.
+ *
+ * 1. Added support for alternate PPS schemes, with code mostly
+ * copied from the Oncore driver (Thanks, Poul-Henning Kamp).
+ * This code runs on SunOS 4.1.3 with ppsclock-1.6a1 and Solaris 7.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "mx4200.h"
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+#include "ntp_sprintf.h"
+
+#ifndef HAVE_STRUCT_PPSCLOCKEV
+struct ppsclockev {
+# ifdef HAVE_STRUCT_TIMESPEC
+ struct timespec tv;
+# else
+ struct timeval tv;
+# endif
+ u_int serial;
+};
+#endif /* ! HAVE_STRUCT_PPSCLOCKEV */
+
+#ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+#else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# endif
+#endif
+
+/*
+ * This driver supports the Magnavox Model MX 4200 GPS Receiver
+ * adapted to precision timing applications. It requires the
+ * ppsclock line discipline or streams module described in the
+ * Line Disciplines and Streams Drivers page. It also requires a
+ * gadget box and 1-PPS level converter, such as described in the
+ * Pulse-per-second (PPS) Signal Interfacing page.
+ *
+ * It's likely that other compatible Magnavox receivers such as the
+ * MX 4200D, MX 9212, MX 9012R, MX 9112 will be supported by this code.
+ */
+
+/*
+ * Check this every time you edit the code!
+ */
+#define YEAR_LAST_MODIFIED 2000
+
+/*
+ * GPS Definitions
+ */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B4800 /* baud */
+
+/*
+ * Radio interface parameters
+ */
+#define PRECISION (-18) /* precision assumed (about 4 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */
+#define DEFFUDGETIME 0 /* default fudge time (ms) */
+
+#define SLEEPTIME 32 /* seconds to wait for reconfig to complete */
+
+/*
+ * Position Averaging.
+ */
+#define INTERVAL 1 /* Interval between position measurements (s) */
+#define AVGING_TIME 24 /* Number of hours to average */
+#define NOT_INITIALIZED -9999. /* initial pivot longitude */
+
+/*
+ * MX4200 unit control structure.
+ */
+struct mx4200unit {
+ u_int pollcnt; /* poll message counter */
+ u_int polled; /* Hand in a time sample? */
+ u_int lastserial; /* last pps serial number */
+ struct ppsclockev ppsev; /* PPS control structure */
+ double avg_lat; /* average latitude */
+ double avg_lon; /* average longitude */
+ double avg_alt; /* average height */
+ double central_meridian; /* central meridian */
+ double N_fixes; /* Number of position measurements */
+ int last_leap; /* leap second warning */
+ u_int moving; /* mobile platform? */
+ u_long sloppyclockflag; /* fudge flags */
+ u_int known; /* position known yet? */
+ u_long clamp_time; /* when to stop postion averaging */
+ u_long log_time; /* when to print receiver status */
+ pps_handle_t pps_h;
+ pps_params_t pps_p;
+ pps_info_t pps_i;
+};
+
+static char pmvxg[] = "PMVXG";
+
+/* XXX should be somewhere else */
+#ifdef __GNUC__
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+#ifndef __attribute__
+#define __attribute__(args)
+#endif /* __attribute__ */
+#endif /* __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) */
+#else
+#ifndef __attribute__
+#define __attribute__(args)
+#endif /* __attribute__ */
+#endif /* __GNUC__ */
+/* XXX end */
+
+/*
+ * Function prototypes
+ */
+static int mx4200_start P((int, struct peer *));
+static void mx4200_shutdown P((int, struct peer *));
+static void mx4200_receive P((struct recvbuf *));
+static void mx4200_poll P((int, struct peer *));
+
+static char * mx4200_parse_t P((struct peer *));
+static char * mx4200_parse_p P((struct peer *));
+static char * mx4200_parse_s P((struct peer *));
+#ifdef QSORT_USES_VOID_P
+int mx4200_cmpl_fp P((const void *, const void *));
+#else
+int mx4200_cmpl_fp P((const l_fp *, const l_fp *));
+#endif /* not QSORT_USES_VOID_P */
+static int mx4200_config P((struct peer *));
+static void mx4200_ref P((struct peer *));
+static void mx4200_send P((struct peer *, char *, ...))
+ __attribute__ ((format (printf, 2, 3)));
+static u_char mx4200_cksum P((char *, int));
+static int mx4200_jday P((int, int, int));
+static void mx4200_debug P((struct peer *, char *, ...))
+ __attribute__ ((format (printf, 2, 3)));
+static int mx4200_pps P((struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_mx4200 = {
+ mx4200_start, /* start up driver */
+ mx4200_shutdown, /* shut down driver */
+ mx4200_poll, /* transmit poll message */
+ noentry, /* not used (old mx4200_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old mx4200_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+
+/*
+ * mx4200_start - open the devices and initialize data for processing
+ */
+static int
+mx4200_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_PPS))) {
+ return (0);
+ }
+
+ /*
+ * Allocate unit structure
+ */
+ if (!(up = (struct mx4200unit *) emalloc(sizeof(struct mx4200unit)))) {
+ perror("emalloc");
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct mx4200unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = mx4200_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ /* Ensure the receiver is properly configured */
+ return mx4200_config(peer);
+}
+
+
+/*
+ * mx4200_shutdown - shut down the clock
+ */
+static void
+mx4200_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * mx4200_config - Configure the receiver
+ */
+static int
+mx4200_config(
+ struct peer *peer
+ )
+{
+ char tr_mode;
+ int add_mode;
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ int mode;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /*
+ * Initialize the unit variables
+ *
+ * STRANGE BEHAVIOUR WARNING: The fudge flags are not available
+ * at the time mx4200_start is called. These are set later,
+ * and so the code must be prepared to handle changing flags.
+ */
+ up->sloppyclockflag = pp->sloppyclockflag;
+ if (pp->sloppyclockflag & CLK_FLAG2) {
+ up->moving = 1; /* Receiver on mobile platform */
+ msyslog(LOG_DEBUG, "mx4200_config: mobile platform");
+ } else {
+ up->moving = 0; /* Static Installation */
+ }
+ up->pollcnt = 2;
+ up->polled = 0;
+ up->known = 0;
+ up->avg_lat = 0.0;
+ up->avg_lon = 0.0;
+ up->avg_alt = 0.0;
+ up->central_meridian = NOT_INITIALIZED;
+ up->N_fixes = 0.0;
+ up->last_leap = 0; /* LEAP_NOWARNING */
+ up->clamp_time = current_time + (AVGING_TIME * 60 * 60);
+ up->log_time = current_time + SLEEPTIME;
+
+ if (time_pps_create(pp->io.fd, &up->pps_h) < 0) {
+ perror("time_pps_create");
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_create failed: %m");
+ return (0);
+ }
+ if (time_pps_getcap(up->pps_h, &mode) < 0) {
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_getcap failed: %m");
+ return (0);
+ }
+
+ if (time_pps_getparams(up->pps_h, &up->pps_p) < 0) {
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_getparams failed: %m");
+ return (0);
+ }
+
+ /* nb. only turn things on, if someone else has turned something
+ * on before we get here, leave it alone!
+ */
+
+ up->pps_p.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
+ up->pps_p.mode &= mode; /* only set what is legal */
+
+ if (time_pps_setparams(up->pps_h, &up->pps_p) < 0) {
+ perror("time_pps_setparams");
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_setparams failed: %m");
+ exit(1);
+ }
+
+ if (time_pps_kcbind(up->pps_h, PPS_KC_HARDPPS, PPS_CAPTUREASSERT,
+ PPS_TSFMT_TSPEC) < 0) {
+ perror("time_pps_kcbind");
+ msyslog(LOG_ERR,
+ "mx4200_config: time_pps_kcbind failed: %m");
+ exit(1);
+ }
+
+
+ /*
+ * "007" Control Port Configuration
+ * Zero the output list (do it twice to flush possible junk)
+ */
+ mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ /* control port output block Label */
+ 1); /* clear current output control list (1=yes) */
+ /* add/delete sentences from list */
+ /* must be null */
+ /* sentence output rate (sec) */
+ /* precision for position output */
+ /* nmea version for cga & gll output */
+ /* pass-through control */
+ mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
+ PMVXG_S_PORTCONF, 1);
+
+ /*
+ * Request software configuration so we can syslog the firmware version
+ */
+ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_SOFTCONF);
+
+ /*
+ * "001" Initialization/Mode Control, Part A
+ * Where ARE we?
+ */
+ mx4200_send(peer, "%s,%03d,,,,,,,,,,", pmvxg,
+ PMVXG_S_INITMODEA);
+ /* day of month */
+ /* month of year */
+ /* year */
+ /* gmt */
+ /* latitude DDMM.MMMM */
+ /* north/south */
+ /* longitude DDDMM.MMMM */
+ /* east/west */
+ /* height */
+ /* Altitude Reference 1=MSL */
+
+ /*
+ * "001" Initialization/Mode Control, Part B
+ * Start off in 2d/3d coast mode, holding altitude to last known
+ * value if only 3 satellites available.
+ */
+ mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
+ pmvxg, PMVXG_S_INITMODEB,
+ 3, /* 2d/3d coast */
+ /* reserved */
+ 0.1, /* hor accel fact as per Steve (m/s**2) */
+ 0.1, /* ver accel fact as per Steve (m/s**2) */
+ 10, /* vdop */
+ 10, /* hdop limit as per Steve */
+ 5, /* elevation limit as per Steve (deg) */
+ 'U', /* time output mode (UTC) */
+ 0); /* local time offset from gmt (HHHMM) */
+
+ /*
+ * "023" Time Recovery Configuration
+ * Get UTC time from a stationary receiver.
+ * (Set field 1 'D' == dynamic if we are on a moving platform).
+ * (Set field 1 'S' == static if we are not moving).
+ * (Set field 1 'K' == known position if we can initialize lat/lon/alt).
+ */
+
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->moving = 1; /* Receiver on mobile platform */
+ else
+ up->moving = 0; /* Static Installation */
+
+ up->pollcnt = 2;
+ if (up->moving) {
+ /* dynamic: solve for pos, alt, time, while moving */
+ tr_mode = 'D';
+ } else {
+ /* static: solve for pos, alt, time, while stationary */
+ tr_mode = 'S';
+ }
+ mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
+ PMVXG_S_TRECOVCONF,
+ tr_mode, /* time recovery mode (see above ) */
+ 'U', /* synchronize to UTC */
+ 'A', /* always output a time pulse */
+ 500, /* max time error in ns */
+ 0, /* user bias in ns */
+ 1); /* output "830" sentences to control port */
+ /* Multi-satellite mode */
+
+ /*
+ * Output position information (to calculate fixed installation
+ * location) only if we are not moving
+ */
+ if (up->moving) {
+ add_mode = 2; /* delete from list */
+ } else {
+ add_mode = 1; /* add to list */
+ }
+
+
+ /*
+ * "007" Control Port Configuration
+ * Output "021" position, height, velocity reports
+ */
+ mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ PMVXG_D_PHV, /* control port output block Label */
+ 0, /* clear current output control list (0=no) */
+ add_mode, /* add/delete sentences from list (1=add, 2=del) */
+ /* must be null */
+ INTERVAL); /* sentence output rate (sec) */
+ /* precision for position output */
+ /* nmea version for cga & gll output */
+ /* pass-through control */
+
+ return (1);
+}
+
+/*
+ * mx4200_ref - Reconfigure unit as a reference station at a known position.
+ */
+static void
+mx4200_ref(
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ double minute, lat, lon, alt;
+ char lats[16], lons[16];
+ char nsc, ewc;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /* Should never happen! */
+ if (up->moving) return;
+
+ /*
+ * Set up to output status information in the near future
+ */
+ up->log_time = current_time + SLEEPTIME;
+
+ /*
+ * "007" Control Port Configuration
+ * Stop outputting "021" position, height, velocity reports
+ */
+ mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ PMVXG_D_PHV, /* control port output block Label */
+ 0, /* clear current output control list (0=no) */
+ 2); /* add/delete sentences from list (2=delete) */
+ /* must be null */
+ /* sentence output rate (sec) */
+ /* precision for position output */
+ /* nmea version for cga & gll output */
+ /* pass-through control */
+
+ /*
+ * "001" Initialization/Mode Control, Part B
+ * Put receiver in fully-constrained 2d nav mode
+ */
+ mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
+ pmvxg, PMVXG_S_INITMODEB,
+ 2, /* 2d nav */
+ /* reserved */
+ 0.1, /* hor accel fact as per Steve (m/s**2) */
+ 0.1, /* ver accel fact as per Steve (m/s**2) */
+ 10, /* vdop */
+ 10, /* hdop limit as per Steve */
+ 5, /* elevation limit as per Steve (deg) */
+ 'U', /* time output mode (UTC) */
+ 0); /* local time offset from gmt (HHHMM) */
+
+ /*
+ * "023" Time Recovery Configuration
+ * Get UTC time from a stationary receiver. Solve for time only.
+ * This should improve the time resolution dramatically.
+ */
+ mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
+ PMVXG_S_TRECOVCONF,
+ 'K', /* known position: solve for time only */
+ 'U', /* synchronize to UTC */
+ 'A', /* always output a time pulse */
+ 500, /* max time error in ns */
+ 0, /* user bias in ns */
+ 1); /* output "830" sentences to control port */
+ /* Multi-satellite mode */
+
+ /*
+ * "000" Initialization/Mode Control - Part A
+ * Fix to our averaged position.
+ */
+ if (up->central_meridian != NOT_INITIALIZED) {
+ up->avg_lon += up->central_meridian;
+ if (up->avg_lon < -180.0) up->avg_lon += 360.0;
+ if (up->avg_lon > 180.0) up->avg_lon -= 360.0;
+ }
+
+ if (up->avg_lat >= 0.0) {
+ lat = up->avg_lat;
+ nsc = 'N';
+ } else {
+ lat = up->avg_lat * (-1.0);
+ nsc = 'S';
+ }
+ if (up->avg_lon >= 0.0) {
+ lon = up->avg_lon;
+ ewc = 'E';
+ } else {
+ lon = up->avg_lon * (-1.0);
+ ewc = 'W';
+ }
+ alt = up->avg_alt;
+ minute = (lat - (double)(int)lat) * 60.0;
+ sprintf(lats,"%02d%02.4f", (int)lat, minute);
+ minute = (lon - (double)(int)lon) * 60.0;
+ sprintf(lons,"%03d%02.4f", (int)lon, minute);
+
+ mx4200_send(peer, "%s,%03d,,,,,%s,%c,%s,%c,%.2f,%d", pmvxg,
+ PMVXG_S_INITMODEA,
+ /* day of month */
+ /* month of year */
+ /* year */
+ /* gmt */
+ lats, /* latitude DDMM.MMMM */
+ nsc, /* north/south */
+ lons, /* longitude DDDMM.MMMM */
+ ewc, /* east/west */
+ alt, /* Altitude */
+ 1); /* Altitude Reference (0=WGS84 ellipsoid, 1=MSL geoid)*/
+
+ msyslog(LOG_DEBUG,
+ "mx4200: reconfig to fixed location: %s %c, %s %c, %.2f m",
+ lats, nsc, lons, ewc, alt );
+
+}
+
+/*
+ * mx4200_poll - mx4200 watchdog routine
+ */
+static void
+mx4200_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+
+ /*
+ * If we haven't had a response in a while, reset the receiver.
+ */
+ if (up->pollcnt > 0) {
+ up->pollcnt--;
+ } else {
+ refclock_report(peer, CEVNT_TIMEOUT);
+
+ /*
+ * Request a "000" status message which should trigger a
+ * reconfig
+ */
+ mx4200_send(peer, "%s,%03d",
+ "CDGPQ", /* query from CDU to GPS */
+ PMVXG_D_STATUS); /* label of desired sentence */
+ }
+
+ /*
+ * polled every 64 seconds. Ask mx4200_receive to hand in
+ * a timestamp.
+ */
+ up->polled = 1;
+ pp->polls++;
+
+ /*
+ * Output receiver status information.
+ */
+ if ((up->log_time > 0) && (current_time > up->log_time)) {
+ up->log_time = 0;
+ /*
+ * Output the following messages once, for debugging.
+ * "004" Mode Data
+ * "523" Time Recovery Parameters
+ */
+ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA);
+ mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE);
+ }
+}
+
+static char char2hex[] = "0123456789ABCDEF";
+
+/*
+ * mx4200_receive - receive gps data
+ */
+static void
+mx4200_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char *cp;
+ int sentence_type;
+ u_char ck;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /*
+ * If operating mode has been changed, then reinitialize the receiver
+ * before doing anything else.
+ */
+ if ((pp->sloppyclockflag & CLK_FLAG2) !=
+ (up->sloppyclockflag & CLK_FLAG2)) {
+ up->sloppyclockflag = pp->sloppyclockflag;
+ mx4200_debug(peer,
+ "mx4200_receive: mode switch: reset receiver\n");
+ mx4200_config(peer);
+ return;
+ }
+ up->sloppyclockflag = pp->sloppyclockflag;
+
+ /*
+ * Read clock output. Automatically handles STREAMS, CLKLDISC.
+ */
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
+
+ /*
+ * There is a case where <cr><lf> generates 2 timestamps.
+ */
+ if (pp->lencode == 0)
+ return;
+
+ up->pollcnt = 2;
+ pp->a_lastcode[pp->lencode] = '\0';
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ mx4200_debug(peer, "mx4200_receive: %d %s\n",
+ pp->lencode, pp->a_lastcode);
+
+ /*
+ * The structure of the control port sentences is based on the
+ * NMEA-0183 Standard for interfacing Marine Electronics
+ * Navigation Devices (Version 1.5)
+ *
+ * $PMVXG,XXX, ....................*CK<cr><lf>
+ *
+ * $ Sentence Start Identifier (reserved char)
+ * (Start-of-Sentence Identifier)
+ * P Special ID (Proprietary)
+ * MVX Originator ID (Magnavox)
+ * G Interface ID (GPS)
+ * , Field Delimiters (reserved char)
+ * XXX Sentence Type
+ * ...... Data
+ * * Checksum Field Delimiter (reserved char)
+ * CK Checksum
+ * <cr><lf> Carriage-Return/Line Feed (reserved chars)
+ * (End-of-Sentence Identifier)
+ *
+ * Reject if any important landmarks are missing.
+ */
+ cp = pp->a_lastcode + pp->lencode - 3;
+ if (cp < pp->a_lastcode || *pp->a_lastcode != '$' || cp[0] != '*' ) {
+ mx4200_debug(peer, "mx4200_receive: bad format\n");
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Check and discard the checksum
+ */
+ ck = mx4200_cksum(&pp->a_lastcode[1], pp->lencode - 4);
+ if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) {
+ mx4200_debug(peer, "mx4200_receive: bad checksum\n");
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ *cp = '\0';
+
+ /*
+ * Get the sentence type.
+ */
+ sentence_type = 0;
+ if ((cp = strchr(pp->a_lastcode, ',')) == NULL) {
+ mx4200_debug(peer, "mx4200_receive: no sentence\n");
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ cp++;
+ sentence_type = strtol(cp, &cp, 10);
+
+ /*
+ * Process the sentence according to its type.
+ */
+ switch (sentence_type) {
+
+ /*
+ * "000" Status message
+ */
+ case PMVXG_D_STATUS:
+ /*
+ * XXX
+ * Since we configure the receiver to not give us status
+ * messages and since the receiver outputs status messages by
+ * default after being reset to factory defaults when sent the
+ * "$PMVXG,018,C\r\n" message, any status message we get
+ * indicates the reciever needs to be initialized; thus, it is
+ * not necessary to decode the status message.
+ */
+ if ((cp = mx4200_parse_s(peer)) != NULL) {
+ mx4200_debug(peer,
+ "mx4200_receive: status: %s\n", cp);
+ }
+ mx4200_debug(peer, "mx4200_receive: reset receiver\n");
+ mx4200_config(peer);
+ break;
+
+ /*
+ * "021" Position, Height, Velocity message,
+ * if we are still averaging our position
+ */
+ case PMVXG_D_PHV:
+ if (!up->known) {
+ /*
+ * Parse the message, calculating our averaged position.
+ */
+ if ((cp = mx4200_parse_p(peer)) != NULL) {
+ mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp);
+ return;
+ }
+ mx4200_debug(peer,
+ "mx4200_receive: position avg %f %.9f %.9f %.4f\n",
+ up->N_fixes, up->avg_lat, up->avg_lon, up->avg_alt);
+ /*
+ * Reinitialize as a reference station
+ * if position is well known.
+ */
+ if (current_time > up->clamp_time) {
+ up->known++;
+ mx4200_debug(peer, "mx4200_receive: reconfiguring!\n");
+ mx4200_ref(peer);
+ }
+ }
+ break;
+
+ /*
+ * Print to the syslog:
+ * "004" Mode Data
+ * "030" Software Configuration
+ * "523" Time Recovery Parameters Currently in Use
+ */
+ case PMVXG_D_MODEDATA:
+ case PMVXG_D_SOFTCONF:
+ case PMVXG_D_TRECOVUSEAGE:
+
+ if ((cp = mx4200_parse_s(peer)) != NULL) {
+ mx4200_debug(peer,
+ "mx4200_receive: multi-record: %s\n", cp);
+ }
+ break;
+
+ /*
+ * "830" Time Recovery Results message
+ */
+ case PMVXG_D_TRECOVOUT:
+
+ /*
+ * Capture the last PPS signal.
+ * Precision timestamp is returned in pp->lastrec
+ */
+ if (mx4200_pps(peer) != NULL) {
+ mx4200_debug(peer, "mx4200_receive: pps failure\n");
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+
+ /*
+ * Parse the time recovery message, and keep the info
+ * to print the pretty billboards.
+ */
+ if ((cp = mx4200_parse_t(peer)) != NULL) {
+ mx4200_debug(peer, "mx4200_receive: time: %s\n", cp);
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Add the new sample to a median filter.
+ */
+ if (!refclock_process(pp)) {
+ mx4200_debug(peer,"mx4200_receive: offset: %.6f\n",
+ pp->offset);
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!up->polled)
+ return;
+
+ /*
+ * Return offset and dispersion to control module. We use
+ * lastrec as both the reference time and receive time in
+ * order to avoid being cute, like setting the reference time
+ * later than the receive time, which may cause a paranoid
+ * protocol module to chuck out the data.
+ */
+ mx4200_debug(peer, "mx4200_receive: process time: ");
+ mx4200_debug(peer, "%4d-%03d %02d:%02d:%02d at %s, %.6f\n",
+ pp->year, pp->day, pp->hour, pp->minute, pp->second,
+ prettydate(&pp->lastrec), pp->offset);
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /*
+ * We have succeeded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+ break;
+
+ /*
+ * Ignore all other sentence types
+ */
+ default:
+ break;
+
+ } /* switch (sentence_type) */
+
+ return;
+}
+
+
+/*
+ * Parse a mx4200 time recovery message. Returns a string if error.
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 830=Time Recovery Results
+ * This sentence is output approximately 1 second
+ * preceding the 1PPS output. It indicates the
+ * exact time of the next pulse, whether or not the
+ * time mark will be valid (based on operator-specified
+ * error tolerance), the time to which the pulse is
+ * synchronized, the receiver operating mode,
+ * and the time error of the *last* 1PPS output.
+ * 1 char Time Mark Valid: T=Valid, F=Not Valid
+ * 2 int Year: 1993-
+ * 3 int Month of Year: 1-12
+ * 4 int Day of Month: 1-31
+ * 5 int Time of Day: HH:MM:SS
+ * 6 char Time Synchronization: U=UTC, G=GPS
+ * 7 char Time Recovery Mode: D=Dynamic, S=Static,
+ * K=Known Position, N=No Time Recovery
+ * 8 int Oscillator Offset: The filter's estimate of the oscillator
+ * frequency error, in parts per billion (ppb).
+ * 9 int Time Mark Error: The computed error of the *last* pulse
+ * output, in nanoseconds.
+ * 10 int User Time Bias: Operator specified bias, in nanoseconds
+ * 11 int Leap Second Flag: Indicates that a leap second will
+ * occur. This value is usually zero, except during
+ * the week prior to the leap second occurrence, when
+ * this value will be set to +1 or -1. A value of
+ * +1 indicates that GPS time will be 1 second
+ * further ahead of UTC time.
+ *
+ */
+static char *
+mx4200_parse_t(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ char time_mark_valid, time_sync, op_mode;
+ int sentence_type, valid;
+ int year, day_of_year, month, day_of_month;
+ int hour, minute, second, leapsec;
+ int oscillator_offset, time_mark_error, time_bias;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ leapsec = 0; /* Not all receivers output leap second warnings (!) */
+ sscanf(pp->a_lastcode,
+ "$PMVXG,%d,%c,%d,%d,%d,%d:%d:%d,%c,%c,%d,%d,%d,%d",
+ &sentence_type, &time_mark_valid, &year, &month, &day_of_month,
+ &hour, &minute, &second, &time_sync, &op_mode,
+ &oscillator_offset, &time_mark_error, &time_bias, &leapsec);
+
+ if (sentence_type != PMVXG_D_TRECOVOUT)
+ return ("wrong rec-type");
+
+ switch (time_mark_valid) {
+ case 'T':
+ valid = 1;
+ break;
+ case 'F':
+ valid = 0;
+ break;
+ default:
+ return ("bad pulse-valid");
+ }
+
+ switch (time_sync) {
+ case 'G':
+ return ("synchronized to GPS; should be UTC");
+ case 'U':
+ break; /* UTC -> ok */
+ default:
+ return ("not synchronized to UTC");
+ }
+
+ /*
+ * Check for insane time (allow for possible leap seconds)
+ */
+ if (second > 60 || minute > 59 || hour > 23 ||
+ second < 0 || minute < 0 || hour < 0) {
+ mx4200_debug(peer,
+ "mx4200_parse_t: bad time %02d:%02d:%02d",
+ hour, minute, second);
+ if (leapsec != 0)
+ mx4200_debug(peer, " (leap %+d\n)", leapsec);
+ mx4200_debug(peer, "\n");
+ refclock_report(peer, CEVNT_BADTIME);
+ return ("bad time");
+ }
+ if ( second == 60 ) {
+ msyslog(LOG_DEBUG,
+ "mx4200: leap second! %02d:%02d:%02d",
+ hour, minute, second);
+ }
+
+ /*
+ * Check for insane date
+ * (Certainly can't be any year before this code was last altered!)
+ */
+ if (day_of_month > 31 || month > 12 ||
+ day_of_month < 1 || month < 1 || year < YEAR_LAST_MODIFIED) {
+ mx4200_debug(peer,
+ "mx4200_parse_t: bad date (%4d-%02d-%02d)\n",
+ year, month, day_of_month);
+ refclock_report(peer, CEVNT_BADDATE);
+ return ("bad date");
+ }
+
+ /*
+ * Silly Hack for MX4200:
+ * ASCII message is for *next* 1PPS signal, but we have the
+ * timestamp for the *last* 1PPS signal. So we have to subtract
+ * a second. Discard if we are on a month boundary to avoid
+ * possible leap seconds and leap days.
+ */
+ second--;
+ if (second < 0) {
+ second = 59;
+ minute--;
+ if (minute < 0) {
+ minute = 59;
+ hour--;
+ if (hour < 0) {
+ hour = 23;
+ day_of_month--;
+ if (day_of_month < 1) {
+ return ("sorry, month boundary");
+ }
+ }
+ }
+ }
+
+ /*
+ * Calculate Julian date
+ */
+ if (!(day_of_year = mx4200_jday(year, month, day_of_month))) {
+ mx4200_debug(peer,
+ "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n",
+ day_of_year, year, month, day_of_month);
+ refclock_report(peer, CEVNT_BADDATE);
+ return("invalid julian date");
+ }
+
+ /*
+ * Setup leap second indicator
+ */
+ switch (leapsec) {
+ case 0:
+ pp->leap = LEAP_NOWARNING;
+ break;
+ case 1:
+ pp->leap = LEAP_ADDSECOND;
+ break;
+ case -1:
+ pp->leap = LEAP_DELSECOND;
+ break;
+ default:
+ pp->leap = LEAP_NOTINSYNC;
+ }
+
+ /*
+ * Any change to the leap second warning status?
+ */
+ if (leapsec != up->last_leap ) {
+ msyslog(LOG_DEBUG,
+ "mx4200: leap second warning: %d to %d (%d)",
+ up->last_leap, leapsec, pp->leap);
+ }
+ up->last_leap = leapsec;
+
+ /*
+ * Copy time data for billboard monitoring.
+ */
+
+ pp->year = year;
+ pp->day = day_of_year;
+ pp->hour = hour;
+ pp->minute = minute;
+ pp->second = second;
+
+ /*
+ * Toss if sentence is marked invalid
+ */
+ if (!valid || pp->leap == LEAP_NOTINSYNC) {
+ mx4200_debug(peer, "mx4200_parse_t: time mark not valid\n");
+ refclock_report(peer, CEVNT_BADTIME);
+ return ("pulse invalid");
+ }
+
+ return (NULL);
+}
+
+/*
+ * Calculate the checksum
+ */
+static u_char
+mx4200_cksum(
+ register char *cp,
+ register int n
+ )
+{
+ register u_char ck;
+
+ for (ck = 0; n-- > 0; cp++)
+ ck ^= *cp;
+ return (ck);
+}
+
+/*
+ * Tables to compute the day of year. Viva la leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Calculate the the Julian Day
+ */
+static int
+mx4200_jday(
+ int year,
+ int month,
+ int day_of_month
+ )
+{
+ register int day, i;
+ int leap_year;
+
+ /*
+ * Is this a leap year ?
+ */
+ if (year % 4) {
+ leap_year = 0; /* FALSE */
+ } else {
+ if (year % 100) {
+ leap_year = 1; /* TRUE */
+ } else {
+ if (year % 400) {
+ leap_year = 0; /* FALSE */
+ } else {
+ leap_year = 1; /* TRUE */
+ }
+ }
+ }
+
+ /*
+ * Calculate the Julian Date
+ */
+ day = day_of_month;
+
+ if (leap_year) {
+ /* a leap year */
+ if (day > day2tab[month - 1]) {
+ return (0);
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ } else {
+ /* not a leap year */
+ if (day > day1tab[month - 1]) {
+ return (0);
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ }
+ return (day);
+}
+
+/*
+ * Parse a mx4200 position/height/velocity sentence.
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,021,SSSSSS.SS,DDMM.MMMM,N,DDDMM.MMMM,E,HHHHH.H,GGGG.G,EEEE.E,WWWW.W,MM
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 021=Position, Height Velocity Data
+ * This sentence gives the receiver position, height,
+ * navigation mode, and velocity north/east.
+ * *This sentence is intended for post-analysis
+ * applications.*
+ * 1 float UTC measurement time (seconds into week)
+ * 2 float WGS-84 Lattitude (degrees, minutes)
+ * 3 char N=North, S=South
+ * 4 float WGS-84 Longitude (degrees, minutes)
+ * 5 char E=East, W=West
+ * 6 float Altitude (meters above mean sea level)
+ * 7 float Geoidal height (meters)
+ * 8 float East velocity (m/sec)
+ * 9 float West Velocity (m/sec)
+ * 10 int Navigation Mode
+ * Mode if navigating:
+ * 1 = Position from remote device
+ * 2 = 2-D position
+ * 3 = 3-D position
+ * 4 = 2-D differential position
+ * 5 = 3-D differential position
+ * 6 = Static
+ * 8 = Position known -- reference station
+ * 9 = Position known -- Navigator
+ * Mode if not navigating:
+ * 51 = Too few satellites
+ * 52 = DOPs too large
+ * 53 = Position STD too large
+ * 54 = Velocity STD too large
+ * 55 = Too many iterations for velocity
+ * 56 = Too many iterations for position
+ * 57 = 3 sat startup failed
+ * 58 = Command abort
+ */
+static char *
+mx4200_parse_p(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ int sentence_type, mode;
+ double mtime, lat, lon, alt, geoid, vele, veln;
+ char north_south, east_west;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /* Should never happen! */
+ if (up->moving) return ("mobile platform - no pos!");
+
+ sscanf ( pp->a_lastcode,
+ "$PMVXG,%d,%lf,%lf,%c,%lf,%c,%lf,%lf,%lf,%lf,%d",
+ &sentence_type, &mtime, &lat, &north_south, &lon, &east_west,
+ &alt, &geoid, &vele, &veln, &mode);
+
+ /* Sentence type */
+ if (sentence_type != PMVXG_D_PHV)
+ return ("wrong rec-type");
+
+ /*
+ * return if not navigating
+ */
+ if (mode > 10)
+ return ("not navigating");
+ if (mode != 3 && mode != 5)
+ return ("not navigating in 3D");
+
+ /* Latitude (always +ve) and convert DDMM.MMMM to decimal */
+ if (lat < 0.0) return ("negative latitude");
+ if (lat > 9000.0) lat = 9000.0;
+ lat *= 0.01;
+ lat = ((int)lat) + (((lat - (int)lat)) * 1.6666666666666666);
+
+ /* North/South */
+ switch (north_south) {
+ case 'N':
+ break;
+ case 'S':
+ lat *= -1.0;
+ break;
+ default:
+ return ("invalid north/south indicator");
+ }
+
+ /* Longitude (always +ve) and convert DDDMM.MMMM to decimal */
+ if (lon < 0.0) return ("negative longitude");
+ if (lon > 180.0) lon = 180.0;
+ lon *= 0.01;
+ lon = ((int)lon) + (((lon - (int)lon)) * 1.6666666666666666);
+
+ /* East/West */
+ switch (east_west) {
+ case 'E':
+ break;
+ case 'W':
+ lon *= -1.0;
+ break;
+ default:
+ return ("invalid east/west indicator");
+ }
+
+ /*
+ * Normalize longitude to near 0 degrees.
+ * Assume all data are clustered around first reading.
+ */
+ if (up->central_meridian == NOT_INITIALIZED) {
+ up->central_meridian = lon;
+ mx4200_debug(peer,
+ "mx4200_receive: central meridian = %.9f \n",
+ up->central_meridian);
+ }
+ lon -= up->central_meridian;
+ if (lon < -180.0) lon += 360.0;
+ if (lon > 180.0) lon -= 360.0;
+
+ /*
+ * Calculate running averages
+ */
+
+ up->avg_lon = (up->N_fixes * up->avg_lon) + lon;
+ up->avg_lat = (up->N_fixes * up->avg_lat) + lat;
+ up->avg_alt = (up->N_fixes * up->avg_alt) + alt;
+
+ up->N_fixes += 1.0;
+
+ up->avg_lon /= up->N_fixes;
+ up->avg_lat /= up->N_fixes;
+ up->avg_alt /= up->N_fixes;
+
+ mx4200_debug(peer,
+ "mx4200_receive: position rdg %.0f: %.9f %.9f %.4f (CM=%.9f)\n",
+ up->N_fixes, lat, lon, alt, up->central_meridian);
+
+ return (NULL);
+}
+
+/*
+ * Parse a mx4200 Status sentence
+ * Parse a mx4200 Mode Data sentence
+ * Parse a mx4200 Software Configuration sentence
+ * Parse a mx4200 Time Recovery Parameters Currently in Use sentence
+ * (used only for logging raw strings)
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,000,XXX,XX,X,HHMM,X
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 000=Status.
+ * Returns status of the receiver to the controller.
+ * 1 Current Receiver Status:
+ * ACQ = Satellite re-acquisition
+ * ALT = Constellation selection
+ * COR = Providing corrections (for reference stations only)
+ * IAC = Initial acquisition
+ * IDL = Idle, no satellites
+ * NAV = Navigation
+ * STS = Search the Sky (no almanac available)
+ * TRK = Tracking
+ * 2 Number of satellites that should be visible
+ * 3 Number of satellites being tracked
+ * 4 Time since last navigation status if not currently navigating
+ * (hours, minutes)
+ * 5 Initialization status:
+ * 0 = Waiting for initialization parameters
+ * 1 = Initialization completed
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,004,C,R,D,H.HH,V.VV,TT,HHHH,VVVV,T
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 004=Software Configuration.
+ * Defines the navigation mode and criteria for
+ * acceptable navigation for the receiver.
+ * 1 Constrain Altitude Mode:
+ * 0 = Auto. Constrain altitude (2-D solution) and use
+ * manual altitude input when 3 sats avalable. Do
+ * not constrain altitude (3-D solution) when 4 sats
+ * available.
+ * 1 = Always constrain altitude (2-D solution).
+ * 2 = Never constrain altitude (3-D solution).
+ * 3 = Coast. Constrain altitude (2-D solution) and use
+ * last GPS altitude calculation when 3 sats avalable.
+ * Do not constrain altitude (3-D solution) when 4 sats
+ * available.
+ * 2 Altitude Reference: (always 0 for MX4200)
+ * 0 = Ellipsoid
+ * 1 = Geoid (MSL)
+ * 3 Differential Navigation Control:
+ * 0 = Disabled
+ * 1 = Enabled
+ * 4 Horizontal Acceleration Constant (m/sec**2)
+ * 5 Vertical Acceleration Constant (m/sec**2) (0 for MX4200)
+ * 6 Tracking Elevation Limit (degrees)
+ * 7 HDOP Limit
+ * 8 VDOP Limit
+ * 9 Time Output Mode:
+ * U = UTC
+ * L = Local time
+ * 10 Local Time Offset (minutes) (absent on MX4200)
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,030,NNNN,FFF
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 030=Software Configuration.
+ * This sentence contains the navigation processor
+ * and baseband firmware version numbers.
+ * 1 Nav Processor Version Number
+ * 2 Baseband Firmware Version Number
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,523,M,S,M,EEEE,BBBBBB,C,R
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 523=Time Recovery Parameters Currently in Use.
+ * This sentence contains the configuration of the
+ * time recovery feature of the receiver.
+ * 1 Time Recovery Mode:
+ * D = Dynamic; solve for position and time while moving
+ * S = Static; solve for position and time while stationary
+ * K = Known position input, solve for time only
+ * N = No time recovery
+ * 2 Time Synchronization:
+ * U = UTC time
+ * G = GPS time
+ * 3 Time Mark Mode:
+ * A = Always output a time pulse
+ * V = Only output time pulse if time is valid (as determined
+ * by Maximum Time Error)
+ * 4 Maximum Time Error - the maximum error (in nanoseconds) for
+ * which a time mark will be considered valid.
+ * 5 User Time Bias - external bias in nanoseconds
+ * 6 Time Message Control:
+ * 0 = Do not output the time recovery message
+ * 1 = Output the time recovery message (record 830) to
+ * Control port
+ * 2 = Output the time recovery message (record 830) to
+ * Equipment port
+ * 7 Reserved
+ * 8 Position Known PRN (absent on MX 4200)
+ *
+ */
+static char *
+mx4200_parse_s(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ int sentence_type;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ sscanf ( pp->a_lastcode, "$PMVXG,%d", &sentence_type);
+
+ /* Sentence type */
+ switch (sentence_type) {
+
+ case PMVXG_D_STATUS:
+ msyslog(LOG_DEBUG,
+ "mx4200: status: %s", pp->a_lastcode);
+ break;
+ case PMVXG_D_MODEDATA:
+ msyslog(LOG_DEBUG,
+ "mx4200: mode data: %s", pp->a_lastcode);
+ break;
+ case PMVXG_D_SOFTCONF:
+ msyslog(LOG_DEBUG,
+ "mx4200: firmware configuration: %s", pp->a_lastcode);
+ break;
+ case PMVXG_D_TRECOVUSEAGE:
+ msyslog(LOG_DEBUG,
+ "mx4200: time recovery parms: %s", pp->a_lastcode);
+ break;
+ default:
+ return ("wrong rec-type");
+ }
+
+ return (NULL);
+}
+
+/*
+ * Process a PPS signal, placing a timestamp in pp->lastrec.
+ */
+static int
+mx4200_pps(
+ struct peer *peer
+ )
+{
+ int temp_serial;
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ struct timespec timeout;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /*
+ * Grab the timestamp of the PPS signal.
+ */
+ temp_serial = up->pps_i.assert_sequence;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ if (time_pps_fetch(up->pps_h, PPS_TSFMT_TSPEC, &(up->pps_i),
+ &timeout) < 0) {
+ mx4200_debug(peer,
+ "mx4200_pps: time_pps_fetch: serial=%d, %s\n",
+ up->pps_i.assert_sequence, strerror(errno));
+ refclock_report(peer, CEVNT_FAULT);
+ return(1);
+ }
+ if (temp_serial == up->pps_i.assert_sequence) {
+ mx4200_debug(peer,
+ "mx4200_pps: assert_sequence serial not incrementing: %d\n",
+ up->pps_i.assert_sequence);
+ refclock_report(peer, CEVNT_FAULT);
+ return(1);
+ }
+ /*
+ * Check pps serial number against last one
+ */
+ if (up->lastserial + 1 != up->pps_i.assert_sequence &&
+ up->lastserial != 0) {
+ if (up->pps_i.assert_sequence == up->lastserial) {
+ mx4200_debug(peer, "mx4200_pps: no new pps event\n");
+ } else {
+ mx4200_debug(peer, "mx4200_pps: missed %d pps events\n",
+ up->pps_i.assert_sequence - up->lastserial - 1);
+ }
+ refclock_report(peer, CEVNT_FAULT);
+ }
+ up->lastserial = up->pps_i.assert_sequence;
+
+ /*
+ * Return the timestamp in pp->lastrec
+ */
+
+ pp->lastrec.l_ui = up->pps_i.assert_timestamp.tv_sec +
+ (u_int32) JAN_1970;
+ pp->lastrec.l_uf = ((double)(up->pps_i.assert_timestamp.tv_nsec) *
+ 4.2949672960) + 0.5;
+
+ return(0);
+}
+
+/*
+ * mx4200_debug - print debug messages
+ */
+#if defined(__STDC__)
+static void
+mx4200_debug(struct peer *peer, char *fmt, ...)
+#else
+static void
+mx4200_debug(peer, fmt, va_alist)
+ struct peer *peer;
+ char *fmt;
+#endif /* __STDC__ */
+{
+ va_list ap;
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ if (debug) {
+
+#if defined(__STDC__)
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif /* __STDC__ */
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+
+ /*
+ * Print debug message to stdout
+ * In the future, we may want to get get more creative...
+ */
+ vprintf(fmt, ap);
+
+ va_end(ap);
+ }
+}
+
+/*
+ * Send a character string to the receiver. Checksum is appended here.
+ */
+#if defined(__STDC__)
+static void
+mx4200_send(struct peer *peer, char *fmt, ...)
+#else
+static void
+mx4200_send(peer, fmt, va_alist)
+ struct peer *peer;
+ char *fmt;
+ va_dcl
+#endif /* __STDC__ */
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ register char *cp;
+ register int n, m;
+ va_list ap;
+ char buf[1024];
+ u_char ck;
+
+#if defined(__STDC__)
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif /* __STDC__ */
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ cp = buf;
+ *cp++ = '$';
+ n = VSNPRINTF((cp, sizeof(buf) - 1, fmt, ap));
+ ck = mx4200_cksum(cp, n);
+ cp += n;
+ ++n;
+ n += SNPRINTF((cp, sizeof(buf) - n - 5, "*%02X\r\n", ck));
+
+ m = write(pp->io.fd, buf, (unsigned)n);
+ if (m < 0)
+ msyslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf);
+ mx4200_debug(peer, "mx4200_send: %d %s\n", m, buf);
+ va_end(ap);
+}
+
+#else
+int refclock_mx4200_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_neoclock4x.c b/ntpd/refclock_neoclock4x.c
new file mode 100644
index 0000000..082b1cf
--- /dev/null
+++ b/ntpd/refclock_neoclock4x.c
@@ -0,0 +1,1068 @@
+/*
+ *
+ * Refclock_neoclock4x.c
+ * - NeoClock4X driver for DCF77 or FIA Timecode
+ *
+ * Date: 2003-07-07 v1.13
+ *
+ * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir
+ * for details about the NeoClock4X device
+ *
+ * Copyright (C) 2002-2003 by Linum Software GmbH <neoclock4x@linum.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined HAVE_SYS_MODEM_H
+# include <sys/modem.h>
+# define TIOCMSET MCSETA
+# define TIOCMGET MCGETA
+# define TIOCM_RTS MRTS
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# ifdef TERMIOS_NEEDS__SVID3
+# define _SVID3
+# endif
+# include <termios.h>
+# ifdef TERMIOS_NEEDS__SVID3
+# undef _SVID3
+# endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+/*
+ * If you want the driver for whatever reason to not use
+ * the TX line to send anything to your NeoClock4X
+ * device you must tell the NTP refclock driver which
+ * firmware you NeoClock4X device uses.
+ *
+ * If you want to enable this feature change the "#if 0"
+ * line to "#if 1" and make sure that the defined firmware
+ * matches the firmware off your NeoClock4X receiver!
+ *
+ */
+
+#if 0
+#define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A
+#endif
+
+#define NEOCLOCK4X_FIRMWARE_VERSION_A 'A'
+
+#define NEOCLOCK4X_TIMECODELEN 37
+
+#define NEOCLOCK4X_OFFSET_SERIAL 3
+#define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9
+#define NEOCLOCK4X_OFFSET_DAY 12
+#define NEOCLOCK4X_OFFSET_MONTH 14
+#define NEOCLOCK4X_OFFSET_YEAR 16
+#define NEOCLOCK4X_OFFSET_HOUR 18
+#define NEOCLOCK4X_OFFSET_MINUTE 20
+#define NEOCLOCK4X_OFFSET_SECOND 22
+#define NEOCLOCK4X_OFFSET_HSEC 24
+#define NEOCLOCK4X_OFFSET_DOW 26
+#define NEOCLOCK4X_OFFSET_TIMESOURCE 28
+#define NEOCLOCK4X_OFFSET_DSTSTATUS 29
+#define NEOCLOCK4X_OFFSET_QUARZSTATUS 30
+#define NEOCLOCK4X_OFFSET_ANTENNA1 31
+#define NEOCLOCK4X_OFFSET_ANTENNA2 33
+#define NEOCLOCK4X_OFFSET_CRC 35
+
+#define NEOCLOCK4X_DRIVER_VERSION "1.12 (2003-01-10)"
+
+struct neoclock4x_unit {
+ l_fp laststamp; /* last receive timestamp */
+ short unit; /* NTP refclock unit number */
+ u_long polled; /* flag to detect noreplies */
+ char leap_status; /* leap second flag */
+ int recvnow;
+
+ char firmware[80];
+ char firmwaretag;
+ char serial[7];
+ char radiosignal[4];
+ char timesource;
+ char dststatus;
+ char quarzstatus;
+ int antenna1;
+ int antenna2;
+ int utc_year;
+ int utc_month;
+ int utc_day;
+ int utc_hour;
+ int utc_minute;
+ int utc_second;
+ int utc_msec;
+};
+
+static int neoclock4x_start P((int, struct peer *));
+static void neoclock4x_shutdown P((int, struct peer *));
+static void neoclock4x_receive P((struct recvbuf *));
+static void neoclock4x_poll P((int, struct peer *));
+static void neoclock4x_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));
+
+static int neol_atoi_len P((const char str[], int *, int));
+static int neol_hexatoi_len P((const char str[], int *, int));
+static void neol_jdn_to_ymd P((unsigned long, int *, int *, int *));
+static void neol_localtime P((unsigned long, int* , int*, int*, int*, int*, int*));
+static unsigned long neol_mktime P((int, int, int, int, int, int));
+#if 0
+static void neol_mdelay P((int));
+#endif
+#if !defined(NEOCLOCK4X_FIRMWARE)
+static int neol_query_firmware P((int, int, char *, int));
+static int neol_check_firmware P((int, const char*, char *));
+#endif
+
+struct refclock refclock_neoclock4x = {
+ neoclock4x_start, /* start up driver */
+ neoclock4x_shutdown, /* shut down driver */
+ neoclock4x_poll, /* transmit poll message */
+ neoclock4x_control,
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+static int
+neoclock4x_start(int unit,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char dev[20];
+ int sl232;
+#if defined(HAVE_TERMIOS)
+ struct termios termsettings;
+#endif
+#if !defined(NEOCLOCK4X_FIRMWARE)
+ int tries;
+#endif
+
+ (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit);
+
+ /* LDISC_STD, LDISC_RAW
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ fd = refclock_open(dev, B2400, LDISC_CLK);
+ if(fd <= 0)
+ {
+ return (0);
+ }
+
+#if defined(HAVE_TERMIOS)
+ if(tcgetattr(fd, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ /* 2400 Baud 8N2 */
+ termsettings.c_cflag &= ~PARENB;
+ termsettings.c_cflag |= CSTOPB;
+ termsettings.c_cflag &= ~CSIZE;
+ termsettings.c_cflag |= CS8;
+
+ if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#elif defined(HAVE_SYSV_TTYS)
+ if(ioctl(fd, TCGETA, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ /* 2400 Baud 8N2 */
+ termsettings.c_cflag &= ~PARENB;
+ termsettings.c_cflag |= CSTOPB;
+ termsettings.c_cflag &= ~CSIZE;
+ termsettings.c_cflag |= CS8;
+
+ if(ioctl(fd, TCSETA, &termsettings) < 0)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#else
+ msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
+ (void) close(fd);
+ return (0);
+#endif
+
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+ /* turn on RTS, and DTR for power supply */
+ /* NeoClock4x is powered from serial line */
+ if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#ifdef TIOCM_RTS
+ sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */
+#else
+ sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */
+#endif
+ if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
+ (void) close(fd);
+ return (0);
+ }
+#else
+ msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
+ unit);
+ (void) close(fd);
+ return (0);
+#endif
+
+ up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
+ if(!(up))
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
+ (void) close(fd);
+ return (0);
+ }
+
+ memset((char *)up, 0, sizeof(struct neoclock4x_unit));
+ pp = peer->procptr;
+ pp->clockdesc = "NeoClock4X";
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = neoclock4x_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ /*
+ * no fudge time is given by user!
+ * use 169.583333 ms to compensate the serial line delay
+ * formula is:
+ * 2400 Baud / 11 bit = 218.18 charaters per second
+ * (NeoClock4X timecode len)
+ */
+ pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = -10;
+ peer->burst = NSTAGE;
+ memcpy((char *)&pp->refid, "neol", 4);
+
+ up->leap_status = 0;
+ up->unit = unit;
+ strcpy(up->firmware, "?");
+ up->firmwaretag = '?';
+ strcpy(up->serial, "?");
+ strcpy(up->radiosignal, "?");
+ up->timesource = '?';
+ up->dststatus = '?';
+ up->quarzstatus = '?';
+ up->antenna1 = -1;
+ up->antenna2 = -1;
+ up->utc_year = 0;
+ up->utc_month = 0;
+ up->utc_day = 0;
+ up->utc_hour = 0;
+ up->utc_minute = 0;
+ up->utc_second = 0;
+ up->utc_msec = 0;
+
+#if defined(NEOCLOCK4X_FIRMWARE)
+#if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
+ strcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)");
+ up->firmwaretag = 'A';
+#else
+ msyslog(LOG_EMERG, "NeoClock4X(%d): Unkown firmware defined at compile time for NeoClock4X",
+ unit);
+ (void) close(fd);
+ pp->io.fd = -1;
+ free(pp->unitptr);
+ pp->unitptr = NULL;
+ return (0);
+#endif
+#else
+ for(tries=0; tries < 5; tries++)
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
+ /* wait 3 seconds for receiver to power up */
+ sleep(3);
+ if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
+ {
+ break;
+ }
+ }
+
+ /* can I handle this firmware version? */
+ if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
+ {
+ (void) close(fd);
+ pp->io.fd = -1;
+ free(pp->unitptr);
+ pp->unitptr = NULL;
+ return (0);
+ }
+#endif
+
+ if(!io_addclock(&pp->io))
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
+ (void) close(fd);
+ pp->io.fd = -1;
+ free(pp->unitptr);
+ pp->unitptr = NULL;
+ return (0);
+ }
+
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
+
+ return (1);
+}
+
+static void
+neoclock4x_shutdown(int unit,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+ int sl232;
+
+ if(NULL != peer)
+ {
+ pp = peer->procptr;
+ if(pp != NULL)
+ {
+ up = (struct neoclock4x_unit *)pp->unitptr;
+ if(up != NULL)
+ {
+ if(-1 != pp->io.fd)
+ {
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+ /* turn on RTS, and DTR for power supply */
+ /* NeoClock4x is powered from serial line */
+ if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
+ unit);
+ }
+#ifdef TIOCM_RTS
+ /* turn on RTS, and DTR for power supply */
+ sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
+#else
+ /* turn on RTS, and DTR for power supply */
+ sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
+#endif
+ if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
+ unit);
+ }
+#endif
+ io_closeclock(&pp->io);
+ }
+ free(up);
+ pp->unitptr = NULL;
+ }
+ }
+ }
+
+ msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
+
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
+}
+
+static void
+neoclock4x_receive(struct recvbuf *rbufp)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ unsigned long calc_utc;
+ int day;
+ int month; /* ddd conversion */
+ int c;
+ int dsec;
+ unsigned char calc_chksum;
+ int recv_chksum;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct neoclock4x_unit *)pp->unitptr;
+
+ /* wait till poll interval is reached */
+ if(0 == up->recvnow)
+ return;
+
+ /* reset poll interval flag */
+ up->recvnow = 0;
+
+ /* read last received timecode */
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
+ pp->leap = LEAP_NOWARNING;
+
+ if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
+ up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
+
+ /* calculate checksum */
+ calc_chksum = 0;
+ for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
+ {
+ calc_chksum += pp->a_lastcode[c];
+ }
+ if(recv_chksum != calc_chksum)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
+ up->unit, pp->a_lastcode);
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /* Allow synchronization even is quartz clock is
+ * never initialized.
+ * WARNING: This is dangerous!
+ */
+ up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
+ if(0==(pp->sloppyclockflag & CLK_FLAG2))
+ {
+ if('I' != up->quarzstatus)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
+ up->unit, pp->a_lastcode);
+ pp->leap = LEAP_NOTINSYNC;
+ refclock_report(peer, CEVNT_BADDATE);
+ return;
+ }
+ }
+ if('I' != up->quarzstatus)
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
+ up->unit, pp->a_lastcode);
+ }
+
+ /*
+ * If NeoClock4X is not synchronized to a radio clock
+ * check if we're allowed to synchronize with the quartz
+ * clock.
+ */
+ up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
+ if(0==(pp->sloppyclockflag & CLK_FLAG2))
+ {
+ if('A' != up->timesource)
+ {
+ /* not allowed to sync with quartz clock */
+ if(0==(pp->sloppyclockflag & CLK_FLAG1))
+ {
+ refclock_report(peer, CEVNT_BADTIME);
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ }
+ }
+
+ /* this should only used when first install is done */
+ if(pp->sloppyclockflag & CLK_FLAG4)
+ {
+ msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
+ up->unit, pp->a_lastcode);
+ }
+
+ /* 123456789012345678901234567890123456789012345 */
+ /* S/N123456DCF1004021010001202ASX1213CR\r\n */
+
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
+ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2);
+ pp->nsec = dsec * 10000; /* convert 1/100s from neoclock to nanoseconds */
+
+ memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
+ up->radiosignal[3] = 0;
+ memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
+ up->serial[6] = 0;
+ up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
+ neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
+ neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
+
+ /*
+ Validate received values at least enough to prevent internal
+ array-bounds problems, etc.
+ */
+ if((pp->hour < 0) || (pp->hour > 23) ||
+ (pp->minute < 0) || (pp->minute > 59) ||
+ (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
+ (day < 1) || (day > 31) ||
+ (month < 1) || (month > 12) ||
+ (pp->year < 0) || (pp->year > 99)) {
+ /* Data out of range. */
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
+ up->unit, pp->a_lastcode);
+ refclock_report(peer, CEVNT_BADDATE);
+ return;
+ }
+
+ /* Year-2000 check not needed anymore. Same problem
+ * will arise at 2099 but what should we do...?
+ *
+ * wrap 2-digit date into 4-digit
+ *
+ * if(pp->year < YEAR_PIVOT)
+ * {
+ * pp->year += 100;
+ * }
+ */
+ pp->year += 2000;
+
+ /* adjust NeoClock4X local time to UTC */
+ calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
+ calc_utc -= 3600;
+ /* adjust NeoClock4X daylight saving time if needed */
+ if('S' == up->dststatus)
+ calc_utc -= 3600;
+ neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
+
+ /*
+ some preparations
+ */
+ pp->day = ymd2yd(pp->year, month, day);
+ pp->leap = 0;
+
+ if(pp->sloppyclockflag & CLK_FLAG4)
+ {
+ msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
+ up->unit,
+ pp->year, month, day,
+ pp->hour, pp->minute, pp->second, pp->nsec/1000);
+ }
+
+ up->utc_year = pp->year;
+ up->utc_month = month;
+ up->utc_day = day;
+ up->utc_hour = pp->hour;
+ up->utc_minute = pp->minute;
+ up->utc_second = pp->second;
+ up->utc_msec = pp->nsec/1000;
+
+ if(!refclock_process(pp))
+ {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ refclock_receive(peer);
+
+ /* report good status */
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+}
+
+static void
+neoclock4x_poll(int unit,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct neoclock4x_unit *)pp->unitptr;
+
+ pp->polls++;
+ up->recvnow = 1;
+}
+
+static void
+neoclock4x_control(int unit,
+ struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *peer)
+{
+ struct neoclock4x_unit *up;
+ struct refclockproc *pp;
+
+ if(NULL == peer)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
+ return;
+ }
+
+ pp = peer->procptr;
+ if(NULL == pp)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
+ return;
+ }
+
+ up = (struct neoclock4x_unit *)pp->unitptr;
+ if(NULL == up)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
+ return;
+ }
+
+ if(NULL != in)
+ {
+ /* check to see if a user supplied time offset is given */
+ if(in->haveflags & CLK_HAVETIME1)
+ {
+ pp->fudgetime1 = in->fudgetime1;
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
+ unit, pp->fudgetime1);
+ }
+
+ /* notify */
+ if(pp->sloppyclockflag & CLK_FLAG1)
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
+ }
+ else
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
+ }
+ }
+
+ if(NULL != out)
+ {
+ static char outstatus[800]; /* status output buffer */
+ char *tt;
+ char tmpbuf[80];
+
+ outstatus[0] = '\0';
+ out->kv_list = (struct ctl_var *)0;
+ out->type = REFCLK_NEOCLOCK4X;
+
+ snprintf(tmpbuf, sizeof(tmpbuf)-1,
+ "%04d-%02d-%02d %02d:%02d:%02d.%03d",
+ up->utc_year, up->utc_month, up->utc_day,
+ up->utc_hour, up->utc_minute, up->utc_second,
+ up->utc_msec);
+ tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
+ snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
+
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ if('A' == up->timesource)
+ snprintf(tt, 39, "timesource=\"radio\"");
+ else if('C' == up->timesource)
+ snprintf(tt, 39, "timesource=\"quartz\"");
+ else
+ snprintf(tt, 39, "timesource=\"unknown\"");
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ if('I' == up->quarzstatus)
+ snprintf(tt, 39, "quartzstatus=\"synchronized\"");
+ else if('X' == up->quarzstatus)
+ snprintf(tt, 39, "quartzstatus=\"not synchronized\"");
+ else
+ snprintf(tt, 39, "quartzstatus=\"unknown\"");
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ if('S' == up->dststatus)
+ snprintf(tt, 39, "dststatus=\"summer\"");
+ else if('W' == up->dststatus)
+ snprintf(tt, 39, "dststatus=\"winter\"");
+ else
+ snprintf(tt, 39, "dststatus=\"unknown\"");
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 79, "firmware=\"%s\"", up->firmware);
+ tt = add_var(&out->kv_list, 40, RO|DEF);
+ snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag);
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION);
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ snprintf(tt, 79, "serialnumber=\"%s\"", up->serial);
+ }
+}
+
+static int
+neol_hexatoi_len(const char str[],
+ int *result,
+ int maxlen)
+{
+ int hexdigit;
+ int i;
+ int n = 0;
+
+ for(i=0; isxdigit(str[i]) && i < maxlen; i++)
+ {
+ hexdigit = isdigit(str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10;
+ n = 16 * n + hexdigit;
+ }
+ *result = n;
+ return (n);
+}
+
+static int
+neol_atoi_len(const char str[],
+ int *result,
+ int maxlen)
+{
+ int digit;
+ int i;
+ int n = 0;
+
+ for(i=0; isdigit(str[i]) && i < maxlen; i++)
+ {
+ digit = str[i] - '0';
+ n = 10 * n + digit;
+ }
+ *result = n;
+ return (n);
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static unsigned long
+neol_mktime(int year,
+ int mon,
+ int day,
+ int hour,
+ int min,
+ int sec)
+{
+ if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */
+ mon += 12; /* Puts Feb last since it has leap day */
+ year -= 1;
+ }
+ return (((
+ (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+ year*365 - 719499
+ )*24 + hour /* now have hours */
+ )*60 + min /* now have minutes */
+ )*60 + sec; /* finally seconds */
+}
+
+static void
+neol_localtime(unsigned long utc,
+ int* year,
+ int* month,
+ int* day,
+ int* hour,
+ int* min,
+ int* sec)
+{
+ *sec = utc % 60;
+ utc /= 60;
+ *min = utc % 60;
+ utc /= 60;
+ *hour = utc % 24;
+ utc /= 24;
+
+ /* JDN Date 1/1/1970 */
+ neol_jdn_to_ymd(utc + 2440588L, year, month, day);
+}
+
+static void
+neol_jdn_to_ymd(unsigned long jdn,
+ int *yy,
+ int *mm,
+ int *dd)
+{
+ unsigned long x, z, m, d, y;
+ unsigned long daysPer400Years = 146097UL;
+ unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
+
+ x = jdn + 68569UL;
+ z = 4UL * x / daysPer400Years;
+ x = x - (daysPer400Years * z + 3UL) / 4UL;
+ y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
+ x = x - 1461UL * y / 4UL + 31UL;
+ m = 80UL * x / 2447UL;
+ d = x - 2447UL * m / 80UL;
+ x = m / 11UL;
+ m = m + 2UL - 12UL * x;
+ y = 100UL * (z - 49UL) + y + x;
+
+ *yy = (int)y;
+ *mm = (int)m;
+ *dd = (int)d;
+}
+
+#if 0
+/*
+ * delay in milliseconds
+ */
+static void
+neol_mdelay(int milliseconds)
+{
+ struct timeval tv;
+
+ if(milliseconds)
+ {
+ tv.tv_sec = 0;
+ tv.tv_usec = milliseconds * 1000;
+ select(1, NULL, NULL, NULL, &tv);
+ }
+}
+#endif
+
+#if !defined(NEOCLOCK4X_FIRMWARE)
+static int
+neol_query_firmware(int fd,
+ int unit,
+ char *firmware,
+ int maxlen)
+{
+ char tmpbuf[256];
+ int len;
+ int lastsearch;
+ unsigned char c;
+ int last_c_was_crlf;
+ int last_crlf_conv_len;
+ int init;
+ int read_errors;
+ int flag = 0;
+ int chars_read;
+
+ /* wait a little bit */
+ sleep(1);
+ if(-1 != write(fd, "V", 1))
+ {
+ /* wait a little bit */
+ sleep(1);
+ memset(tmpbuf, 0x00, sizeof(tmpbuf));
+
+ len = 0;
+ lastsearch = 0;
+ last_c_was_crlf = 0;
+ last_crlf_conv_len = 0;
+ init = 1;
+ read_errors = 0;
+ chars_read = 0;
+ for(;;)
+ {
+ if(read_errors > 5)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
+ strcpy(tmpbuf, "unknown due to timeout");
+ break;
+ }
+ if(chars_read > 500)
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
+ strcpy(tmpbuf, "unknown due to garbage input");
+ break;
+ }
+ if(-1 == read(fd, &c, 1))
+ {
+ if(EAGAIN != errno)
+ {
+ msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %s", unit ,strerror(errno));
+ read_errors++;
+ }
+ else
+ {
+ sleep(1);
+ }
+ continue;
+ }
+ else
+ {
+ chars_read++;
+ }
+
+ if(init)
+ {
+ if(0xA9 != c) /* wait for (c) char in input stream */
+ continue;
+
+ strcpy(tmpbuf, "(c)");
+ len = 3;
+ init = 0;
+ continue;
+ }
+
+#if 0
+ msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
+#endif
+
+ if(0x0A == c || 0x0D == c)
+ {
+ if(last_c_was_crlf)
+ {
+ char *ptr;
+ ptr = strstr(&tmpbuf[lastsearch], "S/N");
+ if(NULL != ptr)
+ {
+ tmpbuf[last_crlf_conv_len] = 0;
+ flag = 1;
+ break;
+ }
+ /* convert \n to / */
+ last_crlf_conv_len = len;
+ tmpbuf[len++] = ' ';
+ tmpbuf[len++] = '/';
+ tmpbuf[len++] = ' ';
+ lastsearch = len;
+ }
+ last_c_was_crlf = 1;
+ }
+ else
+ {
+ last_c_was_crlf = 0;
+ if(0x00 != c)
+ tmpbuf[len++] = (char) c;
+ }
+ tmpbuf[len] = '\0';
+ if(len > sizeof(tmpbuf)-5)
+ break;
+ }
+ }
+ else
+ {
+ msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
+ strcpy(tmpbuf, "unknown error");
+ }
+ strncpy(firmware, tmpbuf, maxlen);
+ firmware[maxlen] = '\0';
+
+ if(flag)
+ {
+ NLOG(NLOG_CLOCKINFO)
+ msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
+ }
+
+ return (flag);
+}
+
+static int
+neol_check_firmware(int unit,
+ const char *firmware,
+ char *firmwaretag)
+{
+ char *ptr;
+
+ *firmwaretag = '?';
+ ptr = strstr(firmware, "NDF:");
+ if(NULL != ptr)
+ {
+ if((strlen(firmware) - strlen(ptr)) >= 7)
+ {
+ if(':' == *(ptr+5) && '*' == *(ptr+6))
+ *firmwaretag = *(ptr+4);
+ }
+ }
+
+ if('A' != *firmwaretag)
+ {
+ msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
+ return (0);
+ }
+
+ return (1);
+}
+#endif
+
+#else
+int refclock_neoclock4x_bs;
+#endif /* REFCLOCK */
+
+/*
+ * History:
+ * refclock_neoclock4x.c
+ *
+ * 2002/04/27 cjh
+ * Revision 1.0 first release
+ *
+ * 2002/07/15 cjh
+ * preparing for bitkeeper reposity
+ *
+ * 2002/09/09 cjh
+ * Revision 1.1
+ * - don't assume sprintf returns an int anymore
+ * - change the way the firmware version is read
+ * - some customers would like to put a device called
+ * data diode to the NeoClock4X device to disable
+ * the write line. We need to now the firmware
+ * version even in this case. We made a compile time
+ * definition in this case. The code was previously
+ * only available on request.
+ *
+ * 2003/01/08 cjh
+ * Revision 1.11
+ * - changing xprinf to xnprinf to avoid buffer overflows
+ * - change some logic
+ * - fixed memory leaks if drivers can't initialize
+ *
+ * 2003/01/10 cjh
+ * Revision 1.12
+ * - replaced ldiv
+ * - add code to support FreeBSD
+ *
+ * 2003/07/07 cjh
+ * Revision 1.13
+ * - fix reporting of clock status
+ * changes. previously a bad clock
+ * status was never reset.
+ */
diff --git a/ntpd/refclock_nmea.c b/ntpd/refclock_nmea.c
new file mode 100644
index 0000000..28d6263
--- /dev/null
+++ b/ntpd/refclock_nmea.c
@@ -0,0 +1,723 @@
+/*
+ * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
+ * Michael Petry Jun 20, 1994
+ * based on refclock_heathn.c
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(SYS_WINNT)
+#undef close
+#define close closesocket
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_NMEA)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_PPSAPI
+# ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+# else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# endif
+# endif
+#endif /* HAVE_PPSAPI */
+
+/*
+ * This driver supports the NMEA GPS Receiver with
+ *
+ * Protype was refclock_trak.c, Thanks a lot.
+ *
+ * The receiver used spits out the NMEA sentences for boat navigation.
+ * And you thought it was an information superhighway. Try a raging river
+ * filled with rapids and whirlpools that rip away your data and warp time.
+ *
+ * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
+ * On startup if initialization of the PPSAPI fails, it will fall back
+ * to the "normal" timestamps.
+ *
+ * The PPSAPI part of the driver understands fudge flag2 and flag3. If
+ * flag2 is set, it will use the clear edge of the pulse. If flag3 is
+ * set, kernel hardpps is enabled.
+ *
+ * GPS sentences other than RMC (the default) may be enabled by setting
+ * the relevent bits of 'mode' in the server configuration line
+ * server 127.127.20.x mode X
+ *
+ * bit 0 - enables RMC (1)
+ * bit 1 - enables GGA (2)
+ * bit 2 - enables GLL (4)
+ * multiple sentences may be selected
+ */
+
+/*
+ * Definitions
+ */
+#ifdef SYS_WINNT
+# define DEVICE "COM%d:" /* COM 1 - 3 supported */
+#else
+# define DEVICE "/dev/gps%d" /* name of radio device */
+#endif
+#define SPEED232 B4800 /* uart speed (4800 bps) */
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "NMEA GPS Clock" /* who we are */
+#define NANOSECOND 1000000000 /* one second (ns) */
+#define RANGEGATE 500000 /* range gate (ns) */
+
+#define LENNMEA 75 /* min timecode length */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct nmeaunit {
+ int pollcnt; /* poll message counter */
+ int polled; /* Hand in a sample? */
+ l_fp tstamp; /* timestamp of last poll */
+#ifdef HAVE_PPSAPI
+ struct timespec ts; /* last timestamp */
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t handle; /* pps handlebars */
+#endif /* HAVE_PPSAPI */
+};
+
+/*
+ * Function prototypes
+ */
+static int nmea_start P((int, struct peer *));
+static void nmea_shutdown P((int, struct peer *));
+#ifdef HAVE_PPSAPI
+static void nmea_control P((int, struct refclockstat *, struct
+ refclockstat *, struct peer *));
+static int nmea_ppsapi P((struct peer *, int, int));
+static int nmea_pps P((struct nmeaunit *, l_fp *));
+#endif /* HAVE_PPSAPI */
+static void nmea_receive P((struct recvbuf *));
+static void nmea_poll P((int, struct peer *));
+static void gps_send P((int, const char *, struct peer *));
+static char *field_parse P((char *, int));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_nmea = {
+ nmea_start, /* start up driver */
+ nmea_shutdown, /* shut down driver */
+ nmea_poll, /* transmit poll message */
+#ifdef HAVE_PPSAPI
+ nmea_control, /* fudge control */
+#else
+ noentry, /* fudge control */
+#endif /* HAVE_PPSAPI */
+ noentry, /* initialize driver */
+ noentry, /* buginfo */
+ NOFLAGS /* not used */
+};
+
+/*
+ * nmea_start - open the GPS devices and initialize data for processing
+ */
+static int
+nmea_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+
+ fd = refclock_open(device, SPEED232, LDISC_CLK);
+ if (fd < 0)
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct nmeaunit *)emalloc(sizeof(struct nmeaunit));
+ if (up == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct nmeaunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = nmea_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
+
+#ifdef HAVE_PPSAPI
+ /*
+ * Start the PPSAPI interface if it is there. Default to use
+ * the assert edge and do not enable the kernel hardpps.
+ */
+ if (time_pps_create(fd, &up->handle) < 0) {
+ up->handle = 0;
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_create failed: %m");
+ return (1);
+ }
+ return(nmea_ppsapi(peer, 0, 0));
+#else
+ return (1);
+#endif /* HAVE_PPSAPI */
+}
+
+/*
+ * nmea_shutdown - shut down a GPS clock
+ */
+static void
+nmea_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+#ifdef HAVE_PPSAPI
+ if (up->handle != 0)
+ time_pps_destroy(up->handle);
+#endif /* HAVE_PPSAPI */
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+#ifdef HAVE_PPSAPI
+/*
+ * nmea_control - fudge control
+ */
+static void
+nmea_control(
+ int unit, /* unit (not used */
+ struct refclockstat *in, /* input parameters (not uded) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ nmea_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
+ pp->sloppyclockflag & CLK_FLAG3);
+}
+
+
+/*
+ * Initialize PPSAPI
+ */
+int
+nmea_ppsapi(
+ struct peer *peer, /* peer structure pointer */
+ int enb_clear, /* clear enable */
+ int enb_hardpps /* hardpps enable */
+ )
+{
+ struct refclockproc *pp;
+ struct nmeaunit *up;
+ int capability;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ if (time_pps_getcap(up->handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&up->pps_params, 0, sizeof(pps_params_t));
+ if (enb_clear)
+ up->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ up->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!up->pps_params.mode) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: invalid capture edge %d",
+ !enb_clear);
+ return (0);
+ }
+ up->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (enb_hardpps) {
+ if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
+ up->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_nmea: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+ peer->precision = PPS_PRECISION;
+
+#if DEBUG
+ if (debug) {
+ time_pps_getparams(up->handle, &up->pps_params);
+ printf(
+ "refclock_ppsapi: capability 0x%x version %d mode 0x%x kern %d\n",
+ capability, up->pps_params.api_version,
+ up->pps_params.mode, enb_hardpps);
+ }
+#endif
+
+ return (1);
+}
+
+/*
+ * Get PPSAPI timestamps.
+ *
+ * Return 0 on failure and 1 on success.
+ */
+static int
+nmea_pps(
+ struct nmeaunit *up,
+ l_fp *tsptr
+ )
+{
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+ l_fp tstmp;
+
+ /*
+ * Convert the timespec nanoseconds field to ntp l_fp units.
+ */
+ if (up->handle == 0)
+ return (0);
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
+ &timeout) < 0)
+ return (0);
+ if (up->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ up->pps_info.assert_sequence)
+ return (0);
+ ts = up->pps_info.assert_timestamp;
+ } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ up->pps_info.clear_sequence)
+ return (0);
+ ts = up->pps_info.clear_timestamp;
+ } else {
+ return (0);
+ }
+ if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec))
+ return (0);
+ up->ts = ts;
+
+ tstmp.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ tstmp.l_uf = (u_int32)dtemp;
+ *tsptr = tstmp;
+ return (1);
+}
+#endif /* HAVE_PPSAPI */
+
+/*
+ * nmea_receive - receive data from the serial interface
+ */
+static void
+nmea_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ int month, day;
+ int i;
+ char *cp, *dp;
+ int cmdtype;
+ /* Use these variables to hold data until we decide its worth keeping */
+ char rd_lastcode[BMAX];
+ l_fp rd_tmp;
+ u_short rd_lencode;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ rd_lencode = (u_short)refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
+
+ /*
+ * There is a case that a <CR><LF> gives back a "blank" line
+ */
+ if (rd_lencode == 0)
+ return;
+
+#ifdef DEBUG
+ if (debug)
+ printf("nmea: gpsread %d %s\n", rd_lencode,
+ rd_lastcode);
+#endif
+
+ /*
+ * We check the timecode format and decode its contents. The
+ * we only care about a few of them. The most important being
+ * the $GPRMC format
+ * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
+ * For Magellan (ColorTrak) GLL probably datum (order of sentences)
+ * also mode (0,1,2,3) select sentence ANY/ALL, RMC, GGA, GLL
+ * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
+ * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
+ * $GPRMB,...
+ * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
+ * $GPAPB,...
+ * $GPGSA,...
+ * $GPGSV,...
+ * $GPGSV,...
+ */
+#define GPXXX 0
+#define GPRMC 1
+#define GPGGA 2
+#define GPGLL 4
+ cp = rd_lastcode;
+ cmdtype=0;
+ if(strncmp(cp,"$GPRMC",6)==0) {
+ cmdtype=GPRMC;
+ }
+ else if(strncmp(cp,"$GPGGA",6)==0) {
+ cmdtype=GPGGA;
+ }
+ else if(strncmp(cp,"$GPGLL",6)==0) {
+ cmdtype=GPGLL;
+ }
+ else if(strncmp(cp,"$GPXXX",6)==0) {
+ cmdtype=GPXXX;
+ }
+ else
+ return;
+
+
+ /* See if I want to process this message type */
+ if ( ((peer->ttl == 0) && (cmdtype != GPRMC))
+ || ((peer->ttl != 0) && !(cmdtype & peer->ttl)) )
+ return;
+
+ pp->lencode = rd_lencode;
+ strcpy(pp->a_lastcode,rd_lastcode);
+ cp = pp->a_lastcode;
+
+ pp->lastrec = up->tstamp = rd_tmp;
+ up->pollcnt = 2;
+
+#ifdef DEBUG
+ if (debug)
+ printf("nmea: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+
+ /* Grab field depending on clock string type */
+ switch( cmdtype ) {
+ case GPRMC:
+ /*
+ * Test for synchronization. Check for quality byte.
+ */
+ dp = field_parse(cp,2);
+ if( dp[0] != 'A')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /* Now point at the time field */
+ dp = field_parse(cp,1);
+ break;
+
+
+ case GPGGA:
+ /*
+ * Test for synchronization. Check for quality byte.
+ */
+ dp = field_parse(cp,6);
+ if( dp[0] == '0')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /* Now point at the time field */
+ dp = field_parse(cp,1);
+ break;
+
+
+ case GPGLL:
+ /*
+ * Test for synchronization. Check for quality byte.
+ */
+ dp = field_parse(cp,6);
+ if( dp[0] != 'A')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /* Now point at the time field */
+ dp = field_parse(cp,5);
+ break;
+
+
+ case GPXXX:
+ return;
+ default:
+ return;
+
+ }
+
+ /*
+ * Check time code format of NMEA
+ */
+
+ if( !isdigit((int)dp[0]) ||
+ !isdigit((int)dp[1]) ||
+ !isdigit((int)dp[2]) ||
+ !isdigit((int)dp[3]) ||
+ !isdigit((int)dp[4]) ||
+ !isdigit((int)dp[5])
+ ) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+
+ /*
+ * Convert time and check values.
+ */
+ pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0';
+ pp->minute = ((dp[2] - '0') * 10) + dp[3] - '0';
+ pp->second = ((dp[4] - '0') * 10) + dp[5] - '0';
+ /* Default to 0 milliseconds, if decimal convert milliseconds in
+ one, two or three digits
+ */
+ pp->nsec = 0;
+ if (dp[6] == '.') {
+ if (isdigit((int)dp[7])) {
+ pp->nsec = (dp[7] - '0') * 100000000;
+ if (isdigit((int)dp[8])) {
+ pp->nsec += (dp[8] - '0') * 10000000;
+ if (isdigit((int)dp[9])) {
+ pp->nsec += (dp[9] - '0') * 1000000;
+ }
+ }
+ }
+ }
+
+ if (pp->hour > 23 || pp->minute > 59 || pp->second > 59
+ || pp->nsec > 1000000000) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+
+ /*
+ * Convert date and check values.
+ */
+ if (cmdtype==GPRMC) {
+ dp = field_parse(cp,9);
+ day = dp[0] - '0';
+ day = (day * 10) + dp[1] - '0';
+ month = dp[2] - '0';
+ month = (month * 10) + dp[3] - '0';
+ pp->year = dp[4] - '0';
+ pp->year = (pp->year * 10) + dp[5] - '0';
+ }
+ else {
+ /* only time */
+ time_t tt = time(NULL);
+ struct tm * t = gmtime(&tt);
+ day = t->tm_mday;
+ month = t->tm_mon + 1;
+ pp->year= t->tm_year;
+ }
+
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /* Hmmmm this will be a nono for 2100,2200,2300 but I don't think I'll be here */
+ /* good thing that 2000 is a leap year */
+ /* pp->year will be 00-99 if read from GPS, 00-> (years since 1900) from tm_year */
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+
+#ifdef HAVE_PPSAPI
+ /*
+ * If the PPSAPI is working, rather use its timestamps.
+ * assume that the PPS occurs on the second so blow any msec
+ */
+ if (nmea_pps(up, &rd_tmp) == 1) {
+ pp->lastrec = up->tstamp = rd_tmp;
+ pp->nsec = 0;
+ }
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time, in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+
+
+ /*
+ * Only go on if we had been polled.
+ */
+ if (!up->polled)
+ return;
+ up->polled = 0;
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+
+ /* If we get here - what we got from the clock is OK, so say so */
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+}
+
+/*
+ * nmea_poll - called by the transmit procedure
+ *
+ * We go to great pains to avoid changing state here, since there may be
+ * more than one eavesdropper receiving the same timecode.
+ */
+static void
+nmea_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ pp->polls++;
+ up->polled = 1;
+
+ /*
+ * usually nmea_receive can get a timestamp every second
+ */
+
+ gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
+}
+
+/*
+ *
+ * gps_send(fd,cmd, peer) Sends a command to the GPS receiver.
+ * as gps_send(fd,"rqts,u\r", peer);
+ *
+ * We don't currently send any data, but would like to send
+ * RTCM SC104 messages for differential positioning. It should
+ * also give us better time. Without a PPS output, we're
+ * Just fooling ourselves because of the serial code paths
+ *
+ */
+static void
+gps_send(
+ int fd,
+ const char *cmd,
+ struct peer *peer
+ )
+{
+
+ if (write(fd, cmd, strlen(cmd)) == -1) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+static char *
+field_parse(
+ char *cp,
+ int fn
+ )
+{
+ char *tp;
+ int i = fn;
+
+ for (tp = cp; *tp != '\0'; tp++) {
+ if (*tp == ',')
+ i--;
+ if (i == 0)
+ break;
+ }
+ return (++tp);
+}
+#else
+int refclock_nmea_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_oncore.c b/ntpd/refclock_oncore.c
new file mode 100644
index 0000000..14db92f
--- /dev/null
+++ b/ntpd/refclock_oncore.c
@@ -0,0 +1,3723 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * refclock_oncore.c
+ *
+ * Driver for some of the various the Motorola Oncore GPS receivers.
+ * should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T
+ * The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate
+ * than the others.
+ * The receivers without position hold (GT, GT+) will be less accurate.
+ *
+ * Tested with:
+ *
+ * (UT) (VP)
+ * COPYRIGHT 1991-1997 MOTOROLA INC. COPYRIGHT 1991-1996 MOTOROLA INC.
+ * SFTW P/N # 98-P36848P SFTW P/N # 98-P36830P
+ * SOFTWARE VER # 2 SOFTWARE VER # 8
+ * SOFTWARE REV # 2 SOFTWARE REV # 8
+ * SOFTWARE DATE APR 24 1998 SOFTWARE DATE 06 Aug 1996
+ * MODEL # R1121N1114 MODEL # B4121P1155
+ * HWDR P/N # 1 HDWR P/N # _
+ * SERIAL # R0010A SERIAL # SSG0226478
+ * MANUFACTUR DATE 6H07 MANUFACTUR DATE 7E02
+ * OPTIONS LIST IB
+ *
+ * (Basic) (M12)
+ * COPYRIGHT 1991-1994 MOTOROLA INC. COPYRIGHT 1991-2000 MOTOROLA INC.
+ * SFTW P/N # 98-P39949M SFTW P/N # 61-G10002A
+ * SOFTWARE VER # 5 SOFTWARE VER # 1
+ * SOFTWARE REV # 0 SOFTWARE REV # 3
+ * SOFTWARE DATE 20 JAN 1994 SOFTWARE DATE Mar 13 2000
+ * MODEL # A11121P116 MODEL # P143T12NR1
+ * HDWR P/N # _ HWDR P/N # 1
+ * SERIAL # SSG0049809 SERIAL # P003UD
+ * MANUFACTUR DATE 417AMA199 MANUFACTUR DATE 0C27
+ * OPTIONS LIST AB
+ *
+ * --------------------------------------------------------------------------
+ * This code uses the two devices
+ * /dev/oncore.serial.n
+ * /dev/oncore.pps.n
+ * which may be linked to the same device.
+ * and can read initialization data from the file
+ * /etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where
+ * n or N are the unit number, viz 127.127.30.N.
+ * --------------------------------------------------------------------------
+ * Reg.Clemens <reg@dwf.com> Sep98.
+ * Original code written for FreeBSD.
+ * With these mods it works on FreeBSD, SunOS, Solaris and Linux
+ * (SunOS 4.1.3 + ppsclock)
+ * (Solaris7 + MU4)
+ * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later).
+ *
+ * Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the
+ * state machine state) are printed to CLOCKSTATS if that file is enabled
+ * in /etc/ntp.conf.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13)
+ * doing an average of 10000 valid 2D and 3D fixes is what the automatic
+ * site survey mode does. Looking at the output from the receiver
+ * it seems like it is only using 3D fixes.
+ * When we do it ourselves, take 10000 3D fixes.
+ */
+
+#define POS_HOLD_AVERAGE 10000 /* nb, 10000s ~= 2h45m */
+
+/*
+ * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a
+ * "STATUS" line in the oncore config file, which contains the most recent
+ * copy of all types of messages we recognize. This file can be mmap(2)'ed
+ * by monitoring and statistics programs.
+ *
+ * See separate HTML documentation for this option.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ONCORE) && defined(HAVE_PPSAPI)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#ifdef ONCORE_SHMEM_STATUS
+# ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+# ifndef MAP_FAILED
+# define MAP_FAILED ((u_char *) -1)
+# endif /* not MAP_FAILED */
+# endif /* HAVE_SYS_MMAN_H */
+#endif /* ONCORE_SHMEM_STATUS */
+
+#ifdef HAVE_PPSAPI
+# ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+# else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# endif
+# endif
+#endif
+
+#ifdef HAVE_SYS_SIO_H
+# include <sys/sio.h>
+#endif
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+#ifndef HAVE_STRUCT_PPSCLOCKEV
+struct ppsclockev {
+# ifdef HAVE_STRUCT_TIMESPEC
+ struct timespec tv;
+# else
+ struct timeval tv;
+# endif
+ u_int serial;
+};
+#endif /* not HAVE_STRUCT_PPSCLOCKEV */
+
+enum receive_state {
+ ONCORE_NO_IDEA,
+ ONCORE_CHECK_ID,
+ ONCORE_CHECK_CHAN,
+ ONCORE_HAVE_CHAN,
+ ONCORE_RESET_SENT,
+ ONCORE_TEST_SENT,
+ ONCORE_INIT,
+ ONCORE_ALMANAC,
+ ONCORE_RUN
+};
+
+enum site_survey_state {
+ ONCORE_SS_UNKNOWN,
+ ONCORE_SS_TESTING,
+ ONCORE_SS_HW,
+ ONCORE_SS_SW,
+ ONCORE_SS_DONE
+};
+
+enum antenna_state {
+ ONCORE_ANTENNA_UNKNOWN = -1,
+ ONCORE_ANTENNA_OK = 0,
+ ONCORE_ANTENNA_OC = 1,
+ ONCORE_ANTENNA_UC = 2,
+ ONCORE_ANTENNA_NV = 3
+};
+
+/* Model Name, derived from the @@Cj message.
+ * Used to initialize some variables.
+ */
+
+enum oncore_model {
+ ONCORE_BASIC,
+ ONCORE_PVT6,
+ ONCORE_VP,
+ ONCORE_UT,
+ ONCORE_UTPLUS,
+ ONCORE_GT,
+ ONCORE_GTPLUS,
+ ONCORE_SL,
+ ONCORE_M12,
+ ONCORE_UNKNOWN
+};
+
+/* the bits that describe these properties are in the same place
+ * on the VP/UT, but have moved on the M12. As such we extract
+ * them, and use them from this struct.
+ *
+ */
+
+struct RSM {
+ u_char posn0D;
+ u_char posn2D;
+ u_char posn3D;
+ u_char bad_almanac;
+ u_char bad_fix;
+};
+
+/* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to
+ * see what mode it is in. The bits on the M12 are multiplexed with
+ * other messages, so we have to 'keep' the last known mode here.
+ */
+
+enum posn_mode {
+ MODE_UNKNOWN,
+ MODE_0D,
+ MODE_2D,
+ MODE_3D
+};
+
+struct instance {
+ int unit; /* 127.127.30.unit */
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ int ttyfd; /* TTY file descriptor */
+ int ppsfd; /* PPS file descriptor */
+ int shmemfd; /* Status shm descriptor */
+#ifdef HAVE_PPSAPI
+ pps_handle_t pps_h;
+ pps_params_t pps_p;
+#endif
+ enum receive_state o_state; /* Receive state */
+ enum posn_mode mode; /* 0D, 2D, 3D */
+ enum site_survey_state site_survey; /* Site Survey state */
+ enum antenna_state ant_state; /* antenna state */
+
+ int Bj_day;
+
+ u_long delay; /* ns */
+ long offset; /* ns */
+
+ u_char *shmem;
+ char *shmem_fname;
+ u_int shmem_Cb;
+ u_int shmem_Ba;
+ u_int shmem_Ea;
+ u_int shmem_Ha;
+ u_char shmem_reset;
+ u_char shmem_Posn;
+ u_char shmem_bad_Ea;
+ u_char almanac_from_shmem;
+
+ double ss_lat;
+ double ss_long;
+ double ss_ht;
+ double dH;
+ int ss_count;
+ u_char posn_set;
+
+ enum oncore_model model;
+ u_int version;
+ u_int revision;
+
+ u_char chan; /* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */
+ s_char traim; /* do we have traim? yes UT/VP, no BASIC, GT, M12+T, -1 unknown, 0 no, +1 yes */
+
+ /* the following 7 are all timing counters */
+ u_char traim_delay; /* seconds counter, waiting for reply */
+ u_char count; /* cycles thru Ea before starting */
+ u_char count1; /* cycles thru Ea after SS_TESTING, waiting for SS_HW */
+ u_char count2; /* cycles thru Ea after count, to check for @@Ea */
+ u_char count3; /* cycles thru Ea checking for # channels */
+ u_char count4; /* cycles thru leap after Gj to issue Bj */
+ u_char pollcnt;
+ u_char timeout; /* count to retry Cj after Fa self-test */
+
+ struct RSM rsm; /* bits extracted from Receiver Status Msg in @@Ea */
+ u_char printed;
+ u_char polled;
+ u_long ev_serial;
+ int Rcvptr;
+ u_char Rcvbuf[500];
+ u_char BEHa[160]; /* Ba, Ea or Ha */
+ u_char BEHn[80]; /* Bn , En , or Hn */
+ u_char Cj[300];
+ u_char Ag; /* Satellite mask angle */
+ u_char saw_At;
+ u_char saw_Ay;
+ u_char saw_Az;
+ s_char saw_Gj;
+ u_char have_dH;
+ u_char init_type;
+ s_char saw_tooth;
+ s_char chan_in; /* chan number from INPUT, will always use it */
+ u_char chan_id; /* chan number determined from part number */
+ u_char chan_ck; /* chan number determined by sending commands to hardware */
+ s_char traim_in; /* TRAIM from INPUT, will always use it */
+ s_char traim_id; /* TRAIM determined from part number */
+ u_char traim_ck; /* TRAIM determined by sending commands to hardware */
+ u_char once; /* one pass code at top of BaEaHa */
+ s_char assert;
+ u_char hardpps;
+};
+
+#define rcvbuf instance->Rcvbuf
+#define rcvptr instance->Rcvptr
+
+static int oncore_start P((int, struct peer *));
+static void oncore_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));
+static void oncore_poll P((int, struct peer *));
+static void oncore_shutdown P((int, struct peer *));
+static void oncore_consume P((struct instance *));
+static void oncore_read_config P((struct instance *));
+static void oncore_receive P((struct recvbuf *));
+static int oncore_ppsapi P((struct instance *));
+static void oncore_get_timestamp P((struct instance *, long, long));
+static void oncore_init_shmem P((struct instance *));
+
+static void oncore_antenna_report P((struct instance *, enum antenna_state));
+static void oncore_chan_test P((struct instance *));
+static void oncore_check_almanac P((struct instance *));
+static void oncore_check_antenna P((struct instance *));
+static void oncore_check_leap_sec P((struct instance *));
+static int oncore_checksum_ok P((u_char *, int));
+static void oncore_compute_dH P((struct instance *));
+static void oncore_load_almanac P((struct instance *));
+static void oncore_print_Cb P((struct instance *, u_char *));
+/* static void oncore_print_array P((u_char *, int)); */
+static void oncore_print_posn P((struct instance *));
+static void oncore_sendmsg P((int, u_char *, size_t));
+static void oncore_set_posn P((struct instance *));
+static void oncore_set_traim P((struct instance *));
+static void oncore_shmem_get_3D P((struct instance *));
+static void oncore_ss P((struct instance *));
+static int oncore_wait_almanac P((struct instance *));
+
+static void oncore_msg_any P((struct instance *, u_char *, size_t, int));
+static void oncore_msg_Adef P((struct instance *, u_char *, size_t));
+static void oncore_msg_Ag P((struct instance *, u_char *, size_t));
+static void oncore_msg_As P((struct instance *, u_char *, size_t));
+static void oncore_msg_At P((struct instance *, u_char *, size_t));
+static void oncore_msg_Ay P((struct instance *, u_char *, size_t));
+static void oncore_msg_Az P((struct instance *, u_char *, size_t));
+static void oncore_msg_BaEaHa P((struct instance *, u_char *, size_t));
+static void oncore_msg_Bd P((struct instance *, u_char *, size_t));
+static void oncore_msg_Bj P((struct instance *, u_char *, size_t));
+static void oncore_msg_BnEnHn P((struct instance *, u_char *, size_t));
+static void oncore_msg_CaFaIa P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cb P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cf P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cj P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cj_id P((struct instance *, u_char *, size_t));
+static void oncore_msg_Cj_init P((struct instance *, u_char *, size_t));
+static void oncore_msg_Ga P((struct instance *, u_char *, size_t));
+static void oncore_msg_Gb P((struct instance *, u_char *, size_t));
+static void oncore_msg_Gd P((struct instance *, u_char *, size_t));
+static void oncore_msg_Gj P((struct instance *, u_char *, size_t));
+static void oncore_msg_Sz P((struct instance *, u_char *, size_t));
+
+struct refclock refclock_oncore = {
+ oncore_start, /* start up driver */
+ oncore_shutdown, /* shut down driver */
+ oncore_poll, /* transmit poll message */
+ oncore_control, /* fudge (flag) control messages */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+/*
+ * Understanding the next bit here is not easy unless you have a manual
+ * for the the various Oncore Models.
+ */
+
+static struct msg_desc {
+ const char flag[3];
+ const int len;
+ void (*handler) P((struct instance *, u_char *, size_t));
+ const char *fmt;
+ int shmem;
+} oncore_messages[] = {
+ /* Ea and En first since they're most common */
+ { "Ea", 76, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" },
+ { "Ba", 68, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC" },
+ { "Ha", 154, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC" },
+ { "Bn", 59, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC" },
+ { "En", 69, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" },
+ { "Hn", 78, oncore_msg_BnEnHn, "" },
+ { "Ab", 10, 0, "" },
+ { "Ac", 11, 0, "" },
+ { "Ad", 11, oncore_msg_Adef, "" },
+ { "Ae", 11, oncore_msg_Adef, "" },
+ { "Af", 15, oncore_msg_Adef, "" },
+ { "Ag", 8, oncore_msg_Ag, "" }, /* Satellite mask angle */
+ { "As", 20, oncore_msg_As, "" },
+ { "At", 8, oncore_msg_At, "" },
+ { "Au", 12, 0, "" },
+ { "Av", 8, 0, "" },
+ { "Aw", 8, 0, "" },
+ { "Ay", 11, oncore_msg_Ay, "" },
+ { "Az", 11, oncore_msg_Az, "" },
+ { "AB", 8, 0, "" },
+ { "Bb", 92, 0, "" },
+ { "Bd", 23, oncore_msg_Bd, "" },
+ { "Bj", 8, oncore_msg_Bj, "" },
+ { "Ca", 9, oncore_msg_CaFaIa, "" },
+ { "Cb", 33, oncore_msg_Cb, "" },
+ { "Cf", 7, oncore_msg_Cf, "" },
+ { "Cg", 8, 0, "" },
+ { "Ch", 9, 0, "" },
+ { "Cj", 294, oncore_msg_Cj, "" },
+ { "Ek", 71, 0, "" },
+ { "Fa", 9, oncore_msg_CaFaIa, "" },
+ { "Ga", 20, oncore_msg_Ga, "" },
+ { "Gb", 17, oncore_msg_Gb, "" },
+ { "Gc", 8, 0, "" },
+ { "Gd", 8, oncore_msg_Gd, "" },
+ { "Ge", 8, 0, "" },
+ { "Gj", 21, oncore_msg_Gj, "" },
+ { "Ia", 10, oncore_msg_CaFaIa, "" },
+ { "Sz", 8, oncore_msg_Sz, "" },
+ { {0}, 7, 0, "" }
+};
+
+
+static u_char oncore_cmd_Aa[] = { 'A', 'a', 0, 0, 0 }; /* 6/8 Time of Day */
+static u_char oncore_cmd_Ab[] = { 'A', 'b', 0, 0, 0 }; /* 6/8 GMT Correction */
+static u_char oncore_cmd_AB[] = { 'A', 'B', 4 }; /* VP Application Type: Static */
+static u_char oncore_cmd_Ac[] = { 'A', 'c', 0, 0, 0, 0 }; /* 6/8 Date */
+static u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 }; /* 6/8 Latitude */
+static u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 }; /* 6/8 Longitude */
+static u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 }; /* 6/8 Height */
+static u_char oncore_cmd_Ag[] = { 'A', 'g', 0 }; /* 6/8/12 Satellite Mask Angle */
+static u_char oncore_cmd_Agx[] = { 'A', 'g', 0xff }; /* 6/8/12 Satellite Mask Angle: read */
+static u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 6/8/12 Posn Hold Parameters */
+static u_char oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff, /* 6/8/12 Posn Hold Readback */
+ 0x7f,0xff,0xff,0xff, /* on UT+ this doesnt work with 0xff */
+ 0x7f,0xff,0xff,0xff, 0xff }; /* but does work with 0x7f (sigh). */
+static u_char oncore_cmd_At0[] = { 'A', 't', 0 }; /* 6/8 Posn Hold: off */
+static u_char oncore_cmd_At1[] = { 'A', 't', 1 }; /* 6/8 Posn Hold: on */
+static u_char oncore_cmd_At2[] = { 'A', 't', 2 }; /* 6/8 Posn Hold: Start Site Survey */
+static u_char oncore_cmd_Atx[] = { 'A', 't', 0xff }; /* 6/8 Posn Hold: Read Back */
+static u_char oncore_cmd_Au[] = { 'A', 'u', 0,0,0,0, 0 }; /* GT/M12 Altitude Hold Ht. */
+static u_char oncore_cmd_Av0[] = { 'A', 'v', 0 }; /* VP/GT Altitude Hold: off */
+static u_char oncore_cmd_Av1[] = { 'A', 'v', 1 }; /* VP/GT Altitude Hold: on */
+static u_char oncore_cmd_Aw[] = { 'A', 'w', 1 }; /* 6/8/12 UTC/GPS time selection */
+static u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 }; /* Timing 1PPS time offset: set */
+static u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff }; /* Timing 1PPS time offset: Read */
+static u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 }; /* 6/8UT/12 1PPS Cable Delay: set */
+static u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff }; /* 6/8UT/12 1PPS Cable Delay: Read */
+static u_char oncore_cmd_Ba0[] = { 'B', 'a', 0 }; /* 6 Position/Data/Status: off */
+static u_char oncore_cmd_Ba[] = { 'B', 'a', 1 }; /* 6 Position/Data/Status: on */
+static u_char oncore_cmd_Bb[] = { 'B', 'b', 1 }; /* 6/8/12 Visible Satellites */
+static u_char oncore_cmd_Bd[] = { 'B', 'd', 1 }; /* 6/8/12? Almanac Status Msg. */
+static u_char oncore_cmd_Be[] = { 'B', 'e', 1 }; /* 6/8/12 Request Almanac Data */
+static u_char oncore_cmd_Bj[] = { 'B', 'j', 0 }; /* 6/8 Leap Second Pending */
+static u_char oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim on */
+static u_char oncore_cmd_Bn[] = { 'B', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg on traim on */
+static u_char oncore_cmd_Bnx[] = { 'B', 'n', 1, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg on traim off */
+static u_char oncore_cmd_Ca[] = { 'C', 'a' }; /* 6 Self Test */
+static u_char oncore_cmd_Cf[] = { 'C', 'f' }; /* 6/8/12 Set to Defaults */
+static u_char oncore_cmd_Cg[] = { 'C', 'g', 1 }; /* VP Posn Fix/Idle Mode */
+static u_char oncore_cmd_Cj[] = { 'C', 'j' }; /* 6/8/12 Receiver ID */
+static u_char oncore_cmd_Ea0[] = { 'E', 'a', 0 }; /* 8 Position/Data/Status: off */
+static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 }; /* 8 Position/Data/Status: on */
+static u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */ /* 8 Posn/Status/Data - extension */
+static u_char oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim on */
+static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg on traim on */
+static u_char oncore_cmd_Enx[] = { 'E', 'n', 1, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg on traim off */
+static u_char oncore_cmd_Fa[] = { 'F', 'a' }; /* 8 Self Test */
+static u_char oncore_cmd_Ga[] = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 12 Position Set */
+static u_char oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff, /* 12 Position Set: Read */
+ 0xff, 0xff, 0xff, 0xff, /* */
+ 0xff, 0xff, 0xff, 0xff, 0xff }; /* */
+static u_char oncore_cmd_Gb[] = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* 12 set Date/Time */
+static u_char oncore_cmd_Gc[] = { 'G', 'c', 1 }; /* 12 PPS Control: On Cont */
+static u_char oncore_cmd_Gd0[] = { 'G', 'd', 0 }; /* 12 Position Control: 3D (no hold) */
+static u_char oncore_cmd_Gd1[] = { 'G', 'd', 1 }; /* 12 Position Control: 0D (3D hold) */
+static u_char oncore_cmd_Gd2[] = { 'G', 'd', 2 }; /* 12 Position Control: 2D (Alt Hold) */
+static u_char oncore_cmd_Gd3[] = { 'G', 'd', 3 }; /* 12 Position Coltrol: Start Site Survey */
+static u_char oncore_cmd_Ge0[] = { 'G', 'e', 0 }; /* M12+T TRAIM: off */
+static u_char oncore_cmd_Ge[] = { 'G', 'e', 1 }; /* M12+T TRAIM: on */
+static u_char oncore_cmd_Gj[] = { 'G', 'j' }; /* 8?/12 Leap Second Pending */
+static u_char oncore_cmd_Ha0[] = { 'H', 'a', 0 }; /* 12 Position/Data/Status: off */
+static u_char oncore_cmd_Ha[] = { 'H', 'a', 1 }; /* 12 Position/Data/Status: on */
+static u_char oncore_cmd_Hn0[] = { 'H', 'n', 0 }; /* 12 TRAIM Status: off */
+static u_char oncore_cmd_Hn[] = { 'H', 'n', 1 }; /* 12 TRAIM Status: on */
+static u_char oncore_cmd_Ia[] = { 'I', 'a' }; /* 12 Self Test */
+
+/* it appears that as of 1997/1998, the UT had As,At, but not Au,Av
+ * the GT had Au,Av, but not As,At
+ * This was as of v2.0 of both firmware sets. possibly 1.3 for UT.
+ * Bj in UT at v1.3
+ * dont see Bd in UT/GT thru 1999
+ * Gj in UT as of 3.0, 1999 , Bj as of 1.3
+ */
+
+static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jly",
+ "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+#define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */
+#define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */
+#define INIT_FILE "/etc/ntp.oncore" /* optional init file */
+
+#define SPEED B9600 /* Oncore Binary speed (9600 bps) */
+
+/*
+ * Assemble and disassemble 32bit signed quantities from a buffer.
+ *
+ */
+
+ /* to buffer, int w, u_char *buf */
+#define w32_buf(buf,w) { u_int i_tmp; \
+ i_tmp = (w<0) ? (~(-w)+1) : (w); \
+ (buf)[0] = (i_tmp >> 24) & 0xff; \
+ (buf)[1] = (i_tmp >> 16) & 0xff; \
+ (buf)[2] = (i_tmp >> 8) & 0xff; \
+ (buf)[3] = (i_tmp ) & 0xff; \
+ }
+
+#define w32(buf) (((buf)[0]&0xff) << 24 | \
+ ((buf)[1]&0xff) << 16 | \
+ ((buf)[2]&0xff) << 8 | \
+ ((buf)[3]&0xff) )
+
+ /* from buffer, char *buf, result to an int */
+#define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf))
+
+
+/*
+ * oncore_start - initialize data for processing
+ */
+
+static int
+oncore_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct instance *instance;
+ struct refclockproc *pp;
+ int fd1, fd2;
+ char device1[30], device2[30];
+ const char *cp;
+ struct stat stat1, stat2;
+
+ /* OPEN DEVICES */
+ /* opening different devices for fd1 and fd2 presents no problems */
+ /* opening the SAME device twice, seems to be OS dependent.
+ (a) on Linux (no streams) no problem
+ (b) on SunOS (and possibly Solaris, untested), (streams)
+ never see the line discipline.
+ Since things ALWAYS work if we only open the device once, we check
+ to see if the two devices are in fact the same, then proceed to
+ do one open or two.
+ */
+
+ (void)sprintf(device1, DEVICE1, unit);
+ (void)sprintf(device2, DEVICE2, unit);
+
+ if (stat(device1, &stat1)) {
+ perror("ONCORE: stat fd1");
+ exit(1);
+ }
+
+ if (stat(device2, &stat2)) {
+ perror("ONCORE: stat fd2");
+ exit(1);
+ }
+
+ /* create instance structure for this unit */
+
+ if (!(instance = (struct instance *) malloc(sizeof *instance))) {
+ perror("malloc");
+ return (0);
+ }
+ memset((char *) instance, 0, sizeof *instance);
+
+ if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) {
+ /* same device here */
+ if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW
+#if !defined(HAVE_PPSAPI) && !defined(TIOCDCDTIMESTAMP)
+ | LDISC_PPS
+#endif
+ ))) {
+ perror("ONCORE: fd1");
+ exit(1);
+ }
+ fd2 = fd1;
+ } else { /* different devices here */
+ if (!(fd1=refclock_open(device1, SPEED, LDISC_RAW))) {
+ perror("ONCORE: fd1");
+ exit(1);
+ }
+ if ((fd2=open(device2, O_RDWR)) < 0) {
+ perror("ONCORE: fd2");
+ exit(1);
+ }
+ }
+
+ /* initialize miscellaneous variables */
+
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t) instance;
+ instance->pp = pp;
+ instance->unit = unit;
+ instance->peer = peer;
+ instance->assert = 1;
+ instance->once = 1;
+
+ cp = "ONCORE DRIVER -- CONFIGURING";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->o_state = ONCORE_NO_IDEA;
+ cp = "state = ONCORE_NO_IDEA";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->ttyfd = fd1;
+ instance->ppsfd = fd2;
+
+ instance->Bj_day = -1;
+ instance->traim = -1;
+ instance->traim_in = -1;
+ instance->chan_in = -1;
+ instance->model = ONCORE_UNKNOWN;
+ instance->mode = MODE_UNKNOWN;
+ instance->site_survey = ONCORE_SS_UNKNOWN;
+ instance->Ag = 0xff; /* Satellite mask angle, unset by user */
+ instance->ant_state = ONCORE_ANTENNA_UNKNOWN;
+
+ peer->precision = -26;
+ peer->minpoll = 4;
+ peer->maxpoll = 4;
+ pp->clockdesc = "Motorola Oncore GPS Receiver";
+ memcpy((char *)&pp->refid, "GPS\0", (size_t) 4);
+
+ /* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */
+
+ oncore_read_config(instance);
+
+#ifdef HAVE_PPSAPI
+ if (time_pps_create(fd2, &instance->pps_h) < 0) {
+ perror("time_pps_create");
+ return(0);
+ }
+
+ if (instance->assert)
+ cp = "Initializing timing to Assert.";
+ else
+ cp = "Initializing timing to Clear.";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ if (instance->hardpps) {
+ cp = "HARDPPS Set.";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+
+ if (!oncore_ppsapi(instance))
+ return(0);
+#endif
+
+ pp->io.clock_recv = oncore_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd1;
+ if (!io_addclock(&pp->io)) {
+ perror("io_addclock");
+ (void) close(fd1);
+ free(instance);
+ return (0);
+ }
+
+#ifdef ONCORE_SHMEM_STATUS
+ /*
+ * Before starting ONCORE, lets setup SHMEM
+ * This will include merging an old SHMEM into the new one if
+ * an old one is found.
+ */
+
+ oncore_init_shmem(instance);
+#endif
+
+ /*
+ * This will return the Model of the Oncore receiver.
+ * and start the Initialization loop in oncore_msg_Cj.
+ */
+
+ instance->o_state = ONCORE_CHECK_ID;
+ cp = "state = ONCORE_CHECK_ID";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->timeout = 4;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+
+ instance->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * Fudge control (get Flag2 and Flag3, not available at oncore_start time.
+ */
+
+static void
+oncore_control(
+ int unit, /* unit (not used) */
+ struct refclockstat *in, /* input parameters (not used) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ char *cp;
+ struct refclockproc *pp;
+ struct instance *instance;
+
+ pp = peer->procptr;
+ instance = (struct instance *) pp->unitptr;
+
+ instance->assert = !(pp->sloppyclockflag & CLK_FLAG2);
+ instance->hardpps = pp->sloppyclockflag & CLK_FLAG3;
+
+ if (instance->assert)
+ cp = "Resetting timing to Assert.";
+ else
+ cp = "Resetting timing to Clear.";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ if (instance->hardpps) {
+ cp = "HARDPPS Set.";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+
+ (void) oncore_ppsapi(instance);
+}
+
+
+
+/*
+ * oncore_shutdown - shut down the clock
+ */
+
+static void
+oncore_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct instance *instance;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ instance = (struct instance *) pp->unitptr;
+
+ io_closeclock(&pp->io);
+
+ close(instance->ttyfd);
+ close(instance->ppsfd);
+ if (instance->shmemfd)
+ close(instance->shmemfd);
+ free(instance);
+}
+
+
+
+/*
+ * oncore_poll - called by the transmit procedure
+ */
+
+static void
+oncore_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct instance *instance;
+
+ instance = (struct instance *) peer->procptr->unitptr;
+ if (instance->timeout) {
+ char *cp;
+
+ instance->timeout--;
+ if (instance->timeout == 0) {
+ cp = "Oncore: No response from @@Cj, shutting down driver";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ oncore_shutdown(unit, peer);
+ } else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ cp = "Oncore: Resend @@Cj";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+ return;
+ }
+
+ if (!instance->pollcnt)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ instance->pollcnt--;
+ peer->procptr->polls++;
+ instance->polled = 1;
+}
+
+
+
+/*
+ * Initialize PPSAPI
+ */
+
+#ifdef HAVE_PPSAPI
+static int
+oncore_ppsapi(
+ struct instance *instance
+ )
+{
+ int mode;
+
+ if (time_pps_getcap(instance->pps_h, &mode) < 0) {
+ msyslog(LOG_ERR, "refclock_ioctl: time_pps_getcap failed: %m");
+ return (0);
+ }
+
+ if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) {
+ msyslog(LOG_ERR, "refclock_ioctl: time_pps_getparams failed: %m");
+ return (0);
+ }
+
+ /* nb. only turn things on, if someone else has turned something
+ * on before we get here, leave it alone!
+ */
+
+ if (instance->assert) { /* nb, default or ON */
+ instance->pps_p.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
+ instance->pps_p.assert_offset.tv_sec = 0;
+ instance->pps_p.assert_offset.tv_nsec = 0;
+ } else {
+ instance->pps_p.mode = PPS_CAPTURECLEAR | PPS_OFFSETCLEAR;
+ instance->pps_p.clear_offset.tv_sec = 0;
+ instance->pps_p.clear_offset.tv_nsec = 0;
+ }
+ instance->pps_p.mode |= PPS_TSFMT_TSPEC;
+ instance->pps_p.mode &= mode; /* only set what is legal */
+
+ if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
+ perror("time_pps_setparams");
+ exit(1);
+ }
+
+ /* If HARDPPS is on, we tell kernel */
+
+ if (instance->hardpps) {
+ int i;
+
+ if (instance->assert)
+ i = PPS_CAPTUREASSERT;
+ else
+ i = PPS_CAPTURECLEAR;
+
+ if (i&mode) {
+ if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR, "refclock_ioctl: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+ }
+ return(1);
+}
+#endif
+
+
+
+#ifdef ONCORE_SHMEM_STATUS
+static void
+oncore_init_shmem(
+ struct instance *instance
+ )
+{
+ int i, l, n, fd, shmem_old_size, n1;
+ char *buf, Msg[160];
+ u_char *cp, *cp1, *shmem_old;
+ struct msg_desc *mp;
+ struct stat sbuf;
+ size_t shmem_length;
+
+ /*
+ * The first thing we do is see if there is an instance->shmem_fname file (still)
+ * out there from a previous run. If so, we copy it in and use it to initialize
+ * shmem (so we won't lose our almanac if we need it).
+ */
+
+ shmem_old = 0;
+ if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0)
+ perror("LOAD:SHMEM");
+ else {
+ fstat(fd, &sbuf);
+ shmem_old_size = sbuf.st_size;
+ shmem_old = (u_char *) malloc((unsigned) sbuf.st_size);
+ if (shmem_old == NULL) {
+ perror("malloc");
+ close(fd);
+ return;
+ }
+
+ read(fd, shmem_old, shmem_old_size);
+ close(fd);
+ }
+
+ /* OK, we now create the NEW SHMEM. */
+
+ if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
+ perror(instance->shmem_fname);
+ return;
+ }
+
+ /* see how big it needs to be */
+
+ n = 1;
+ for (mp=oncore_messages; mp->flag[0]; mp++) {
+ mp->shmem = n;
+ /* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */
+ if (!strcmp(mp->flag, "Cb")) {
+ instance->shmem_Cb = n;
+ n += (mp->len + 3) * 34;
+ }
+ if (!strcmp(mp->flag, "Ba")) {
+ instance->shmem_Ba = n;
+ n += (mp->len + 3) * 3;
+ }
+ if (!strcmp(mp->flag, "Ea")) {
+ instance->shmem_Ea = n;
+ n += (mp->len + 3) * 3;
+ }
+ if (!strcmp(mp->flag, "Ha")) {
+ instance->shmem_Ha = n;
+ n += (mp->len + 3) * 3;
+ }
+ n += (mp->len + 3);
+ }
+ shmem_length = n + 2;
+ fprintf(stderr, "ONCORE: SHMEM length: %d bytes\n", (int) shmem_length);
+
+ buf = malloc(shmem_length);
+ if (buf == NULL) {
+ perror("malloc");
+ close(instance->shmemfd);
+ return;
+ }
+
+ memset(buf, 0, shmem_length);
+
+ /* next build the new SHMEM buffer in memory */
+
+ for (mp=oncore_messages; mp->flag[0]; mp++) {
+ l = mp->shmem;
+ buf[l + 0] = mp->len >> 8;
+ buf[l + 1] = mp->len & 0xff;
+ buf[l + 2] = 0;
+ buf[l + 3] = '@';
+ buf[l + 4] = '@';
+ buf[l + 5] = mp->flag[0];
+ buf[l + 6] = mp->flag[1];
+ if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) {
+ if (!strcmp(mp->flag, "Cb"))
+ n = 35;
+ else
+ n = 4;
+ for (i=1; i<n; i++) {
+ buf[l + i * (mp->len+3) + 0] = mp->len >> 8;
+ buf[l + i * (mp->len+3) + 1] = mp->len & 0xff;
+ buf[l + i * (mp->len+3) + 2] = 0;
+ buf[l + i * (mp->len+3) + 3] = '@';
+ buf[l + i * (mp->len+3) + 4] = '@';
+ buf[l + i * (mp->len+3) + 5] = mp->flag[0];
+ buf[l + i * (mp->len+3) + 6] = mp->flag[1];
+ }
+ }
+ }
+
+ /* we now walk thru the two buffers (shmem_old and buf, soon to become shmem)
+ * copying the data in shmem_old to buf. When we are done we write it out
+ * and free both buffers.
+ * If the structures change (an addition or deletion) I will stop copying.
+ * The two will be the same unless we add/subtract from the oncore_messages list
+ * so this should work most of the time, and takes a lot less code than doing it right.
+ */
+
+ if (shmem_old) {
+ for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3), cp1+=(n+3)) {
+ n1 = 256*(*(cp1-3)) + *(cp1-2);
+ if (n1 != n || strncmp(cp, cp1, 4))
+ break;
+
+ memcpy(cp, cp1, (size_t) n);
+ }
+ free(shmem_old);
+ }
+
+ i = write(instance->shmemfd, buf, shmem_length);
+ free(buf);
+
+ if (i != shmem_length) {
+ perror(instance->shmem_fname);
+ close(instance->shmemfd);
+ return;
+ }
+
+ instance->shmem = (u_char *) mmap(0, shmem_length,
+ PROT_READ | PROT_WRITE,
+#ifdef MAP_HASSEMAPHORE
+ MAP_HASSEMAPHORE |
+#endif
+ MAP_SHARED, instance->shmemfd, (off_t)0);
+
+ if (instance->shmem == (u_char *)MAP_FAILED) {
+ instance->shmem = 0;
+ close(instance->shmemfd);
+ return;
+ }
+
+ sprintf(Msg, "SHMEM (size = %d) is CONFIGURED and available as %s", shmem_length, instance->shmem_fname);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+#endif /* ONCORE_SHMEM_STATUS */
+
+
+
+/*
+ * Read Input file if it exists.
+ */
+
+static void
+oncore_read_config(
+ struct instance *instance
+ )
+{
+/*
+ * First we try to open the configuration file
+ * /etc/oncoreN
+ * where N is the unit number viz 127.127.30.N.
+ * If we don't find it we try
+ * /etc/ntp.oncore.N
+ * and then
+ * /etc/ntp.oncore
+ *
+ * If we don't find any then we don't have the cable delay or PPS offset
+ * and we choose MODE (4) below.
+ *
+ * Five Choices for MODE
+ * (0) ONCORE is preinitialized, don't do anything to change it.
+ * nb, DON'T set 0D mode, DON'T set Delay, position...
+ * (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode.
+ * (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position,
+ * lock this in, go to 0D mode.
+ * (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode.
+ * (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position,
+ * lock this in, go to 0D mode.
+ * NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY]
+ * then this position is set as the INITIAL position of the ONCORE.
+ * This can reduce the time to first fix.
+ * -------------------------------------------------------------------------------
+ * Note that an Oncore UT without a battery backup retains NO information if it is
+ * power cycled, with a Battery Backup it remembers the almanac, etc.
+ * For an Oncore VP, there is an eeprom that will contain this data, along with the
+ * option of Battery Backup.
+ * So a UT without Battery Backup is equivalent to doing a HARD RESET on each
+ * power cycle, since there is nowhere to store the data.
+ * -------------------------------------------------------------------------------
+ *
+ * If we open one or the other of the files, we read it looking for
+ * MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, HARDPPS,
+ * STATUS, POSN3D, POSN2D, CHAN, TRAIM
+ * then initialize using method MODE. For Mode = (1,3) all of (LAT, LON, HT) must
+ * be present or mode reverts to (2,4).
+ *
+ * Read input file.
+ *
+ * # is comment to end of line
+ * = allowed between 1st and 2nd fields.
+ *
+ * Expect to see one line with 'MODE' as first field, followed by an integer
+ * in the range 0-4 (default = 4).
+ *
+ * Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields.
+ * All numbers are floating point.
+ * DDD.ddd
+ * DDD MMM.mmm
+ * DDD MMM SSS.sss
+ *
+ * Expect to see one line with 'HT' as first field,
+ * followed by 1-2 fields. First is a number, the second is 'FT' or 'M'
+ * for feet or meters. HT is the height above the GPS ellipsoid.
+ * If the receiver reports height in both GPS and MSL, then we will report
+ * the difference GPS-MSL on the clockstats file.
+ *
+ * There is an optional line, starting with DELAY, followed
+ * by 1 or two fields. The first is a number (a time) the second is
+ * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
+ * DELAY is cable delay, typically a few tens of ns.
+ *
+ * There is an optional line, starting with OFFSET, followed
+ * by 1 or two fields. The first is a number (a time) the second is
+ * 'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
+ * OFFSET is the offset of the PPS pulse from 0. (only fully implemented
+ * with the PPSAPI, we need to be able to tell the Kernel about this
+ * offset if the Kernel PLL is in use, but can only do this presently
+ * when using the PPSAPI interface. If not using the Kernel PLL,
+ * then there is no problem.
+ *
+ * There is an optional line, with either ASSERT or CLEAR on it, which
+ * determine which transition of the PPS signal is used for timing by the
+ * PPSAPI. If neither is present, then ASSERT is assumed.
+ * ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input.
+ * For Flag2, ASSERT=0, and hence is default.
+ *
+ * There is an optional line, with HARDPPS on it. Including this line causes
+ * the PPS signal to control the kernel PLL.
+ * HARDPPS can also be set with FLAG3 of the ntp.conf input.
+ * For Flag3, 0 is disabled, and the default.
+ *
+ * There are three options that have to do with using the shared memory option.
+ * First, to enable the option there must be a SHMEM line with a file name.
+ * The file name is the file associated with the shared memory.
+ *
+ * In shared memory, there is one 'record' for each returned variable.
+ * For the @@Ea data there are three 'records' containing position data.
+ * There will always be data in the record corresponding to the '0D' @@Ea record,
+ * and the user has a choice of filling the '3D' record by specifying POSN3D,
+ * or the '2D' record by specifying POSN2D. In either case the '2D' or '3D'
+ * record is filled once every 15s.
+ *
+ * Two additional variables that can be set are CHAN and TRAIM. These should be
+ * set correctly by the code examining the @@Cj record, but we bring them out here
+ * to allow the user to override either the # of channels, or the existence of TRAIM.
+ * CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be
+ * followed by YES or NO.
+ *
+ * There is an optional line with MASK on it followed by one integer field in the
+ * range 0 to 89. This sets the satellite mask angle and will determine the minimum
+ * elevation angle for satellites to be tracked by the receiver. The default value
+ * is 10 deg for the VP and 0 deg for all other receivers.
+ *
+ * So acceptable input would be
+ * # these are my coordinates (RWC)
+ * LON -106 34.610
+ * LAT 35 08.999
+ * HT 1589 # could equally well say HT 5215 FT
+ * DELAY 60 ns
+ */
+
+ FILE *fd;
+ char *cp, *cc, *ca, line[100], units[2], device[20], Msg[160];
+ int i, sign, lat_flg, long_flg, ht_flg, mode, mask;
+ double f1, f2, f3;
+
+ sprintf(device, "%s%d", INIT_FILE, instance->unit); /* try "ntp.oncore0" first */
+ if ((fd=fopen(device, "r")) == NULL) { /* it was in the original documentation */
+ sprintf(device, "%s.%d", INIT_FILE, instance->unit); /* then try "ntp.oncore.0 */
+ if ((fd=fopen(device, "r")) == NULL) {
+ if ((fd=fopen(INIT_FILE, "r")) == NULL) { /* and finally "ntp.oncore" */
+ instance->init_type = 4;
+ return;
+ }
+ }
+ }
+
+ mode = mask = 0;
+ lat_flg = long_flg = ht_flg = 0;
+ while (fgets(line, 100, fd)) {
+
+ /* Remove comments */
+ if ((cp = strchr(line, '#')))
+ *cp = '\0';
+
+ /* Remove trailing space */
+ for (i = strlen(line);
+ i > 0 && isascii((int)line[i - 1]) && isspace((int)line[i - 1]);
+ )
+ line[--i] = '\0';
+
+ /* Remove leading space */
+ for (cc = line; *cc && isascii((int)*cc) && isspace((int)*cc); cc++)
+ continue;
+
+ /* Stop if nothing left */
+ if (!*cc)
+ continue;
+
+ /* Uppercase the command and find the arg */
+ for (ca = cc; *ca; ca++) {
+ if (isascii((int)*ca)) {
+ if (islower((int)*ca)) {
+ *ca = toupper(*ca);
+ } else if (isspace((int)*ca) || (*ca == '='))
+ break;
+ }
+ }
+
+ /* Remove space (and possible =) leading the arg */
+ for (; *ca && isascii((int)*ca) && (isspace((int)*ca) || (*ca == '=')); ca++)
+ continue;
+
+ if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) {
+ i = strlen(ca);
+ instance->shmem_fname = (char *) malloc((unsigned) (i+1));
+ strcpy(instance->shmem_fname, ca);
+ continue;
+ }
+
+ /* Uppercase argument as well */
+ for (cp = ca; *cp; cp++)
+ if (isascii((int)*cp) && islower((int)*cp))
+ *cp = toupper(*cp);
+
+ if (!strncmp(cc, "LAT", (size_t) 3)) {
+ f1 = f2 = f3 = 0;
+ sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
+ sign = 1;
+ if (f1 < 0) {
+ f1 = -f1;
+ sign = -1;
+ }
+ instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
+ lat_flg++;
+ } else if (!strncmp(cc, "LON", (size_t) 3)) {
+ f1 = f2 = f3 = 0;
+ sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
+ sign = 1;
+ if (f1 < 0) {
+ f1 = -f1;
+ sign = -1;
+ }
+ instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
+ long_flg++;
+ } else if (!strncmp(cc, "HT", (size_t) 2)) {
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(ca, "%lf %1s", &f1, units);
+ if (units[0] == 'F')
+ f1 = 0.3048 * f1;
+ instance->ss_ht = 100 * f1; /* cm */
+ ht_flg++;
+ } else if (!strncmp(cc, "DELAY", (size_t) 5)) {
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(ca, "%lf %1s", &f1, units);
+ if (units[0] == 'N')
+ ;
+ else if (units[0] == 'U')
+ f1 = 1000 * f1;
+ else if (units[0] == 'M')
+ f1 = 1000000 * f1;
+ else
+ f1 = 1000000000 * f1;
+ if (f1 < 0 || f1 > 1.e9)
+ f1 = 0;
+ if (f1 < 0 || f1 > 999999) {
+ sprintf(Msg, "PPS Cable delay of %fns out of Range, ignored", f1);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ } else
+ instance->delay = f1; /* delay in ns */
+ } else if (!strncmp(cc, "OFFSET", (size_t) 6)) {
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(ca, "%lf %1s", &f1, units);
+ if (units[0] == 'N')
+ ;
+ else if (units[0] == 'U')
+ f1 = 1000 * f1;
+ else if (units[0] == 'M')
+ f1 = 1000000 * f1;
+ else
+ f1 = 1000000000 * f1;
+ if (f1 < 0 || f1 > 1.e9)
+ f1 = 0;
+ if (f1 < 0 || f1 > 999999999.) {
+ sprintf(Msg, "PPS Offset of %fns out of Range, ignored", f1);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ } else
+ instance->offset = f1; /* offset in ns */
+ } else if (!strncmp(cc, "MODE", (size_t) 4)) {
+ sscanf(ca, "%d", &mode);
+ if (mode < 0 || mode > 4)
+ mode = 4;
+ } else if (!strncmp(cc, "ASSERT", (size_t) 6)) {
+ instance->assert = 1;
+ } else if (!strncmp(cc, "CLEAR", (size_t) 5)) {
+ instance->assert = 0;
+ } else if (!strncmp(cc, "HARDPPS", (size_t) 7)) {
+ instance->hardpps = 1;
+ } else if (!strncmp(cc, "POSN2D", (size_t) 6)) {
+ instance->shmem_Posn = 2;
+ } else if (!strncmp(cc, "POSN3D", (size_t) 6)) {
+ instance->shmem_Posn = 3;
+ } else if (!strncmp(cc, "CHAN", (size_t) 4)) {
+ sscanf(ca, "%d", &i);
+ if ((i == 6) || (i == 8) || (i == 12))
+ instance->chan_in = i;
+ } else if (!strncmp(cc, "TRAIM", (size_t) 5)) {
+ instance->traim_in = 1; /* so TRAIM alone is YES */
+ if (!strcmp(ca, "NO") || !strcmp(ca, "OFF")) /* Yes/No, On/Off */
+ instance->traim_in = 0;
+ } else if (!strncmp(cc, "MASK", (size_t) 4)) {
+ sscanf(ca, "%d", &mask);
+ if (mask > -1 && mask < 90)
+ instance->Ag = mask; /* Satellite mask angle */
+ }
+ }
+ fclose(fd);
+
+ /*
+ * OK, have read all of data file, and extracted the good stuff.
+ * If lat/long/ht specified they ALL must be specified for mode = (1,3).
+ */
+
+ instance->posn_set = 1;
+ if (!( lat_flg && long_flg && ht_flg )) {
+ printf("ONCORE: incomplete data on %s\n", INIT_FILE);
+ instance->posn_set = 0;
+ if (mode == 1 || mode == 3) {
+ sprintf(Msg, "Input Mode = %d, but no/incomplete position, mode set to %d", mode, mode+1);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ mode++;
+ }
+ }
+ instance->init_type = mode;
+
+ sprintf(Msg, "Input mode = %d", mode);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/*
+ * move data from NTP to buffer (toss the extra in the unlikely case it won't fit)
+ */
+
+static void
+oncore_receive(
+ struct recvbuf *rbufp
+ )
+{
+ size_t i;
+ u_char *p;
+ struct peer *peer;
+ struct instance *instance;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ instance = (struct instance *) peer->procptr->unitptr;
+ p = (u_char *) &rbufp->recv_space;
+
+#if 0
+ if (debug > 4) {
+ int i;
+ printf("ONCORE: >>>");
+ for(i=0; i<rbufp->recv_length; i++)
+ printf("%02x ", p[i]);
+ printf("\n");
+ printf("ONCORE: >>>");
+ for(i=0; i<rbufp->recv_length; i++)
+ printf("%03o ", p[i]);
+ printf("\n");
+ }
+#endif
+
+ i = rbufp->recv_length;
+ if (rcvbuf+rcvptr+i > &rcvbuf[sizeof rcvbuf])
+ i = sizeof(rcvbuf) - rcvptr; /* and some char will be lost */
+ memcpy(rcvbuf+rcvptr, p, i);
+ rcvptr += i;
+ oncore_consume(instance);
+}
+
+
+
+/*
+ * Deal with any complete messages
+ */
+
+static void
+oncore_consume(
+ struct instance *instance
+ )
+{
+ int i, m;
+ unsigned l;
+
+ while (rcvptr >= 7) {
+ if (rcvbuf[0] != '@' || rcvbuf[1] != '@') {
+ /* We're not in sync, lets try to get there */
+ for (i=1; i < rcvptr-1; i++)
+ if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@')
+ break;
+ if (debug > 4)
+ printf("ONCORE[%d]: >>> skipping %d chars\n", instance->unit, i);
+ if (i != rcvptr)
+ memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i));
+ rcvptr -= i;
+ continue;
+ }
+
+ /* Ok, we have a header now */
+ l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1;
+ for(m=0; m<l; m++)
+ if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), (size_t) 2))
+ break;
+ if (m == l) {
+ if (debug > 4)
+ printf("ONCORE[%d]: >>> Unknown MSG, skipping 4 (%c%c)\n", instance->unit, rcvbuf[2], rcvbuf[3]);
+ memcpy(rcvbuf, rcvbuf+4, (size_t) 4);
+ rcvptr -= 4;
+ continue;
+ }
+
+ l = oncore_messages[m].len;
+#if 0
+ if (debug > 3)
+ printf("ONCORE[%d]: GOT: %c%c %d of %d entry %d\n", instance->unit, rcvbuf[2], rcvbuf[3], rcvptr, l, m);
+#endif
+ /* Got the entire message ? */
+
+ if (rcvptr < l)
+ return;
+
+ /* are we at the end of message? should be <Cksum><CR><LF> */
+
+ if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') {
+ if (debug)
+ printf("ONCORE[%d]: NO <CR><LF> at end of message\n", instance->unit);
+ } else { /* check the CheckSum */
+ if (oncore_checksum_ok(rcvbuf, l)) {
+ if (instance->shmem != NULL) {
+ instance->shmem[oncore_messages[m].shmem + 2]++;
+ memcpy(instance->shmem + oncore_messages[m].shmem + 3,
+ rcvbuf, (size_t) l);
+ }
+ oncore_msg_any(instance, rcvbuf, (size_t) (l-3), m);
+ if (oncore_messages[m].handler)
+ oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3));
+ } else if (debug) {
+ printf("ONCORE[%d]: Checksum mismatch!\n", instance->unit);
+ printf("ONCORE[%d]: @@%c%c ", instance->unit, rcvbuf[2], rcvbuf[3]);
+ for (i=4; i<l; i++)
+ printf("%03o ", rcvbuf[i]);
+ printf("\n");
+ }
+ }
+
+ if (l != rcvptr)
+ memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l));
+ rcvptr -= l;
+ }
+}
+
+
+
+static void
+oncore_get_timestamp(
+ struct instance *instance,
+ long dt1, /* tick offset THIS time step */
+ long dt2 /* tick offset NEXT time step */
+ )
+{
+ int Rsm;
+ u_long i, j;
+ l_fp ts, ts_tmp;
+ double dmy;
+#ifdef HAVE_STRUCT_TIMESPEC
+ struct timespec *tsp = 0;
+#else
+ struct timeval *tsp = 0;
+#endif
+#ifdef HAVE_PPSAPI
+ int current_mode;
+ pps_params_t current_params;
+ struct timespec timeout;
+ pps_info_t pps_i;
+#else /* ! HAVE_PPSAPI */
+#ifdef HAVE_CIOGETEV
+ struct ppsclockev ev;
+ int r = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ struct ppsclockev ev;
+ int r = TIOCGPPSEV;
+#endif
+#if TIOCDCDTIMESTAMP
+ struct timeval tv;
+#endif
+#endif /* ! HAVE_PPS_API */
+
+#if 1
+ /* If we are in SiteSurvey mode, then we are in 3D mode, and we fall thru.
+ * If we have Finished the SiteSurvey, then we fall thru for the 14/15
+ * times we get here in 0D mode (the 1/15 is in 3D for SHMEM).
+ * This gives good time, which gets better when the SS is done.
+ */
+
+ if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D))
+#else
+ /* old check, only fall thru for SS_DONE and 0D mode, 2h45m wait for ticks */
+
+ if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D))
+#endif
+ return;
+
+ /* Don't do anything without an almanac to define the GPS->UTC delta */
+
+ if (instance->rsm.bad_almanac)
+ return;
+
+#ifdef HAVE_PPSAPI
+ j = instance->ev_serial;
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i,
+ &timeout) < 0) {
+ printf("ONCORE: time_pps_fetch failed\n");
+ return;
+ }
+
+ if (instance->assert) {
+ tsp = &pps_i.assert_timestamp;
+
+ if (debug > 2) {
+ i = (u_long) pps_i.assert_sequence;
+#ifdef HAVE_STRUCT_TIMESPEC
+ printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n",
+ instance->unit, i, j,
+ (long)tsp->tv_sec, (long)tsp->tv_nsec);
+#else
+ printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n",
+ instance->unit, i, j,
+ (long)tsp->tv_sec, (long)tsp->tv_usec);
+#endif
+ }
+
+ if (pps_i.assert_sequence == j) {
+ printf("ONCORE: oncore_get_timestamp, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = pps_i.assert_sequence;
+ } else {
+ tsp = &pps_i.clear_timestamp;
+
+ if (debug > 2) {
+ i = (u_long) pps_i.clear_sequence;
+#ifdef HAVE_STRUCT_TIMESPEC
+ printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%09ld\n",
+ instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_nsec);
+#else
+ printf("ONCORE[%d]: serial/j (%lu, %lu) %ld.%06ld\n",
+ instance->unit, i, j, (long)tsp->tv_sec, (long)tsp->tv_usec);
+#endif
+ }
+
+ if (pps_i.clear_sequence == j) {
+ printf("ONCORE: oncore_get_timestamp, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = pps_i.clear_sequence;
+ }
+
+ /* convert timespec -> ntp l_fp */
+
+ dmy = tsp->tv_nsec;
+ dmy /= 1e9;
+ ts.l_uf = dmy * 4294967296.0;
+ ts.l_ui = tsp->tv_sec;
+#if 0
+ alternate code for previous 4 lines is
+ dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
+ DTOLFP(dmy, &ts);
+ dmy = tsp->tv_sec; /* integer part */
+ DTOLFP(dmy, &ts_tmp);
+ L_ADD(&ts, &ts_tmp);
+ or more simply
+ dmy = 1.0e-9*tsp->tv_nsec; /* fractional part */
+ DTOLFP(dmy, &ts);
+ ts.l_ui = tsp->tv_sec;
+#endif /* 0 */
+#else
+# if defined(HAVE_TIOCGPPSEV) || defined(HAVE_CIOGETEV)
+ j = instance->ev_serial;
+ if (ioctl(instance->ppsfd, r, (caddr_t) &ev) < 0) {
+ perror("ONCORE: IOCTL:");
+ return;
+ }
+
+ tsp = &ev.tv;
+
+ if (debug > 2)
+#ifdef HAVE_STRUCT_TIMESPEC
+ printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n",
+ ev.serial, j, tsp->tv_sec, tsp->tv_nsec);
+#else
+ printf("ONCORE: serial/j (%d, %d) %ld.%06ld\n",
+ ev.serial, j, tsp->tv_sec, tsp->tv_usec);
+#endif
+
+ if (ev.serial == j) {
+ printf("ONCORE: oncore_get_timestamp, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = ev.serial;
+
+ /* convert timeval -> ntp l_fp */
+
+ TVTOTS(tsp, &ts);
+# else
+# if defined(TIOCDCDTIMESTAMP)
+ if(ioctl(instance->ppsfd, TIOCDCDTIMESTAMP, &tv) < 0) {
+ perror("ONCORE: ioctl(TIOCDCDTIMESTAMP)");
+ return;
+ }
+ tsp = &tv;
+ TVTOTS(tsp, &ts);
+# else
+#error "Cannot compile -- no PPS mechanism configured!"
+# endif
+# endif
+#endif
+ /* now have timestamp in ts */
+ /* add in saw_tooth and offset, these will be ZERO if no TRAIM */
+
+ /* saw_tooth not really necessary if using TIMEVAL */
+ /* since its only precise to us, but do it anyway. */
+
+ /* offset in ns, and is positive (late), we subtract */
+ /* to put the PPS time transition back where it belongs */
+
+#ifdef HAVE_PPSAPI
+ /* must hand the offset for the NEXT sec off to the Kernel to do */
+ /* the addition, so that the Kernel PLL sees the offset too */
+
+ if (instance->assert)
+ instance->pps_p.assert_offset.tv_nsec = -dt2;
+ else
+ instance->pps_p.clear_offset.tv_nsec = -dt2;
+
+ /* The following code is necessary, and not just a time_pps_setparams,
+ * using the saved instance->pps_p, since some other process on the
+ * machine may have diddled with the mode bits (say adding something
+ * that it needs). We take what is there and ADD what we need.
+ * [[ The results from the time_pps_getcap is unlikely to change so
+ * we could probably just save it, but I choose to do the call ]]
+ * Unfortunately, there is only ONE set of mode bits in the kernel per
+ * interface, and not one set for each open handle.
+ *
+ * There is still a race condition here where we might mess up someone
+ * elses mode, but if he is being careful too, he should survive.
+ */
+
+ if (time_pps_getcap(instance->pps_h, &current_mode) < 0) {
+ msyslog(LOG_ERR, "refclock_ioctl: time_pps_getcap failed: %m");
+ return;
+ }
+
+ if (time_pps_getparams(instance->pps_h, &current_params) < 0) {
+ msyslog(LOG_ERR, "refclock_ioctl: time_pps_getparams failed: %m");
+ return;
+ }
+
+ /* or current and mine */
+ current_params.mode |= instance->pps_p.mode;
+ /* but only set whats legal */
+ current_params.mode &= current_mode;
+
+ current_params.assert_offset.tv_sec = 0;
+ current_params.assert_offset.tv_nsec = -dt2;
+ current_params.clear_offset.tv_sec = 0;
+ current_params.clear_offset.tv_nsec = -dt2;
+
+ if (time_pps_setparams(instance->pps_h, &current_params))
+ perror("time_pps_setparams");
+#else
+ /* if not PPSAPI, no way to inform kernel of OFFSET, just add the */
+ /* offset for THIS second */
+
+ dmy = -1.0e-9*dt1;
+ DTOLFP(dmy, &ts_tmp);
+ L_ADD(&ts, &ts_tmp);
+#endif
+ /* have time from UNIX origin, convert to NTP origin. */
+
+ ts.l_ui += JAN_1970;
+ instance->pp->lastrec = ts;
+
+ /* print out information about this timestamp (long line) */
+
+ ts_tmp = ts;
+ ts_tmp.l_ui = 0; /* zero integer part */
+ LFPTOD(&ts_tmp, dmy); /* convert fractional part to a double */
+ j = 1.0e9*dmy; /* then to integer ns */
+
+ Rsm = 0;
+ if (instance->chan == 6)
+ Rsm = instance->BEHa[64];
+ else if (instance->chan == 8)
+ Rsm = instance->BEHa[72];
+ else if (instance->chan == 12)
+ Rsm = ((instance->BEHa[129]<<8) | instance->BEHa[130]);
+
+ if (instance->chan == 6 || instance->chan == 8) {
+ sprintf(instance->pp->a_lastcode, /* MAX length 128, currently at 117 */
+ "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d sigma %2d neg-sawtooth %3d sat %d%d%d%d%d%d%d%d",
+ ts.l_ui, j,
+ instance->pp->year, instance->pp->day,
+ instance->pp->hour, instance->pp->minute, instance->pp->second,
+ (long) tsp->tv_sec % 60,
+ Rsm, 0.1*(256*instance->BEHa[35]+instance->BEHa[36]),
+ /*rsat dop */
+ instance->BEHa[38], instance->BEHa[39], instance->BEHn[21],
+ /* nsat visible, nsat tracked, traim */
+ instance->BEHn[23]*256+instance->BEHn[24], (s_char) instance->BEHn[25],
+ /* sigma neg-sawtooth */
+ /*sat*/ instance->BEHa[41], instance->BEHa[45], instance->BEHa[49], instance->BEHa[53],
+ instance->BEHa[57], instance->BEHa[61], instance->BEHa[65], instance->BEHa[69]
+ ); /* will be 0 for 6 chan */
+ } else if (instance->chan == 12) {
+ sprintf(instance->pp->a_lastcode,
+ "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d sigma %d neg-sawtooth %3d sat %d%d%d%d%d%d%d%d%d%d%d%d",
+ ts.l_ui, j,
+ instance->pp->year, instance->pp->day,
+ instance->pp->hour, instance->pp->minute, instance->pp->second,
+ (long) tsp->tv_sec % 60,
+ Rsm, 0.1*(256*instance->BEHa[53]+instance->BEHa[54]),
+ /*rsat dop */
+ instance->BEHa[55], instance->BEHa[56], instance->BEHn[6],
+ /* nsat visible, nsat tracked traim */
+ instance->BEHn[12]*256+instance->BEHn[13], (s_char) instance->BEHn[14],
+ /* sigma neg-sawtooth */
+ /*sat*/ instance->BEHa[58], instance->BEHa[64], instance->BEHa[70], instance->BEHa[76],
+ instance->BEHa[82], instance->BEHa[88], instance->BEHa[94], instance->BEHa[100],
+ instance->BEHa[106], instance->BEHa[112], instance->BEHa[118], instance->BEHa[124]
+ );
+ }
+
+ if (debug > 2) {
+ int n;
+ n = strlen(instance->pp->a_lastcode);
+ printf("ONCORE[%d]: len = %d %s\n", instance->unit, n, instance->pp->a_lastcode);
+ }
+
+ /* and some things I dont understnd (magic ntp things) */
+
+ if (!refclock_process(instance->pp)) {
+ refclock_report(instance->peer, CEVNT_BADTIME);
+ return;
+ }
+
+ record_clock_stats(&(instance->peer->srcadr), instance->pp->a_lastcode);
+ instance->pollcnt = 2;
+
+ if (instance->polled) {
+ instance->polled = 0;
+/*
+ instance->pp->dispersion = instance->pp->skew = 0;
+*/
+ instance->pp->lastref = instance->pp->lastrec;
+ refclock_receive(instance->peer);
+ }
+}
+
+
+/*************** oncore_msg_XX routines start here *******************/
+
+
+/*
+ * print Oncore response message.
+ */
+
+static void
+oncore_msg_any(
+ struct instance *instance,
+ u_char *buf,
+ size_t len,
+ int idx
+ )
+{
+ int i;
+ const char *fmt = oncore_messages[idx].fmt;
+ const char *p;
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ struct timeval tv;
+
+ if (debug > 3) {
+#ifdef HAVE_GETCLOCK
+ (void) getclock(TIMEOFDAY, &ts);
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = ts.tv_nsec / 1000;
+#else
+ GETTIMEOFDAY(&tv, 0);
+#endif
+ printf("ONCORE[%d]: %ld.%06ld\n", instance->unit, (long) tv.tv_sec, (long) tv.tv_usec);
+
+ if (!*fmt) {
+ printf(">>@@%c%c ", buf[2], buf[3]);
+ for(i=2; i < len && i < 2400 ; i++)
+ printf("%02x", buf[i]);
+ printf("\n");
+ return;
+ } else {
+ printf("##");
+ for (p = fmt; *p; p++) {
+ putchar(*p);
+ putchar('_');
+ }
+ printf("\n%c%c", buf[2], buf[3]);
+ i = 4;
+ for (p = fmt; *p; p++) {
+ printf("%02x", buf[i++]);
+ }
+ printf("\n");
+ }
+ }
+}
+
+
+
+/* Latitude, Longitude, Height */
+
+static void
+oncore_msg_Adef(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+}
+
+
+
+/* Mask Angle */
+
+static void
+oncore_msg_Ag(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{ char Msg[160], *cp;
+
+ cp = "set to";
+ if (instance->o_state == ONCORE_RUN)
+ cp = "is";
+
+ instance->Ag = buf[4];
+ sprintf(Msg, "Satellite mask angle %s %d degrees", cp, (int) instance->Ag);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/*
+ * get Position hold position
+ */
+
+static void
+oncore_msg_As(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ instance->ss_lat = buf_w32(&buf[4]);
+ instance->ss_long = buf_w32(&buf[8]);
+ instance->ss_ht = buf_w32(&buf[12]);
+
+ /* Print out Position */
+ oncore_print_posn(instance);
+}
+
+
+
+/*
+ * Try to use Oncore UT+ Auto Survey Feature
+ * If its not there (VP), set flag to do it ourselves.
+ */
+
+static void
+oncore_msg_At(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp;
+
+ instance->saw_At = 1;
+ if (instance->site_survey == ONCORE_SS_TESTING) {
+ if (buf[4] == 2) {
+ record_clock_stats(&(instance->peer->srcadr),
+ "Initiating hardware 3D site survey");
+
+ cp = "SSstate = ONCORE_SS_HW";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->site_survey = ONCORE_SS_HW;
+ }
+ }
+}
+
+
+
+/*
+ * get PPS Offset
+ * Nb. @@Ay is not supported for early UT (no plus) model
+ */
+
+static void
+oncore_msg_Ay(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[120];
+
+ if (instance->saw_Ay)
+ return;
+
+ instance->saw_Ay = 1;
+
+ instance->offset = buf_w32(&buf[4]);
+
+ sprintf(Msg, "PPS Offset is set to %ld ns", instance->offset);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/*
+ * get Cable Delay
+ */
+
+static void
+oncore_msg_Az(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[120];
+
+ if (instance->saw_Az)
+ return;
+
+ instance->saw_Az = 1;
+
+ instance->delay = buf_w32(&buf[4]);
+
+ sprintf(Msg, "Cable delay is set to %ld ns", instance->delay);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/* Ba, Ea and Ha come here, these contain Position */
+
+static void
+oncore_msg_BaEaHa(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+ char Msg[160];
+ int mode;
+
+ /* OK, we are close to the RUN state now.
+ * But we have a few more items to initialize first.
+ *
+ * At the beginning of this routine there are several 'timers'.
+ * We enter this routine 1/sec, and since the upper levels of NTP have usurped
+ * the use of timers, we use the 1/sec entry to do things that
+ * we would normally do with timers...
+ */
+
+ if (instance->o_state == ONCORE_CHECK_CHAN) { /* here while checking for the # chan */
+ if (buf[2] == 'B') { /* 6chan */
+ if (instance->chan_ck < 6) instance->chan_ck = 6;
+ } else if (buf[2] == 'E') { /* 8chan */
+ if (instance->chan_ck < 8) instance->chan_ck = 8;
+ } else if (buf[2] == 'H') { /* 12chan */
+ if (instance->chan_ck < 12) instance->chan_ck = 12;
+ }
+
+ if (instance->count3++ < 5)
+ return;
+
+ instance->count3 = 0;
+
+ if (instance->chan_in != -1) /* set in Input */
+ instance->chan = instance->chan_in;
+ else /* set from test */
+ instance->chan = instance->chan_ck;
+
+ sprintf(Msg, "Input says chan = %d", instance->chan_in);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Model # says chan = %d", instance->chan_id);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Testing says chan = %d", instance->chan_ck);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Using chan = %d", instance->chan);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ instance->o_state = ONCORE_HAVE_CHAN;
+ cp = "state = ONCORE_HAVE_CHAN";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->timeout = 4;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ return;
+ }
+
+ if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN)
+ return;
+
+ /* PAUSE 5sec */
+
+ if (instance->count) {
+ if (instance->count++ < 5) /* make sure results are stable, using position */
+ return;
+ instance->count = 0;
+ }
+
+ memcpy(instance->BEHa, buf, (size_t) (len+3)); /* Ba, Ea or Ha */
+
+ /* check the antenna and almanac for changes (did it get unplugged, is it ready?) */
+
+ oncore_check_almanac(instance);
+ oncore_check_antenna(instance);
+
+ /* Almanac mode, waiting for Almanac, we can't do anything till we have it */
+ /* When we have an almanac, we will start the Bn/En/@@Hn messages */
+
+ if (instance->o_state == ONCORE_ALMANAC)
+ if (oncore_wait_almanac(instance))
+ return;
+
+ /* do some things once when we get this far in BaEaHa */
+
+ if (instance->once) {
+ instance->once = 0;
+ instance->count2 = 1;
+
+ /* Have we seen an @@At (position hold) command response */
+ /* if not, message out */
+
+ if (instance->chan != 12 && !instance->saw_At) {
+ cp = "Not Good, no @@At command (no Position Hold), must be a GT/GT+";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
+ }
+
+ /* have an Almanac, can start the SiteSurvey
+ * (actually only need to get past the almanac_load where we diddle with At
+ * command,- we can't change it after we start the HW_SS below
+ */
+
+ mode = instance->init_type;
+ switch (mode) {
+ case 0: /* NO initialization, don't change anything */
+ case 1: /* Use given Position */
+ case 3:
+ instance->site_survey = ONCORE_SS_DONE;
+ cp = "SSstate = ONCORE_SS_DONE";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ break;
+
+ case 2:
+ case 4: /* Site Survey */
+ cp = "SSstate = ONCORE_SS_TESTING";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->site_survey = ONCORE_SS_TESTING;
+ instance->count1 = 1;
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd3, sizeof(oncore_cmd_Gd3)); /* M12+T */
+ else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At2, sizeof(oncore_cmd_At2)); /* not GT, arg not VP */
+ break;
+ }
+
+ /* Read back PPS Offset for Output */
+ /* Nb. This will fail silently for early UT (no plus) and M12 models */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ayx, sizeof(oncore_cmd_Ayx));
+
+ /* Read back Cable Delay for Output */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Azx, sizeof(oncore_cmd_Azx));
+
+ /* Read back Satellite Mask Angle for Output */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Agx, sizeof(oncore_cmd_Agx));
+ }
+
+ if (instance->count1) {
+ if (instance->count1++ > 5 || instance->site_survey == ONCORE_SS_HW) {
+ instance->count1 = 0;
+ if (instance->site_survey == ONCORE_SS_TESTING) {
+ /*
+ * For instance->site_survey to still be ONCORE_SS_TESTING, then after a 5sec
+ * wait after the @@At2/@@Gd3 command we have not changed the state to
+ * ONCORE_SS_HW. If the Hardware is capable of doing a Site Survey, then
+ * the variable would have been changed by now.
+ * There are three possibilities:
+ * 6/8chan
+ * (a) We did not get a response to the @@At0 or @@At2 commands,
+ * and it must be a GT/GT+/SL with no position hold mode.
+ * We will have to do it ourselves.
+ * (b) We saw the @@At0, @@At2 commands, but @@At2 failed,
+ * must be a VP or older UT which doesn't have Site Survey mode.
+ * We will have to do it ourselves.
+ * 12chan
+ * (c) We saw the @@Gd command, but @@Gd3 failed,
+ * We will have to do it ourselves.
+ */
+
+ sprintf(Msg, "Initiating software 3D site survey (%d samples)",
+ POS_HOLD_AVERAGE);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ record_clock_stats(&(instance->peer->srcadr), "SSstate = ONCORE_SS_SW");
+ instance->site_survey = ONCORE_SS_SW;
+
+ instance->ss_lat = instance->ss_long = instance->ss_ht = 0;
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* disable */
+ else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* disable */
+ }
+ }
+ }
+ }
+
+ /* check the mode we are in 0/2/3D */
+
+ if (instance->chan == 6) {
+ if (instance->BEHa[64]&0x8)
+ instance->mode = MODE_0D;
+ else if (instance->BEHa[64]&0x10)
+ instance->mode = MODE_2D;
+ else if (instance->BEHa[64]&0x20)
+ instance->mode = MODE_3D;
+ } else if (instance->chan == 8) {
+ if (instance->BEHa[72]&0x8)
+ instance->mode = MODE_0D;
+ else if (instance->BEHa[72]&0x10)
+ instance->mode = MODE_2D;
+ else if (instance->BEHa[72]&0x20)
+ instance->mode = MODE_3D;
+ } else if (instance->chan == 12) {
+ int bits;
+
+ bits = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */
+ if (bits == 0x4)
+ instance->mode = MODE_0D;
+ else if (bits == 0x6)
+ instance->mode = MODE_2D;
+ else if (bits == 0x7)
+ instance->mode = MODE_3D;
+ }
+
+ /* copy the record to the (extra) location in SHMEM */
+
+ if (instance->shmem) {
+ int i;
+ u_char *smp; /* pointer to start of shared mem for Ba/Ea/Ha */
+
+ switch(instance->chan) {
+ case 6: smp = &instance->shmem[instance->shmem_Ba]; break;
+ case 8: smp = &instance->shmem[instance->shmem_Ea]; break;
+ case 12: smp = &instance->shmem[instance->shmem_Ha]; break;
+ default: smp = (u_char) 0; break;
+ }
+
+ switch (instance->mode) {
+ case MODE_0D: i = 1; break; /* 0D, Position Hold */
+ case MODE_2D: i = 2; break; /* 2D, Altitude Hold */
+ case MODE_3D: i = 3; break; /* 3D fix */
+ default: i = 0; break;
+ }
+
+ if (i) {
+ i *= (len+6);
+ smp[i + 2]++;
+ memcpy(&smp[i+3], buf, (size_t) (len+3));
+ }
+ }
+
+ /*
+ * check if timer active
+ * if it hasn't been cleared, then @@Bn/@@En/@@Hn did not respond
+ */
+
+ if (instance->traim_delay) {
+ if (instance->traim_delay++ > 5) {
+ instance->traim = 0;
+ instance->traim_delay = 0;
+ cp = "ONCORE: Did not detect TRAIM response, TRAIM = OFF";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ oncore_set_traim(instance);
+ } else
+ return;
+
+ }
+
+ /* by now should have a @@Ba/@@Ea/@@Ha with good data in it */
+
+ if (!instance->have_dH && !instance->traim_delay)
+ oncore_compute_dH(instance);
+
+ /*
+ * must be ONCORE_RUN if we are here.
+ * Have # chan and TRAIM by now.
+ */
+
+ instance->pp->year = buf[6]*256+buf[7];
+ instance->pp->day = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]);
+ instance->pp->hour = buf[8];
+ instance->pp->minute = buf[9];
+ instance->pp->second = buf[10];
+
+ /*
+ * Are we doing a Hardware or Software Site Survey?
+ */
+
+ if (instance->site_survey == ONCORE_SS_HW || instance->site_survey == ONCORE_SS_SW)
+ oncore_ss(instance);
+
+ /* see if we ever saw a response from the @@Ayx above */
+
+ if (instance->count2) {
+ if (instance->count2++ > 5) { /* this delay to check on @@Ay command */
+ instance->count2 = 0;
+
+ /* Have we seen an Ay (1PPS time offset) command response */
+ /* if not, and non-zero offset, zero the offset, and send message */
+
+ if (!instance->saw_Ay && instance->offset) {
+ cp = "No @@Ay command, PPS OFFSET ignored";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->offset = 0;
+ }
+ }
+ }
+
+ /*
+ * Check the leap second status once per day.
+ */
+
+ oncore_check_leap_sec(instance);
+
+ /*
+ * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
+ */
+
+ if (instance->shmem && !instance->shmem_bad_Ea && instance->shmem_Posn && (instance->site_survey == ONCORE_SS_DONE))
+ oncore_shmem_get_3D(instance);
+
+ if (!instance->traim) /* NO traim, no BnEnHn, go get tick */
+ oncore_get_timestamp(instance, instance->offset, instance->offset);
+}
+
+
+
+/* Almanac Status */
+
+static void
+oncore_msg_Bd(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[160];
+
+ sprintf(Msg, "Bd: Almanac %s, week = %d, t = %d, %d SVs: %x",
+ ((buf[4]) ? "LOADED" : "(NONE)"), buf[5], buf[6], buf[7], w32(&buf[8]) );
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/* get leap-second warning message */
+
+/*
+ * @@Bj does NOT behave as documented in current Oncore firmware.
+ * It turns on the LEAP indicator when the data is set, and does not,
+ * as documented, wait until the beginning of the month when the
+ * leap second will occur.
+ * Since this firmware bug will never be fixed in all the outstanding Oncore receivers
+ * @@Bj is only called in June/December.
+ */
+
+static void
+oncore_msg_Bj(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+
+ switch(buf[4]) {
+ case 1:
+ instance->peer->leap = LEAP_ADDSECOND;
+ cp = "Set peer.leap to LEAP_ADDSECOND";
+ break;
+ case 2:
+ instance->peer->leap = LEAP_DELSECOND;
+ cp = "Set peer.leap to LEAP_DELSECOND";
+ break;
+ case 0:
+ default:
+ instance->peer->leap = LEAP_NOWARNING;
+ cp = "Set peer.leap to LEAP_NOWARNING";
+ break;
+ }
+ record_clock_stats(&(instance->peer->srcadr), cp);
+}
+
+
+
+static void
+oncore_msg_BnEnHn(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ long dt1, dt2;
+ char *cp;
+
+ if (instance->o_state != ONCORE_RUN)
+ return;
+
+ if (instance->traim_delay) { /* flag that @@Bn/@@En/Hn returned */
+ instance->traim_ck = 1;
+ instance->traim_delay = 0;
+ cp = "ONCORE: Detected TRAIM, TRAIM = ON";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ oncore_set_traim(instance);
+ }
+
+ memcpy(instance->BEHn, buf, (size_t) len); /* Bn or En or Hn */
+
+ /* If Time RAIM doesn't like it, don't trust it */
+
+ if (buf[2] == 'H') {
+ if (instance->BEHn[6]) /* bad TRAIM */
+ return;
+
+ dt1 = instance->saw_tooth + instance->offset; /* dt this time step */
+ instance->saw_tooth = (s_char) instance->BEHn[10]; /* update for next time Hn[10] */
+ dt2 = instance->saw_tooth + instance->offset; /* dt next time step */
+ } else {
+ if (instance->BEHn[21]) /* bad TRAIM */
+ return;
+
+ dt1 = instance->saw_tooth + instance->offset; /* dt this time step */
+ instance->saw_tooth = (s_char) instance->BEHn[25]; /* update for next time */
+ dt2 = instance->saw_tooth + instance->offset; /* dt next time step */
+ }
+
+ oncore_get_timestamp(instance, dt1, dt2);
+}
+
+
+
+/* Here for @@Ca, @@Fa and @@Ia messages */
+
+/* These are Self test Commands for 6, 8, and 12 chan receivers.
+ * There are good reasons NOT to do a @@Ca, @@Fa or @@Ia command with the ONCORE.
+ * It was found that under some circumstances the following
+ * command would fail if issued immediately after the return from the
+ * @@Fa, but a 2sec delay seemed to fix things. Since simply calling
+ * sleep(2) is wasteful, and may cause trouble for some OS's, repeating
+ * itimer, we set a flag, and test it at the next POLL. If it hasn't
+ * been cleared, we reissue the @@Cj that is issued below.
+ * Note that we do a @@Cj at the beginning, and again here.
+ * The first is to get the info, the 2nd is just used as a safe command
+ * after the @@Fa for all Oncores (and it was in this posn in the
+ * original code).
+ */
+
+static void
+oncore_msg_CaFaIa(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp;
+ int i;
+
+ if (instance->o_state == ONCORE_TEST_SENT) {
+ enum antenna_state antenna;
+
+ instance->timeout = 0;
+
+ if (debug > 2) {
+ if (buf[2] == 'I')
+ printf("ONCORE[%d]: >>@@%ca %x %x %x\n", instance->unit, buf[2], buf[4], buf[5], buf[6]);
+ else
+ printf("ONCORE[%d]: >>@@%ca %x %x\n", instance->unit, buf[2], buf[4], buf[5]);
+ }
+
+ antenna = (buf[4] & 0xc0) >> 6;
+ buf[4] &= ~0xc0;
+
+ i = buf[4] || buf[5];
+ if (buf[2] == 'I') i = i || buf[6];
+ if (i) {
+ if (buf[2] == 'I') {
+ msyslog(LOG_ERR, "ONCORE[%d]: self test failed: result %02x %02x %02x",
+ instance->unit, buf[4], buf[5], buf[6]);
+ } else {
+ msyslog(LOG_ERR, "ONCORE[%d]: self test failed: result %02x %02x",
+ instance->unit, buf[4], buf[5]);
+ }
+ cp = "ONCORE: self test failed, shutting down driver";
+ record_clock_stats(&instance->peer->srcadr, cp);
+
+ refclock_report(instance->peer, CEVNT_FAULT);
+ oncore_shutdown(instance->unit, instance->peer);
+ return;
+ }
+
+ /* report the current antenna state */
+
+ oncore_antenna_report(instance, antenna);
+
+ instance->o_state = ONCORE_INIT;
+ cp = "state = ONCORE_INIT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->timeout = 4;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ }
+}
+
+
+
+/*
+ * Demultiplex the almanac into shmem
+ */
+
+static void
+oncore_msg_Cb(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int i;
+
+ if (instance->shmem == NULL)
+ return;
+
+ if (buf[4] == 5 && buf[5] > 0 && buf[5] < 26)
+ i = buf[5];
+ else if (buf[4] == 4 && buf[5] <= 5)
+ i = buf[5] + 24;
+ else if (buf[4] == 4 && buf[5] <= 10)
+ i = buf[5] + 23;
+ else if (buf[4] == 4 && buf[5] == 25)
+ i = 34;
+ else {
+ char *cp;
+
+ cp = "Cb: Response is NO ALMANAC";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ return;
+ }
+
+ i *= 36;
+ instance->shmem[instance->shmem_Cb + i + 2]++;
+ memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t) (len + 3));
+
+#if 1
+ {
+ char Msg[160];
+ sprintf(Msg, "See Cb [%d,%d]", buf[4], buf[5]);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+#endif
+}
+
+
+
+/*
+ * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
+ * not so for VP (eeprom) or any unit with a battery
+ */
+
+static void
+oncore_msg_Cf(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+
+ if (instance->o_state == ONCORE_RESET_SENT) {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */
+ /* Reset set VP to IDLE */
+ instance->o_state = ONCORE_TEST_SENT;
+ cp = "state = ONCORE_TEST_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
+ }
+}
+
+
+
+/*
+ * This is the Grand Central Station for the Preliminary Initialization.
+ * Once done here we move on to oncore_msg_BaEaHa for final Initialization and Running.
+ *
+ * We do an @@Cj whenever we need a safe command for all Oncores.
+ * The @@Cj gets us back here where we can switch to the next phase of setup.
+ *
+ * o Once at the very beginning (in start) to get the Model number.
+ * This info is printed, but no longer used.
+ * o Again after we have determined the number of Channels in the receiver.
+ * o And once later after we have done a reset and test, (which may hang),
+ * as we are about to initialize the Oncore and start it running.
+ * o We have one routine below for each case.
+ */
+
+static void
+oncore_msg_Cj(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int mode;
+ char *cp;
+
+ memcpy(instance->Cj, buf, len);
+
+ instance->timeout = 0;
+ if (instance->o_state == ONCORE_CHECK_ID) {
+ oncore_msg_Cj_id(instance, buf, len);
+ oncore_chan_test(instance);
+ } else if (instance->o_state == ONCORE_HAVE_CHAN) {
+ mode = instance->init_type;
+ if (mode == 3 || mode == 4) { /* Cf will return here to check for TEST */
+ instance->o_state = ONCORE_RESET_SENT;
+ cp = "state = ONCORE_RESET_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cf, sizeof(oncore_cmd_Cf));
+ } else {
+ instance->o_state = ONCORE_TEST_SENT;
+ cp = "state = ONCORE_TEST_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+ }
+
+ if (instance->o_state == ONCORE_TEST_SENT) {
+ if (instance->chan == 6)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ca, sizeof(oncore_cmd_Ca));
+ else if (instance->chan == 8)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof(oncore_cmd_Fa));
+ else if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ia, sizeof(oncore_cmd_Ia));
+ } else if (instance->o_state == ONCORE_INIT)
+ oncore_msg_Cj_init(instance, buf, len);
+}
+
+
+
+/* The information on determining a Oncore 'Model', viz VP, UT, etc, from
+ * the Model Number comes from "Richard M. Hambly" <rick@cnssys.com>
+ * and from Motorola. Until recently Rick was the only source of
+ * this information as Motorola didn't give the information out.
+ *
+ * Determine the Type from the Model #, this determines #chan and if TRAIM is
+ * available.
+ *
+ * The Information from this routine is NO LONGER USED.
+ * The RESULTS are PRINTED, BUT NOT USED, and the routine COULD BE DELETED
+ */
+
+static void
+oncore_msg_Cj_id(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp, *cp1, *cp2, Model[21], Msg[160];
+
+ /* Write Receiver ID message to clockstats file */
+
+ instance->Cj[294] = '\0';
+ for (cp=(char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) {
+ cp1 = strchr(cp, '\r');
+ if (!cp1)
+ cp1 = (char *)&instance->Cj[294];
+ *cp1 = '\0';
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ *cp1 = '\r';
+ cp = cp1+2;
+ }
+
+ /* next, the Firmware Version and Revision numbers */
+
+ instance->version = atoi(&instance->Cj[83]);
+ instance->revision = atoi(&instance->Cj[111]);
+
+ /* from model number decide which Oncore this is,
+ and then the number of channels */
+
+ for (cp=&instance->Cj[160]; *cp == ' '; cp++) /* start right after 'Model #' */
+ ;
+ cp1 = cp;
+ cp2 = Model;
+ for (; !isspace((int)*cp) && cp-cp1 < 20; cp++, cp2++)
+ *cp2 = *cp;
+ *cp2 = '\0';
+
+ cp = 0;
+ if (!strncmp(Model, "PVT6", (size_t) 4)) {
+ cp = "PVT6";
+ instance->model = ONCORE_PVT6;
+ } else if (Model[0] == 'A') {
+ cp = "Basic";
+ instance->model = ONCORE_BASIC;
+ } else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) {
+ cp = "VP";
+ instance->model = ONCORE_VP;
+ } else if (Model[0] == 'P') {
+ cp = "M12";
+ instance->model = ONCORE_M12;
+ } else if (Model[0] == 'R' || Model[0] == 'D' || Model[0] == 'S') {
+ if (Model[5] == 'N') {
+ cp = "GT";
+ instance->model = ONCORE_GT;
+ } else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') {
+ cp = "GT+";
+ instance->model = ONCORE_GTPLUS;
+ } else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) {
+ cp = "UT";
+ instance->model = ONCORE_UT;
+ } else if (Model[1] == '5' && Model[5] == 'G') {
+ cp = "UT+";
+ instance->model = ONCORE_UTPLUS;
+ } else if (Model[1] == '6' && Model[5] == 'G') {
+ cp = "SL";
+ instance->model = ONCORE_SL;
+ } else {
+ cp = "Unknown";
+ instance->model = ONCORE_UNKNOWN;
+ }
+ } else {
+ cp = "Unknown";
+ instance->model = ONCORE_UNKNOWN;
+ }
+
+ /* use MODEL to set CHAN and TRAIM and possibly zero SHMEM */
+
+ sprintf(Msg, "This looks like an Oncore %s with version %d.%d firmware.", cp, instance->version, instance->revision);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ instance->chan_id = 8; /* default */
+ if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
+ instance->chan_id = 6;
+ else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
+ instance->chan_id = 8;
+ else if (instance->model == ONCORE_M12)
+ instance->chan_id = 12;
+
+ instance->traim_id = 0; /* default */
+ if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6)
+ instance->traim_id = 0;
+ else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS)
+ instance->traim_id = 1;
+ else if (instance->model == ONCORE_M12)
+ instance->traim_id = -1;
+
+ sprintf(Msg, "Channels = %d, TRAIM = %s", instance->chan_id,
+ ((instance->traim_id < 0) ? "UNKNOWN" : ((instance->traim_id > 0) ? "ON" : "OFF")));
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/* OK, know type of Oncore, have possibly reset it, and have tested it.
+ * We know the number of channels.
+ * We will determine whether we have TRAIM before we actually start.
+ * Now initialize.
+ */
+
+static void
+oncore_msg_Cj_init(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp, Cmd[20], Msg[160];
+ int mode;
+
+
+ /* The M12 with 1.3 or 2.0 Firmware, loses track of all Satellites and has to
+ * start again if we go from 0D -> 3D, then loses them again when we
+ * go from 3D -> 0D. We do this to get a @@Ea message for SHMEM.
+ * For NOW we will turn this aspect of filling SHMEM off for the M12
+ */
+
+ if (instance->chan == 12) {
+ instance->shmem_bad_Ea = 1;
+ sprintf(Msg, "*** SHMEM partially enabled for ONCORE M12 s/w v%d.%d ***", instance->version, instance->revision);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem (6/8/12) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off (VP) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time (6/8/12) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static (VP) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem (6/8/12) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bd, sizeof(oncore_cmd_Bd)); /* Tell us when Almanac changes */
+
+ mode = instance->init_type;
+
+ /* If there is Position input in the Config file
+ * and mode = (1,3) set it as posn hold posn, goto 0D mode.
+ * or mode = (2,4) set it as INITIAL position, and do Site Survey.
+ */
+
+ if (instance->posn_set) {
+ record_clock_stats(&(instance->peer->srcadr), "Setting Posn from input data");
+ oncore_set_posn(instance); /* this should print posn indirectly thru the As cmd */
+ } else /* must issue an @@At here to check on 6/8 Position Hold, set_posn would have */
+ if (instance->chan != 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Atx, sizeof(oncore_cmd_Atx));
+
+ if (mode != 0) {
+ /* cable delay in ns */
+ memcpy(Cmd, oncore_cmd_Az, (size_t) sizeof(oncore_cmd_Az));
+ w32_buf(&Cmd[-2+4], instance->delay);
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Az)); /* 6,8,12 */
+
+ /* PPS offset in ns */
+ if (instance->offset) {
+ memcpy(Cmd, oncore_cmd_Ay, (size_t) sizeof(oncore_cmd_Ay)); /* some have it, some don't */
+ w32_buf(&Cmd[-2+4], instance->offset); /* will check for hw response */
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ay));
+ }
+
+ /* Satellite mask angle */
+
+ if (instance->Ag != 0xff) { /* will have 0xff in it if not set by user */
+ memcpy(Cmd, oncore_cmd_Ag, (size_t) sizeof(oncore_cmd_Ag));
+ Cmd[-2+4] = instance->Ag;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ag));
+ }
+ }
+
+ /* 6, 8 12 chan - Position/Status/Data Output Message, 1/s
+ * now we're really running
+ * these were ALL started in the chan test,
+ * However, if we had mode=3,4 then commands got turned off, so we turn
+ * them on again here just in case
+ */
+
+ if (instance->chan == 6) { /* start 6chan, kill 8,12chan commands, possibly testing VP in 6chan mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_En0, sizeof(oncore_cmd_En0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba, sizeof(oncore_cmd_Ba ));
+ } else if (instance->chan == 8) { /* start 8chan, kill 6,12chan commands */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof(oncore_cmd_Ea ));
+ } else if (instance->chan == 12){ /* start 12chan, kill 6,12chan commands */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_En0, sizeof(oncore_cmd_En0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha, sizeof(oncore_cmd_Ha ));
+ }
+
+ instance->count = 1;
+ instance->o_state = ONCORE_ALMANAC;
+ cp = "state = ONCORE_ALMANAC";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+}
+
+
+
+/* 12chan position */
+
+static void
+oncore_msg_Ga(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[160];
+ long lat, lon, ht;
+ double Lat, Lon, Ht;
+
+
+ lat = buf_w32(&buf[4]);
+ lon = buf_w32(&buf[8]);
+ ht = buf_w32(&buf[12]); /* GPS ellipsoid */
+
+ Lat = lat;
+ Lon = lon;
+ Ht = ht;
+
+ Lat /= 3600000;
+ Lon /= 3600000;
+ Ht /= 100;
+
+
+ sprintf(Msg, "Ga Posn Lat = %.7f, Lon = %.7f, Ht = %.2f", Lat, Lon, Ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ instance->ss_lat = lat;
+ instance->ss_long = lon;
+ instance->ss_ht = ht;
+
+ oncore_print_posn(instance);
+}
+
+
+
+/* 12 chan time/date */
+
+static void
+oncore_msg_Gb(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char Msg[160], *gmts;
+ int mo, d, y, h, m, s, gmth, gmtm;
+
+ mo = buf[4];
+ d = buf[5];
+ y = 256*buf[6]+buf[7];
+
+ h = buf[8];
+ m = buf[9];
+ s = buf[10];
+
+ gmts = ((buf[11] == 0) ? "+" : "-");
+ gmth = buf[12];
+ gmtm = buf[13];
+
+ sprintf(Msg, "Date/Time set to: %d%s%d %2d:%02d:%02d GMT (GMT offset is %s%02d:%02d)",
+ d, Month[mo+1], y, h, m, s, gmts, gmth, gmtm);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/*
+ * Try to use Oncore M12+Timing Auto Survey Feature
+ * If its not there (M12), set flag to do it ourselves.
+ */
+
+static void
+oncore_msg_Gd(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ char *cp;
+
+ if (instance->site_survey == ONCORE_SS_TESTING) {
+ if (buf[4] == 3) {
+ record_clock_stats(&(instance->peer->srcadr),
+ "Initiating hardware 3D site survey");
+
+ cp = "SSstate = ONCORE_SS_HW";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->site_survey = ONCORE_SS_HW;
+ }
+ }
+}
+
+
+
+/* Leap Second for M12, gives all info from satellite message */
+/* also in UT v3.0 */
+
+static void
+oncore_msg_Gj(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ int dt;
+ char Msg[160], *cp;
+
+ instance->saw_Gj = 1; /* flag, saw_Gj, dont need to try Bj in check_leap */
+
+ /* print the message to verify whats there */
+
+ dt = buf[5] - buf[4];
+
+#if 1
+ sprintf(Msg, "ONCORE[%d]: Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d",
+ instance->unit,
+ buf[4], buf[5], 256*buf[6]+buf[7], buf[8], buf[9], buf[10],
+ (buf[14]+256*(buf[13]+256*(buf[12]+256*buf[11]))),
+ buf[15], buf[16], buf[17]);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+#endif
+ if (dt) {
+ sprintf(Msg, "ONCORE[%d]: Leap second (%d) scheduled for %d%s%d at %d:%d:%d",
+ instance->unit,
+ dt, buf[9], Month[buf[8]], 256*buf[6]+buf[7],
+ buf[15], buf[16], buf[17]);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+
+ /* Only raise warning within a month of the leap second */
+
+ instance->peer->leap = LEAP_NOWARNING;
+ cp = "Set peer.leap to LEAP_NOWARNING";
+
+ if (buf[6] == instance->BEHa[6] && buf[7] == instance->BEHa[7] && /* year */
+ buf[8] == instance->BEHa[4]) { /* month */
+ if (dt) {
+ if (dt < 0) {
+ instance->peer->leap = LEAP_DELSECOND;
+ cp = "Set peer.leap to LEAP_DELSECOND";
+ } else {
+ instance->peer->leap = LEAP_ADDSECOND;
+ cp = "Set peer.leap to LEAP_ADDSECOND";
+ }
+ }
+ }
+ record_clock_stats(&(instance->peer->srcadr), cp);
+}
+
+
+
+/* Power on failure */
+
+static void
+oncore_msg_Sz(
+ struct instance *instance,
+ u_char *buf,
+ size_t len
+ )
+{
+ const char *cp;
+
+ cp = "Oncore: System Failure at Power On";
+ if (instance && instance->peer) {
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ oncore_shutdown(instance->unit, instance->peer);
+ }
+}
+
+/************** Small Subroutines ***************/
+
+
+static void
+oncore_antenna_report(
+ struct instance *instance,
+ enum antenna_state new_state)
+{
+ char *cp;
+
+ if (instance->ant_state == new_state)
+ return;
+
+ switch (new_state) {
+ case ONCORE_ANTENNA_OK: cp = "GPS antenna: OK"; break;
+ case ONCORE_ANTENNA_OC: cp = "GPS antenna: short (overcurrent)"; break;
+ case ONCORE_ANTENNA_UC: cp = "GPS antenna: open (not connected)"; break;
+ case ONCORE_ANTENNA_NV: cp = "GPS antenna: short (no voltage)"; break;
+ default: cp = "GPS antenna: ?"; break;
+ }
+
+ instance->ant_state = new_state;
+ record_clock_stats(&instance->peer->srcadr, cp);
+}
+
+
+
+static void
+oncore_chan_test(
+ struct instance *instance
+ )
+{
+ char *cp;
+
+ /* subroutine oncore_Cj_id has determined the number of channels from the
+ * model number of the attached oncore. This is not always correct since
+ * the oncore could have non-standard firmware. Here we check (independently) by
+ * trying a 6, 8, and 12 chan command, and see which responds.
+ * Caution: more than one CAN respond.
+ *
+ * This #chan is used by the code rather than that calculated from the model number.
+ */
+
+ instance->o_state = ONCORE_CHECK_CHAN;
+ cp = "state = ONCORE_CHECK_CHAN";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ instance->count3 = 1;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ba, sizeof(oncore_cmd_Ba));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof(oncore_cmd_Ea));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ha, sizeof(oncore_cmd_Ha));
+}
+
+
+
+/* check for a GOOD Almanac, have we got one yet? */
+
+static void
+oncore_check_almanac(
+ struct instance *instance
+ )
+{
+ if (instance->chan == 6) {
+ instance->rsm.bad_almanac = instance->BEHa[64]&0x1;
+ instance->rsm.bad_fix = instance->BEHa[64]&0x52;
+ } else if (instance->chan == 8) {
+ instance->rsm.bad_almanac = instance->BEHa[72]&0x1;
+ instance->rsm.bad_fix = instance->BEHa[72]&0x52;
+ } else if (instance->chan == 12) {
+ int bits1, bits2;
+
+ bits1 = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */
+ bits2 = instance->BEHa[130];
+ instance->rsm.bad_almanac = (bits2 & 0x80);
+ instance->rsm.bad_fix = (bits2 & 0x8) || (bits1 == 0x2);
+ /* too few sat Bad Geom */
+#if 0
+ fprintf(stderr, "ONCORE[%d]: DEBUG BITS: (%x %x), (%x %x), %x %x %x %x %x\n",
+ instance->unit,
+ instance->BEHa[129], instance->BEHa[130], bits1, bits2, instance->mode == MODE_0D,
+ instance->mode == MODE_2D, instance->mode == MODE_3D,
+ instance->rsm.bad_almanac, instance->rsm.bad_fix);
+#endif
+ }
+}
+
+
+
+/* check the antenna for changes (did it get unplugged?) */
+
+static void
+oncore_check_antenna(
+ struct instance *instance
+ )
+{
+ enum antenna_state antenna; /* antenna state */
+
+ antenna = instance->ant_state;
+ if (instance->chan == 12)
+ antenna = (instance->BEHa[130] & 0x6 ) >> 1;
+ else
+ antenna = (instance->BEHa[37] & 0xc0) >> 6; /* prob unset 6, set GT, UT unset VP */
+
+ oncore_antenna_report (instance, antenna);
+}
+
+
+
+/*
+ * Check the leap second status once per day.
+ *
+ * Note that the ONCORE firmware for the Bj command is wrong at
+ * least in the VP.
+ * It starts advertising a LEAP SECOND as soon as the GPS satellite
+ * data message (page 18, subframe 4) is updated to a date in the
+ * future, and does not wait for the month that it will occur.
+ * The event will usually be advertised several months in advance.
+ * Since there is a one bit flag, there is no way to tell if it is
+ * this month, or when...
+ *
+ * As such, we have the workaround below, of only checking for leap
+ * seconds with the Bj command in June/December.
+ *
+ * The Gj command gives more information, and we can tell in which
+ * month to apply the correction.
+ *
+ * Note that with the VP we COULD read the raw data message, and
+ * interpret it ourselves, but since this is specific to this receiver
+ * only, and the above workaround is adequate, we don't bother.
+ */
+
+static void
+oncore_check_leap_sec(
+ struct instance *instance
+ )
+{
+ if (instance->Bj_day != instance->BEHa[5]) { /* do this 1/day */
+ instance->Bj_day = instance->BEHa[5];
+
+ if (instance->saw_Gj < 0) { /* -1 DONT have Gj use Bj */
+ if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12))
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
+ return;
+ }
+
+ if (instance->saw_Gj == 0) /* 0 is dont know if we have Gj */
+ instance->count4 = 1;
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gj, sizeof(oncore_cmd_Gj));
+ return;
+ }
+
+ /* Gj works for some 6/8 chan UT and the M12 */
+ /* if no response from Gj in 5 sec, we try Bj */
+ /* which isnt implemented in all the GT/UT either */
+
+ if (instance->count4) { /* delay, waiting for Gj response */
+ if (instance->saw_Gj == 1)
+ instance->count4 = 0;
+ else if (instance->count4++ > 5) { /* delay, waiting for Gj response */
+ instance->saw_Gj = -1; /* didnt see it, will use Bj */
+ instance->count4 = 0;
+ if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12))
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof(oncore_cmd_Bj));
+ }
+ }
+}
+
+
+
+/* check the message checksum,
+ * buf points to START of message ( @@ )
+ * len is length WITH CR/LF.
+ */
+
+static int
+oncore_checksum_ok(
+ u_char *buf,
+ int len
+ )
+{
+ int i, j;
+
+ j = 0;
+ for (i = 2; i < len-3; i++)
+ j ^= buf[i];
+
+ return(j == buf[len-3]);
+}
+
+
+
+static void
+oncore_compute_dH(
+ struct instance *instance
+ )
+{
+ int GPS, MSL;
+ char Msg[160];
+
+ /* Here calculate dH = GPS - MSL for output message */
+ /* also set Altitude Hold mode if GT */
+
+ instance->have_dH = 1;
+ if (instance->chan == 12) {
+ GPS = buf_w32(&instance->BEHa[39]);
+ MSL = buf_w32(&instance->BEHa[43]);
+ } else {
+ GPS = buf_w32(&instance->BEHa[23]);
+ MSL = buf_w32(&instance->BEHa[27]);
+ }
+ instance->dH = GPS - MSL;
+ instance->dH /= 100.;
+
+ /* if MSL is not set, the calculation is meaningless */
+
+ if (MSL) { /* not set ! */
+ sprintf(Msg, "dH = (GPS - MSL) = %.2fm", instance->dH);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+}
+
+
+
+/*
+ * try loading Almanac from shmem (where it was copied from shmem_old
+ */
+
+static void
+oncore_load_almanac(
+ struct instance *instance
+ )
+{
+ u_char *cp, Cmd[20];
+ int n;
+ struct timeval tv;
+ struct tm *tm;
+
+ if (!instance->shmem)
+ return;
+
+#if 1
+ for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) {
+ if (!strncmp(cp, "@@Cb", 4) &&
+ oncore_checksum_ok(cp, 33) &&
+ (*(cp+4) == 4 || *(cp+4) == 5)) {
+ write(instance->ttyfd, cp, n);
+#if 1
+ oncore_print_Cb(instance, cp);
+#endif
+ }
+ }
+#else
+/************DEBUG************/
+ for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) {
+ char Msg[160];
+
+ sprintf(Msg, "See %c%c%c%c %d", *(cp), *(cp+1), *(cp+2), *(cp+3), *(cp+4));
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ if (!strncmp(cp, "@@Cb", 4)) {
+ oncore_print_Cb(instance, cp);
+ if (oncore_checksum_ok(cp, 33)) {
+ if (*(cp+4) == 4 || *(cp+4) == 5) {
+ record_clock_stats(&(instance->peer->srcadr), "GOOD SF");
+ write(instance->ttyfd, cp, n);
+ } else
+ record_clock_stats(&(instance->peer->srcadr), "BAD SF");
+ } else
+ record_clock_stats(&(instance->peer->srcadr), "BAD CHECKSUM");
+ }
+ }
+/************DEBUG************/
+#endif
+
+ /* Must load position and time or the Almanac doesn't do us any good */
+
+ if (!instance->posn_set) { /* if we input a posn use it, else from SHMEM */
+ record_clock_stats(&(instance->peer->srcadr), "Loading Posn from SHMEM");
+ for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) {
+ if ((instance->chan == 6 && (!strncmp(cp, "@@Ba", 4) && oncore_checksum_ok(cp, 68))) ||
+ (instance->chan == 8 && (!strncmp(cp, "@@Ea", 4) && oncore_checksum_ok(cp, 76))) ||
+ (instance->chan == 12 && (!strncmp(cp, "@@Ha", 4) && oncore_checksum_ok(cp, 154)))) {
+ int ii, jj, kk;
+
+ instance->posn_set = 1;
+ ii = buf_w32(cp + 15);
+ jj = buf_w32(cp + 19);
+ kk = buf_w32(cp + 23);
+{
+char Msg[160];
+sprintf(Msg, "SHMEM posn = %d (%d, %d, %d)", cp-instance->shmem, ii, jj, kk);
+record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+ if (ii != 0 || jj != 0 || kk != 0) { /* phk asked for this test */
+ instance->ss_lat = ii;
+ instance->ss_long = jj;
+ instance->ss_ht = kk;
+ }
+ }
+ }
+ }
+ oncore_set_posn(instance);
+
+ /* and set time to time from Computer clock */
+
+ gettimeofday(&tv, 0);
+ tm = gmtime((const time_t *) &tv.tv_sec);
+#if 1
+ {
+ char Msg[160];
+ sprintf(Msg, "DATE %d %d %d, %d %d %d", 1900+tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ }
+#endif
+ if (instance->chan == 12) {
+ memcpy(Cmd, oncore_cmd_Gb, (size_t) sizeof(oncore_cmd_Gb));
+ Cmd[-2+4] = tm->tm_mon;
+ Cmd[-2+5] = tm->tm_mday;
+ Cmd[-2+6] = (1900+tm->tm_year)/256;
+ Cmd[-2+7] = (1900+tm->tm_year)%256;
+ Cmd[-2+8] = tm->tm_hour;
+ Cmd[-2+9] = tm->tm_min;
+ Cmd[-2+10] = tm->tm_sec;
+ Cmd[-2+11] = 0;
+ Cmd[-2+12] = 0;
+ Cmd[-2+13] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Gb));
+ } else {
+ /* First set GMT offset to zero */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ab, sizeof(oncore_cmd_Ab));
+
+ memcpy(Cmd, oncore_cmd_Ac, (size_t) sizeof(oncore_cmd_Ac));
+ Cmd[-2+4] = tm->tm_mon;
+ Cmd[-2+5] = tm->tm_mday;
+ Cmd[-2+6] = (1900+tm->tm_year)/256;
+ Cmd[-2+7] = (1900+tm->tm_year)%256;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ac));
+
+ memcpy(Cmd, oncore_cmd_Aa, (size_t) sizeof(oncore_cmd_Aa));
+ Cmd[-2+4] = tm->tm_hour;
+ Cmd[-2+5] = tm->tm_min;
+ Cmd[-2+6] = tm->tm_sec;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Aa));
+ }
+
+ record_clock_stats(&(instance->peer->srcadr), "Setting Posn and Time after Loading Almanac");
+}
+
+
+
+/* Almanac data input */
+
+static void
+oncore_print_Cb(
+ struct instance *instance,
+ u_char *cp
+ )
+{
+#if 0
+ int ii;
+ char Msg[160];
+
+ printf("DEBUG: See: %c%c%c%c\n", *(cp), *(cp+1), *(cp+2), *(cp+3));
+ printf("DEBUG: Cb: [%d,%d]", *(cp+4), *(cp+5));
+ for(ii=0; ii<33; ii++)
+ printf(" %d", *(cp+ii));
+ printf("\n");
+
+ sprintf(Msg, "Debug: Cb: [%d,%d]", *(cp+4), *(cp+5));
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+#endif
+}
+
+
+#if 0
+static void
+oncore_print_array(
+ u_char *cp,
+ int n
+ )
+{
+ int jj, i, j, nn;
+
+ nn = 0;
+ printf("\nTOP\n");
+ jj = n/16;
+ for (j=0; j<jj; j++) {
+ printf("%4d: ", nn);
+ nn += 16;
+ for (i=0; i<16; i++)
+ printf(" %o", *cp++);
+ printf("\n");
+ }
+}
+#endif
+
+
+static void
+oncore_print_posn(
+ struct instance *instance
+ )
+{
+ char Msg[120], ew, ns;
+ double xd, xm, xs, yd, ym, ys, hm, hft;
+ int idx, idy, is, imx, imy;
+ long lat, lon;
+
+ record_clock_stats(&(instance->peer->srcadr), "Posn:");
+ ew = 'E';
+ lon = instance->ss_long;
+ if (lon < 0) {
+ ew = 'W';
+ lon = -lon;
+ }
+
+ ns = 'N';
+ lat = instance->ss_lat;
+ if (lat < 0) {
+ ns = 'S';
+ lat = -lat;
+ }
+
+ hm = instance->ss_ht/100.;
+ hft= hm/0.3048;
+
+ xd = lat/3600000.; /* lat, lon in int msec arc, ht in cm. */
+ yd = lon/3600000.;
+ sprintf(Msg, "Lat = %c %11.7fdeg, Long = %c %11.7fdeg, Alt = %5.2fm (%5.2fft) GPS", ns, xd, ew, yd, hm, hft);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ idx = xd;
+ idy = yd;
+ imx = lat%3600000;
+ imy = lon%3600000;
+ xm = imx/60000.;
+ ym = imy/60000.;
+ sprintf(Msg,
+ "Lat = %c %3ddeg %7.4fm, Long = %c %3ddeg %8.5fm, Alt = %7.2fm (%7.2fft) GPS", ns, idx, xm, ew, idy, ym, hm, hft);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ imx = xm;
+ imy = ym;
+ is = lat%60000;
+ xs = is/1000.;
+ is = lon%60000;
+ ys = is/1000.;
+ sprintf(Msg,
+ "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+}
+
+
+
+/*
+ * write message to Oncore.
+ */
+
+static void
+oncore_sendmsg(
+ int fd,
+ u_char *ptr,
+ size_t len
+ )
+{
+ u_char cs = 0;
+
+ if (debug > 4)
+ printf("ONCORE: Send @@%c%c %d\n", ptr[0], ptr[1], (int) len);
+ write(fd, "@@", (size_t) 2);
+ write(fd, ptr, len);
+ while (len--)
+ cs ^= *ptr++;
+ write(fd, &cs, (size_t) 1);
+ write(fd, "\r\n", (size_t) 2);
+}
+
+
+
+static void
+oncore_set_posn(
+ struct instance *instance
+ )
+{
+ int mode;
+ char Cmd[20];
+
+ /* Turn OFF position hold, it needs to be off to set position (for some units),
+ will get set ON in @@Ea later */
+
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* (12) */
+ else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* (6/8) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* (6/8) */
+ }
+
+ mode = instance->init_type;
+
+ if (mode != 0) { /* first set posn hold position */
+ memcpy(Cmd, oncore_cmd_As, (size_t) sizeof(oncore_cmd_As)); /* don't modify static variables */
+ w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
+ w32_buf(&Cmd[-2+8], (int) instance->ss_long);
+ w32_buf(&Cmd[-2+12], (int) instance->ss_ht);
+ Cmd[-2+16] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_As)); /* posn hold 3D posn (6/8/12) */
+
+ memcpy(Cmd, oncore_cmd_Au, (size_t) sizeof(oncore_cmd_Au));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
+ Cmd[-2+8] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Au)); /* altitude hold (6/8/12 not UT, M12T) */
+
+ /* next set current position */
+
+ if (instance->chan == 12) {
+ memcpy(Cmd, oncore_cmd_Ga, (size_t) sizeof(oncore_cmd_Ga));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
+ w32_buf(&Cmd[-2+8], (int) instance->ss_long);
+ w32_buf(&Cmd[-2+12],(int) instance->ss_ht);
+ Cmd[-2+16] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ga)); /* 3d posn (12) */
+ } else {
+ memcpy(Cmd, oncore_cmd_Ad, (size_t) sizeof(oncore_cmd_Ad));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_lat);
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ad)); /* lat (6/8) */
+
+ memcpy(Cmd, oncore_cmd_Ae, (size_t) sizeof(oncore_cmd_Ae));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_long);
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Ae)); /* long (6/8) */
+
+ memcpy(Cmd, oncore_cmd_Af, (size_t) sizeof(oncore_cmd_Af));
+ w32_buf(&Cmd[-2+4], (int) instance->ss_ht);
+ Cmd[-2+8] = 0;
+ oncore_sendmsg(instance->ttyfd, Cmd, sizeof(oncore_cmd_Af)); /* ht (6/8) */
+ }
+
+ /* Finally, turn on position hold */
+
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1));
+ else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At1, sizeof(oncore_cmd_At1));
+ }
+}
+
+
+
+static void
+oncore_set_traim(
+ struct instance *instance
+ )
+{
+ char Msg[160];
+
+ if (instance->traim_in != -1) /* set in Input */
+ instance->traim = instance->traim_in;
+ else
+ instance->traim = instance->traim_ck;
+
+ sprintf(Msg, "Input says TRAIM = %d", instance->traim_in);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Model # says TRAIM = %d", instance->traim_id);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Testing says TRAIM = %d", instance->traim_ck);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ sprintf(Msg, "Using TRAIM = %d", instance->traim);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ if (instance->traim_ck == 1 && instance->traim == 0) {
+ /* if it should be off, and I turned it on during testing,
+ then turn it off again */
+ if (instance->chan == 6)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bnx, sizeof(oncore_cmd_Bnx));
+ else if (instance->chan == 8)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Enx, sizeof(oncore_cmd_Enx));
+ else /* chan == 12 */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ge0, sizeof(oncore_cmd_Ge0));
+ }
+}
+
+
+
+/*
+ * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn.
+ */
+
+static void
+oncore_shmem_get_3D(
+ struct instance *instance
+ )
+{
+ if (instance->pp->second%15 == 3) { /* start the sequence */ /* by changing mode */
+ instance->shmem_reset = 1;
+ if (instance->chan == 12) {
+ if (instance->shmem_Posn == 2)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd2, sizeof(oncore_cmd_Gd2)); /* 2D */
+ else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* 3D */
+ } else {
+ if (instance->saw_At) { /* out of 0D -> 3D mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At0, sizeof(oncore_cmd_At0));
+ if (instance->shmem_Posn == 2) /* 3D -> 2D mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
+ } else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
+ }
+ } else if (instance->shmem_reset || (instance->mode != MODE_0D)) {
+ instance->shmem_reset = 0;
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); /* 0D */
+ else {
+ if (instance->saw_At) {
+ if (instance->mode == MODE_2D) /* 2D -> 3D or 0D mode */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av0, sizeof(oncore_cmd_Av0));
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At1, sizeof(oncore_cmd_At1)); /* to 0D mode */
+ } else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Av1, sizeof(oncore_cmd_Av1));
+ }
+ }
+}
+
+
+
+/*
+ * Here we do the Software SiteSurvey.
+ * We have to average our own position for the Position Hold Mode
+ * We use Heights from the GPS ellipsoid.
+ * We check for the END of either HW or SW SiteSurvey.
+ */
+
+static void
+oncore_ss(
+ struct instance *instance
+ )
+{
+ char *cp, Msg[160];
+ double lat, lon, ht;
+
+
+ if (instance->site_survey == ONCORE_SS_HW) {
+
+ /*
+ * Check to see if Hardware SiteSurvey has Finished.
+ */
+
+ if ((instance->chan == 8 && !(instance->BEHa[37] & 0x20)) ||
+ (instance->chan == 12 && !(instance->BEHa[130] & 0x10))) {
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+
+ if (instance->chan == 12)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gax, sizeof(oncore_cmd_Gax));
+ else
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Asx, sizeof(oncore_cmd_Asx));
+
+ cp = "SSstate = ONCORE_SS_DONE";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->site_survey = ONCORE_SS_DONE;
+ }
+ } else {
+ /*
+ * Must be a Software Site Survey.
+ */
+
+ if (instance->rsm.bad_fix) /* Not if poor geometry or less than 3 sats */
+ return;
+
+ if (instance->mode != MODE_3D) /* Use only 3D Fixes */
+ return;
+
+ instance->ss_lat += buf_w32(&instance->BEHa[15]);
+ instance->ss_long += buf_w32(&instance->BEHa[19]);
+ instance->ss_ht += buf_w32(&instance->BEHa[23]); /* GPS ellipsoid */
+ instance->ss_count++;
+
+ if (instance->ss_count != POS_HOLD_AVERAGE)
+ return;
+
+ instance->ss_lat /= POS_HOLD_AVERAGE;
+ instance->ss_long /= POS_HOLD_AVERAGE;
+ instance->ss_ht /= POS_HOLD_AVERAGE;
+
+ sprintf(Msg, "Surveyed posn: lat %.3f (mas) long %.3f (mas) ht %.3f (cm)",
+ instance->ss_lat, instance->ss_long, instance->ss_ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+ lat = instance->ss_lat/3600000.;
+ lon = instance->ss_long/3600000.;
+ ht = instance->ss_ht/100;
+ sprintf(Msg, "Surveyed posn: lat %.7f (deg) long %.7f (deg) ht %.2f (m)",
+ lat, lon, ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ oncore_set_posn(instance);
+
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+
+ cp = "SSstate = ONCORE_SS_DONE";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ instance->site_survey = ONCORE_SS_DONE;
+ }
+}
+
+
+
+static int
+oncore_wait_almanac(
+ struct instance *instance
+ )
+{
+ if (instance->rsm.bad_almanac) {
+ if (debug)
+ printf("ONCORE[%d]: waiting for almanac\n", instance->unit);
+
+ /*
+ * If we get here (first time) then we don't have an almanac in memory.
+ * Check if we have a SHMEM, and if so try to load whatever is there.
+ */
+
+ if (!instance->almanac_from_shmem) {
+ instance->almanac_from_shmem = 1;
+ oncore_load_almanac(instance);
+ }
+ return(1);
+ } else { /* Here we have the Almanac, we will be starting the @@Bn/@@En/@@Hn
+ commands, and can finally check for TRAIM. Again, we set a delay
+ (5sec) and wait for things to settle down */
+
+ if (instance->chan == 6)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bn, sizeof(oncore_cmd_Bn));
+ else if (instance->chan == 8)
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_En, sizeof(oncore_cmd_En));
+ else if (instance->chan == 12) {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* 1PPS on, continuous */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ge, sizeof(oncore_cmd_Ge)); /* TRAIM on */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Hn, sizeof(oncore_cmd_Hn)); /* TRAIM status 1/s */
+ }
+ instance->traim_delay = 1;
+
+ record_clock_stats(&(instance->peer->srcadr), "Have now loaded an ALMANAC");
+
+ instance->o_state = ONCORE_RUN;
+ record_clock_stats(&(instance->peer->srcadr), "state = ONCORE_RUN");
+ }
+ return(0);
+}
+
+
+
+#else
+int refclock_oncore_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_palisade.c b/ntpd/refclock_palisade.c
new file mode 100644
index 0000000..897221b
--- /dev/null
+++ b/ntpd/refclock_palisade.c
@@ -0,0 +1,954 @@
+/*
+ * This software was developed by the Software and Component Technologies
+ * group of Trimble Navigation, Ltd.
+ *
+ * Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Trimble Navigation, Ltd.
+ * 4. The name of Trimble Navigation Ltd. may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TRIMBLE NAVIGATION LTD. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL TRIMBLE NAVIGATION LTD. BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * refclock_palisade - clock driver for the Trimble Palisade GPS
+ * timing receiver
+ *
+ * For detailed information on this program, please refer to the html
+ * Refclock 29 page accompanying the NTP distribution.
+ *
+ * for questions / bugs / comments, contact:
+ * sven_dietrich@trimble.com
+ *
+ * Sven-Thorsten Dietrich
+ * 645 North Mary Avenue
+ * Post Office Box 3642
+ * Sunnyvale, CA 94088-3642
+ *
+ * Version 2.45; July 14, 1999
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(SYS_WINNT)
+#undef close
+#define close closesocket
+#endif
+
+#if defined(REFCLOCK) && (defined(PALISADE) || defined(CLOCK_PALISADE))
+
+#include "refclock_palisade.h"
+/* Table to get from month to day of the year */
+const int days_of_year [12] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+};
+
+#ifdef DEBUG
+const char * Tracking_Status[15][15] = {
+ { "Doing Fixes\0" }, { "Good 1SV\0" }, { "Approx. 1SV\0" },
+ {"Need Time\0" }, { "Need INIT\0" }, { "PDOP too High\0" },
+ { "Bad 1SV\0" }, { "0SV Usable\0" }, { "1SV Usable\0" },
+ { "2SV Usable\0" }, { "3SV Usable\0" }, { "No Integrity\0" },
+ { "Diff Corr\0" }, { "Overdet Clock\0" }, { "Invalid\0" } };
+#endif
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_palisade = {
+ palisade_start, /* start up driver */
+ palisade_shutdown, /* shut down driver */
+ palisade_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+int day_of_year P((char *dt));
+
+/* Extract the clock type from the mode setting */
+#define CLK_TYPE(x) ((int)(((x)->ttl) & 0x7F))
+
+/* Supported clock types */
+#define CLK_TRIMBLE 0 /* Trimble Palisade */
+#define CLK_PRAECIS 1 /* Endrun Technologies Praecis */
+
+int praecis_msg;
+static void praecis_parse(struct recvbuf *rbufp, struct peer *peer);
+
+/*
+ * palisade_start - open the devices and initialize data for processing
+ */
+static int
+palisade_start (
+#ifdef PALISADE
+ unit, peer
+ )
+ int unit;
+ struct peer *peer;
+#else /* ANSI */
+ int unit,
+ struct peer *peer
+ )
+#endif
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ struct termios tio;
+#ifdef SYS_WINNT
+ (void) sprintf(gpsdev, "COM%d:", unit);
+#else
+ (void) sprintf(gpsdev, DEVICE, unit);
+#endif
+ /*
+ * Open serial port.
+ */
+#if defined PALISADE
+ fd = open(gpsdev, O_RDWR
+#ifdef O_NONBLOCK
+ | O_NONBLOCK
+#endif
+ );
+#else /* NTP 4.x */
+ fd = refclock_open(gpsdev, SPEED232, LDISC_RAW);
+#endif
+ if (fd <= 0) {
+#ifdef DEBUG
+ printf("Palisade(%d) start: open %s failed\n", unit, gpsdev);
+#endif
+ return 0;
+ }
+
+ msyslog(LOG_NOTICE, "Palisade(%d) fd: %d dev: %s", unit, fd,
+ gpsdev);
+
+#if defined PALISADE
+ tio.c_cflag = (CS8|CLOCAL|CREAD|PARENB|PARODD);
+ tio.c_iflag = (IGNBRK);
+ tio.c_oflag = (0);
+ tio.c_lflag = (0);
+
+ if (cfsetispeed(&tio, SPEED232) == -1) {
+ msyslog(LOG_ERR,"Palisade(%d) cfsetispeed(fd, &tio): %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) cfsetispeed(fd, &tio)\n",unit);
+#endif
+ return 0;
+ }
+ if (cfsetospeed(&tio, SPEED232) == -1) {
+#ifdef DEBUG
+ printf("Palisade(%d) cfsetospeed(fd, &tio)\n",unit);
+#endif
+ msyslog(LOG_ERR,"Palisade(%d) cfsetospeed(fd, &tio): %m",unit);
+ return 0;
+ }
+#else /* NTP 4.x */
+ if (tcgetattr(fd, &tio) < 0) {
+ msyslog(LOG_ERR,
+ "Palisade(%d) tcgetattr(fd, &tio): %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) tcgetattr(fd, &tio)\n",unit);
+#endif
+ return (0);
+ }
+
+ tio.c_cflag |= (PARENB|PARODD);
+ tio.c_iflag &= ~ICRNL;
+#endif /* NTP 4.x */
+
+ if (tcsetattr(fd, TCSANOW, &tio) == -1) {
+ msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) tcsetattr(fd, &tio)\n",unit);
+#endif
+ return 0;
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ up = (struct palisade_unit *) emalloc(sizeof(struct palisade_unit));
+
+ if (!(up)) {
+ msyslog(LOG_ERR, "Palisade(%d) emalloc: %m",unit);
+#ifdef DEBUG
+ printf("Palisade(%d) emalloc\n",unit);
+#endif
+ (void) close(fd);
+ return (0);
+ }
+
+ memset((char *)up, 0, sizeof(struct palisade_unit));
+
+ up->type = CLK_TYPE(peer);
+ switch (up->type) {
+ case CLK_TRIMBLE:
+ /* Normal mode, do nothing */
+ break;
+ case CLK_PRAECIS:
+ msyslog(LOG_NOTICE, "Palisade(%d) Praecis mode enabled\n",unit);
+ break;
+ default:
+ msyslog(LOG_NOTICE, "Palisade(%d) mode unknown\n",unit);
+ break;
+ }
+
+ pp = peer->procptr;
+ pp->io.clock_recv = palisade_io;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+#ifdef DEBUG
+ printf("Palisade(%d) io_addclock\n",unit);
+#endif
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ pp->unitptr = (caddr_t)up;
+ pp->clockdesc = DESCRIPTION;
+
+ peer->precision = PRECISION;
+ peer->sstclktype = CTL_SST_TS_UHF;
+ peer->minpoll = TRMB_MINPOLL;
+ peer->maxpoll = TRMB_MAXPOLL;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ up->leap_status = 0;
+ up->unit = (short) unit;
+ up->rpt_status = TSIP_PARSED_EMPTY;
+ up->rpt_cnt = 0;
+
+ return 1;
+}
+
+
+/*
+ * palisade_shutdown - shut down the clock
+ */
+static void
+palisade_shutdown (
+#ifdef PALISADE
+ unit, peer
+ )
+ int unit;
+ struct peer *peer;
+#else /* ANSI */
+ int unit,
+ struct peer *peer
+ )
+#endif
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+
+/*
+ * unpack_date - get day and year from date
+ */
+int
+day_of_year (
+#ifdef PALISADE
+ dt
+ )
+ char * dt;
+#else
+ char * dt
+ )
+#endif
+{
+ int day, mon, year;
+
+ mon = dt[1];
+ /* Check month is inside array bounds */
+ if ((mon < 1) || (mon > 12))
+ return -1;
+
+ day = dt[0] + days_of_year[mon - 1];
+ year = getint((u_char *) (dt + 2));
+
+ if ( !(year % 4) && ((year % 100) ||
+ (!(year % 100) && !(year%400)))
+ &&(mon > 2))
+ day ++; /* leap year and March or later */
+
+ return day;
+}
+
+
+/*
+ * TSIP_decode - decode the TSIP data packets
+ */
+int
+TSIP_decode (
+#ifdef PALISADE
+ peer
+ )
+ struct peer *peer;
+#else
+ struct peer *peer
+ )
+#endif
+{
+ int st;
+ long secint;
+ double secs;
+ double secfrac;
+ unsigned short event = 0;
+
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+
+ /*
+ * Check the time packet, decode its contents.
+ * If the timecode has invalid length or is not in
+ * proper format, declare bad format and exit.
+ */
+
+ if ((up->rpt_buf[0] == (char) 0x41) ||
+ (up->rpt_buf[0] == (char) 0x46) ||
+ (up->rpt_buf[0] == (char) 0x54) ||
+ (up->rpt_buf[0] == (char) 0x4B) ||
+ (up->rpt_buf[0] == (char) 0x6D)) {
+
+ /* standard time packet - GPS time and GPS week number */
+#ifdef DEBUG
+ printf("Palisade Port B packets detected. Connect to Port A\n");
+#endif
+
+ return 0;
+ }
+
+ /*
+ * We cast both to u_char to as 0x8f uses the sign bit on a char
+ */
+ if ((u_char) up->rpt_buf[0] == (u_char) 0x8f) {
+ /*
+ * Superpackets
+ */
+ event = (unsigned short) (getint((u_char *) &mb(1)) & 0xffff);
+ if (!((pp->sloppyclockflag & CLK_FLAG2) || event))
+ /* Ignore Packet */
+ return 0;
+
+ switch (mb(0) & 0xff) {
+ int GPS_UTC_Offset;
+ case PACKET_8F0B:
+
+ if (up->polled <= 0)
+ return 0;
+
+ if (up->rpt_cnt != LENCODE_8F0B) /* check length */
+ break;
+
+#ifdef DEBUG
+if (debug > 1) {
+ int ts;
+ double lat, lon, alt;
+ lat = getdbl((u_char *) &mb(42)) * R2D;
+ lon = getdbl((u_char *) &mb(50)) * R2D;
+ alt = getdbl((u_char *) &mb(58));
+
+ printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
+ up->unit, lat,lon,alt);
+ printf("TSIP_decode: unit %d: Sats:", up->unit);
+ for (st = 66, ts = 0; st <= 73; st++) if (mb(st)) {
+ if (mb(st) > 0) ts++;
+ printf(" %02d", mb(st));
+ }
+ printf(" : Tracking %d\n", ts);
+ }
+#endif
+
+ GPS_UTC_Offset = getint((u_char *) &mb(16));
+ if (GPS_UTC_Offset == 0) { /* Check UTC offset */
+#ifdef DEBUG
+ printf("TSIP_decode: UTC Offset Unknown\n");
+#endif
+ break;
+ }
+
+ secs = getdbl((u_char *) &mb(3));
+ secint = (long) secs;
+ secfrac = secs - secint; /* 0.0 <= secfrac < 1.0 */
+
+ pp->nsec = (long) (secfrac * 1000000000);
+
+ secint %= 86400; /* Only care about today */
+ pp->hour = secint / 3600;
+ secint %= 3600;
+ pp->minute = secint / 60;
+ secint %= 60;
+ pp->second = secint % 60;
+
+ if ((pp->day = day_of_year(&mb(11))) < 0) break;
+
+ pp->year = getint((u_char *) &mb(13));
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02d\n",
+ up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
+ pp->second, pp->nsec, mb(12), mb(11), pp->year, GPS_UTC_Offset);
+#endif
+ /* Only use this packet when no
+ * 8F-AD's are being received
+ */
+
+ if (up->leap_status) {
+ up->leap_status = 0;
+ return 0;
+ }
+
+ return 2;
+ break;
+
+ case PACKET_NTP:
+ /* Palisade-NTP Packet */
+
+ if (up->rpt_cnt != LENCODE_NTP) /* check length */
+ break;
+
+ up->leap_status = mb(19);
+
+ if (up->polled <= 0)
+ return 0;
+
+ /* Check Tracking Status */
+ st = mb(18);
+ if (st < 0 || st > 14) st = 14;
+ if ((st >= 2 && st <= 7) || st == 11 || st == 12) {
+#ifdef DEBUG
+ printf("TSIP_decode: Not Tracking Sats : %s\n",
+ *Tracking_Status[st]);
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ up->polled = -1;
+ return 0;
+ break;
+ }
+
+ if (up->leap_status & PALISADE_LEAP_PENDING) {
+ if (up->leap_status & PALISADE_UTC_TIME)
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_DELSECOND;
+ }
+ else if (up->leap_status)
+ pp->leap = LEAP_NOWARNING;
+
+ else { /* UTC flag is not set:
+ * Receiver may have been reset, and lost
+ * its UTC almanac data */
+ pp->leap = LEAP_NOTINSYNC;
+#ifdef DEBUG
+ printf("TSIP_decode: UTC Almanac unavailable: %d\n",
+ mb(19));
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ up->polled = -1;
+ return 0;
+ }
+
+ pp->nsec = (long) (getdbl((u_char *) &mb(3)) * 1000000);
+
+ if ((pp->day = day_of_year(&mb(14))) < 0)
+ break;
+ pp->year = getint((u_char *) &mb(16));
+ pp->hour = mb(11);
+ pp->minute = mb(12);
+ pp->second = mb(13);
+
+#ifdef DEBUG
+ if (debug > 1)
+printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%06ld %02d/%02d/%04d UTC %02x %s\n",
+ up->unit, mb(0) & 0xff, event, pp->hour, pp->minute,
+ pp->second, pp->nsec, mb(15), mb(14), pp->year,
+ mb(19), *Tracking_Status[st]);
+#endif
+ return 1;
+ break;
+
+ default:
+ /* Ignore Packet */
+ return 0;
+ } /* switch */
+ }/* if 8F packets */
+
+ refclock_report(peer, CEVNT_BADREPLY);
+ up->polled = -1;
+#ifdef DEBUG
+ printf("TSIP_decode: unit %d: bad packet %02x-%02x event %d len %d\n",
+ up->unit, up->rpt_buf[0] & 0xff, mb(0) & 0xff,
+ event, up->rpt_cnt);
+#endif
+ return 0;
+}
+
+/*
+ * palisade__receive - receive data from the serial interface
+ */
+
+static void
+palisade_receive (
+#ifdef PALISADE
+ peer
+ )
+ struct peer * peer;
+#else /* ANSI */
+ struct peer * peer
+ )
+#endif
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+
+ if (! TSIP_decode(peer)) return;
+
+ if (up->polled <= 0)
+ return; /* no poll pending, already received or timeout */
+
+ up->polled = 0; /* Poll reply received */
+ pp->lencode = 0; /* clear time code */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "palisade_receive: unit %d: %4d %03d %02d:%02d:%02d.%06ld\n",
+ up->unit, pp->year, pp->day, pp->hour, pp->minute,
+ pp->second, pp->nsec);
+#endif
+
+ /*
+ * Process the sample
+ * Generate timecode: YYYY DoY HH:MM:SS.microsec
+ * report and process
+ */
+
+ (void) sprintf(pp->a_lastcode,"%4d %03d %02d:%02d:%02d.%06ld",
+ pp->year,pp->day,pp->hour,pp->minute, pp->second,pp->nsec);
+ pp->lencode = 24;
+
+#ifdef PALISADE
+ pp->lasttime = current_time;
+#endif
+ if (!refclock_process(pp
+#ifdef PALISADE
+ , PALISADE_SAMPLES, PALISADE_SAMPLES * 3 / 5
+#endif
+ )) {
+ refclock_report(peer, CEVNT_BADTIME);
+
+#ifdef DEBUG
+ printf("palisade_receive: unit %d: refclock_process failed!\n",
+ up->unit);
+#endif
+ return;
+ }
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+#ifdef DEBUG
+ if (debug)
+ printf("palisade_receive: unit %d: %s\n",
+ up->unit, prettydate(&pp->lastrec));
+#endif
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer
+#ifdef PALISADE
+ , &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap
+#endif
+ );
+}
+
+
+/*
+ * palisade_poll - called by the transmit procedure
+ *
+ */
+static void
+palisade_poll (
+#ifdef PALISADE
+ unit, peer
+ )
+ int unit;
+ struct peer *peer;
+#else
+ int unit,
+ struct peer *peer
+ )
+#endif
+{
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+
+ pp->polls++;
+ if (up->polled > 0) /* last reply never arrived or error */
+ refclock_report(peer, CEVNT_TIMEOUT);
+
+ up->polled = 2; /* synchronous packet + 1 event */
+
+#ifdef DEBUG
+ if (debug)
+ printf("palisade_poll: unit %d: polling %s\n", unit,
+ (pp->sloppyclockflag & CLK_FLAG2) ?
+ "synchronous packet" : "event");
+#endif
+
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ return; /* using synchronous packet input */
+
+ if(up->type == CLK_PRAECIS) {
+ if(write(peer->procptr->io.fd,"SPSTAT\r\n",8) < 0)
+ msyslog(LOG_ERR, "Palisade(%d) write: %m:",unit);
+ else {
+ praecis_msg = 1;
+ return;
+ }
+ }
+
+ if (HW_poll(pp) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+}
+
+static void
+praecis_parse(struct recvbuf *rbufp, struct peer *peer)
+{
+ static char buf[100];
+ static int p = 0;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ memcpy(buf+p,rbufp->recv_space.X_recv_buffer, rbufp->recv_length);
+ p += rbufp->recv_length;
+
+ if(buf[p-2] == '\r' && buf[p-1] == '\n') {
+ buf[p-2] = '\0';
+ record_clock_stats(&peer->srcadr, buf);
+
+ p = 0;
+ praecis_msg = 0;
+
+ if (HW_poll(pp) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+
+ }
+}
+
+static void
+palisade_io (
+#ifdef PALISADE
+ rbufp
+ )
+ struct recvbuf *rbufp;
+#else /* ANSI */
+ struct recvbuf *rbufp
+ )
+#endif
+{
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ struct palisade_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ char * c, * d;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct palisade_unit *)pp->unitptr;
+
+ if(up->type == CLK_PRAECIS) {
+ if(praecis_msg) {
+ praecis_parse(rbufp,peer);
+ return;
+ }
+ }
+
+ c = (char *) &rbufp->recv_space;
+ d = c + rbufp->recv_length;
+
+ while (c != d) {
+
+ /* Build time packet */
+ switch (up->rpt_status) {
+
+ case TSIP_PARSED_DLE_1:
+ switch (*c)
+ {
+ case 0:
+ case DLE:
+ case ETX:
+ up->rpt_status = TSIP_PARSED_EMPTY;
+ break;
+
+ default:
+ up->rpt_status = TSIP_PARSED_DATA;
+ /* save packet ID */
+ up->rpt_buf[0] = *c;
+ break;
+ }
+ break;
+
+ case TSIP_PARSED_DATA:
+ if (*c == DLE)
+ up->rpt_status = TSIP_PARSED_DLE_2;
+ else
+ mb(up->rpt_cnt++) = *c;
+ break;
+
+ case TSIP_PARSED_DLE_2:
+ if (*c == DLE) {
+ up->rpt_status = TSIP_PARSED_DATA;
+ mb(up->rpt_cnt++) =
+ *c;
+ }
+ else if (*c == ETX)
+ up->rpt_status = TSIP_PARSED_FULL;
+ else {
+ /* error: start new report packet */
+ up->rpt_status = TSIP_PARSED_DLE_1;
+ up->rpt_buf[0] = *c;
+ }
+ break;
+
+ case TSIP_PARSED_FULL:
+ case TSIP_PARSED_EMPTY:
+ default:
+ if ( *c != DLE)
+ up->rpt_status = TSIP_PARSED_EMPTY;
+ else
+ up->rpt_status = TSIP_PARSED_DLE_1;
+ break;
+ }
+
+ c++;
+
+ if (up->rpt_status == TSIP_PARSED_DLE_1) {
+ up->rpt_cnt = 0;
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ /* stamp it */
+ get_systime(&pp->lastrec);
+ }
+ else if (up->rpt_status == TSIP_PARSED_EMPTY)
+ up->rpt_cnt = 0;
+
+ else if (up->rpt_cnt > BMAX)
+ up->rpt_status =TSIP_PARSED_EMPTY;
+
+ if (up->rpt_status == TSIP_PARSED_FULL)
+ palisade_receive(peer);
+
+ } /* while chars in buffer */
+}
+
+
+/*
+ * Trigger the Palisade's event input, which is driven off the RTS
+ *
+ * Take a system time stamp to match the GPS time stamp.
+ *
+ */
+long
+HW_poll (
+#ifdef PALISADE
+ pp /* pointer to unit structure */
+ )
+ struct refclockproc * pp; /* pointer to unit structure */
+#else
+ struct refclockproc * pp /* pointer to unit structure */
+ )
+#endif
+{
+ int x; /* state before & after RTS set */
+ struct palisade_unit *up;
+
+ up = (struct palisade_unit *) pp->unitptr;
+
+ /* read the current status, so we put things back right */
+ if (ioctl(pp->io.fd, TIOCMGET, &x) < 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("Palisade HW_poll: unit %d: GET %s\n", up->unit, strerror(errno));
+#endif
+ msyslog(LOG_ERR, "Palisade(%d) HW_poll: ioctl(fd,GET): %m",
+ up->unit);
+ return -1;
+ }
+
+ x |= TIOCM_RTS; /* turn on RTS */
+
+ /* Edge trigger */
+ if (ioctl(pp->io.fd, TIOCMSET, &x) < 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("Palisade HW_poll: unit %d: SET \n", up->unit);
+#endif
+ msyslog(LOG_ERR,
+ "Palisade(%d) HW_poll: ioctl(fd, SET, RTS_on): %m",
+ up->unit);
+ return -1;
+ }
+
+ x &= ~TIOCM_RTS; /* turn off RTS */
+
+ /* poll timestamp */
+ get_systime(&pp->lastrec);
+
+ if (ioctl(pp->io.fd, TIOCMSET, &x) == -1) {
+#ifdef DEBUG
+ if (debug)
+ printf("Palisade HW_poll: unit %d: UNSET \n", up->unit);
+#endif
+ msyslog(LOG_ERR,
+ "Palisade(%d) HW_poll: ioctl(fd, UNSET, RTS_off): %m",
+ up->unit);
+ return -1;
+ }
+
+ return 0;
+}
+
+#if 0 /* unused */
+/*
+ * this 'casts' a character array into a float
+ */
+float
+getfloat (
+#ifdef PALISADE
+ bp
+ )
+ u_char *bp;
+#else
+ u_char *bp
+ )
+#endif
+{
+ float sval;
+#ifdef WORDS_BIGENDIAN
+ ((char *) &sval)[0] = *bp++;
+ ((char *) &sval)[1] = *bp++;
+ ((char *) &sval)[2] = *bp++;
+ ((char *) &sval)[3] = *bp++;
+#else
+ ((char *) &sval)[3] = *bp++;
+ ((char *) &sval)[2] = *bp++;
+ ((char *) &sval)[1] = *bp++;
+ ((char *) &sval)[0] = *bp;
+#endif /* ! XNTP_BIG_ENDIAN */
+ return sval;
+}
+#endif
+
+/*
+ * this 'casts' a character array into a double
+ */
+double
+getdbl (
+#ifdef PALISADE
+ bp
+ )
+ u_char *bp;
+#else
+ u_char *bp
+ )
+#endif
+{
+ double dval;
+#ifdef WORDS_BIGENDIAN
+ ((char *) &dval)[0] = *bp++;
+ ((char *) &dval)[1] = *bp++;
+ ((char *) &dval)[2] = *bp++;
+ ((char *) &dval)[3] = *bp++;
+ ((char *) &dval)[4] = *bp++;
+ ((char *) &dval)[5] = *bp++;
+ ((char *) &dval)[6] = *bp++;
+ ((char *) &dval)[7] = *bp;
+#else
+ ((char *) &dval)[7] = *bp++;
+ ((char *) &dval)[6] = *bp++;
+ ((char *) &dval)[5] = *bp++;
+ ((char *) &dval)[4] = *bp++;
+ ((char *) &dval)[3] = *bp++;
+ ((char *) &dval)[2] = *bp++;
+ ((char *) &dval)[1] = *bp++;
+ ((char *) &dval)[0] = *bp;
+#endif /* ! XNTP_BIG_ENDIAN */
+ return dval;
+}
+
+/*
+ * cast a 16 bit character array into a short (16 bit) int
+ */
+short
+getint (
+#ifdef PALISADE
+ bp
+ )
+ u_char *bp;
+#else
+ u_char *bp
+ )
+#endif
+{
+return (short) (bp[1] + (bp[0] << 8));
+}
+
+#else
+int refclock_palisade_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_palisade.h b/ntpd/refclock_palisade.h
new file mode 100644
index 0000000..7e1ed49
--- /dev/null
+++ b/ntpd/refclock_palisade.h
@@ -0,0 +1,168 @@
+/*
+ * This software was developed by the Software and Component Technologies
+ * group of Trimble Navigation, Ltd.
+ *
+ * Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Trimble Navigation, Ltd.
+ * 4. The name of Trimble Navigation Ltd. may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TRIMBLE NAVIGATION LTD. ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL TRIMBLE NAVIGATION LTD. BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * refclock_palisade - clock driver for the Trimble Palisade GPS
+ * timing receiver
+ *
+ * For detailed information on this program, please refer to the html
+ * Refclock 29 page accompanying the NTP distribution.
+ *
+ * for questions / bugs / comments, contact:
+ * sven_dietrich@trimble.com
+ *
+ * Sven-Thorsten Dietrich
+ * 645 North Mary Avenue
+ * Post Office Box 3642
+ * Sunnyvale, CA 94088-3642
+ *
+ */
+
+#ifndef _REFCLOCK_PALISADE_H
+#define _REFCLOCK_PALISADE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined HAVE_SYS_MODEM_H
+#include <sys/modem.h>
+#define TIOCMSET MCSETA
+#define TIOCMGET MCGETA
+#define TIOCM_RTS MRTS
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# ifdef TERMIOS_NEEDS__SVID3
+# define _SVID3
+# endif
+# include <termios.h>
+# ifdef TERMIOS_NEEDS__SVID3
+# undef _SVID3
+# endif
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+/*
+ * GPS Definitions
+ */
+#define DESCRIPTION "Trimble Palisade GPS" /* Long name */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define TRMB_MINPOLL 4 /* 16 seconds */
+#define TRMB_MAXPOLL 5 /* 32 seconds */
+
+/*
+ * I/O Definitions
+ */
+#define DEVICE "/dev/palisade%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+
+/*
+ * TSIP Report Definitions
+ */
+#define LENCODE_8F0B 74 /* Length of TSIP 8F-0B Packet & header */
+#define LENCODE_NTP 22 /* Length of Palisade NTP Packet */
+
+/* Allowed Sub-Packet ID's */
+#define PACKET_8F0B 0x0B
+#define PACKET_NTP 0xAD
+
+#define DLE 0x10
+#define ETX 0x03
+
+/* parse states */
+#define TSIP_PARSED_EMPTY 0
+#define TSIP_PARSED_FULL 1
+#define TSIP_PARSED_DLE_1 2
+#define TSIP_PARSED_DATA 3
+#define TSIP_PARSED_DLE_2 4
+
+/*
+ * Leap-Insert and Leap-Delete are encoded as follows:
+ * PALISADE_UTC_TIME set and PALISADE_LEAP_PENDING set: INSERT leap
+ */
+
+#define PALISADE_LEAP_INPROGRESS 0x08 /* This is the leap flag */
+#define PALISADE_LEAP_WARNING 0x04 /* GPS Leap Warning (see ICD-200) */
+#define PALISADE_LEAP_PENDING 0x02 /* Leap Pending (24 hours) */
+#define PALISADE_UTC_TIME 0x01 /* UTC time available */
+
+#define mb(_X_) (up->rpt_buf[(_X_ + 1)]) /* shortcut for buffer access */
+
+/* Conversion Definitions */
+#define GPS_PI (3.1415926535898)
+#define R2D (180.0/GPS_PI)
+
+/*
+ * Palisade unit control structure.
+ */
+struct palisade_unit {
+ short unit; /* NTP refclock unit number */
+ int polled; /* flag to detect noreplies */
+ char leap_status; /* leap second flag */
+ char rpt_status; /* TSIP Parser State */
+ short rpt_cnt; /* TSIP packet length so far */
+ char rpt_buf[BMAX]; /* packet assembly buffer */
+ int type; /* Clock mode type */
+};
+
+/*
+ * Function prototypes
+ */
+
+static int palisade_start P((int, struct peer *));
+static void palisade_shutdown P((int, struct peer *));
+static void palisade_receive P((struct peer *));
+static void palisade_poll P((int, struct peer *));
+static void palisade_io P((struct recvbuf *));
+int palisade_configure P((int, struct peer *));
+int TSIP_decode P((struct peer *));
+long HW_poll P((struct refclockproc *));
+float getfloat P((u_char *));
+double getdbl P((u_char *));
+short getint P((u_char *));
+
+#endif /* PALISADE_H */
diff --git a/ntpd/refclock_parse.c b/ntpd/refclock_parse.c
new file mode 100644
index 0000000..52fadaa
--- /dev/null
+++ b/ntpd/refclock_parse.c
@@ -0,0 +1,5386 @@
+/*
+ * /src/NTP/ntp-4/ntpd/refclock_parse.c,v 4.36 1999/11/28 17:18:20 kardel RELEASE_19991128_A
+ *
+ * refclock_parse.c,v 4.36 1999/11/28 17:18:20 kardel RELEASE_19991128_A
+ *
+ * generic reference clock driver for receivers
+ *
+ * optionally make use of a STREAMS module for input processing where
+ * available and configured. Currently the STREAMS module
+ * is only available for Suns running SunOS 4.x and SunOS5.x
+ *
+ * the STREAMS module is not required for operation and may be omitted
+ * at the cost of reduced accuracy. As new kernel interfaces emerger this
+ * restriction may be lifted in future.
+ *
+ * Copyright (c) 1995-1999 by Frank Kardel <kardel@acm.org>
+ * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universität Erlangen-Nürnberg, Germany
+ *
+ * This software may not be sold for profit without a written consent
+ * from the author.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PARSE)
+
+/*
+ * This driver currently provides the support for
+ * - Meinberg receiver DCF77 PZF 535 (TCXO version) (DCF)
+ * - Meinberg receiver DCF77 PZF 535 (OCXO version) (DCF)
+ * - Meinberg receiver DCF77 PZF 509 (DCF)
+ * - Meinberg receiver DCF77 AM receivers (e.g. C51) (DCF)
+ * - IGEL CLOCK (DCF)
+ * - ELV DCF7000 (DCF)
+ * - Schmid clock (DCF)
+ * - Conrad DCF77 receiver module (DCF)
+ * - FAU DCF77 NTP receiver (TimeBrick) (DCF)
+ *
+ * - Meinberg GPS166/GPS167 (GPS)
+ * - Trimble (TSIP and TAIP protocol) (GPS)
+ *
+ * - RCC8000 MSF Receiver (MSF)
+ * - WHARTON 400A Series clock (DCF)
+ * - VARITEXT clock (MSF)
+ */
+
+/*
+ * Meinberg receivers are usually connected via a
+ * 9600 baud serial line
+ *
+ * The Meinberg GPS receivers also have a special NTP time stamp
+ * format. The firmware release is Uni-Erlangen.
+ *
+ * Meinberg generic receiver setup:
+ * output time code every second
+ * Baud rate 9600 7E2S
+ *
+ * Meinberg GPS16x setup:
+ * output time code every second
+ * Baudrate 19200 8N1
+ *
+ * This software supports the standard data formats used
+ * in Meinberg receivers.
+ *
+ * Special software versions are only sensible for the
+ * GPS 16x family of receivers.
+ *
+ * Meinberg can be reached via: http://www.meinberg.de/
+ */
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h" /* includes <sys/time.h> */
+#include "ntp_control.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#ifndef TM_IN_SYS_TIME
+# include <time.h>
+#endif
+
+#if !defined(STREAM) && !defined(HAVE_SYSV_TTYS) && !defined(HAVE_BSD_TTYS) && !defined(HAVE_TERMIOS)
+# include "Bletch: Define one of {STREAM,HAVE_SYSV_TTYS,HAVE_TERMIOS}"
+#endif
+
+#ifdef STREAM
+# include <sys/stream.h>
+# include <sys/stropts.h>
+#endif
+
+#ifdef HAVE_TERMIOS
+# define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_))
+# define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_))
+# undef HAVE_SYSV_TTYS
+#endif
+
+#ifdef HAVE_SYSV_TTYS
+# define TTY_GETATTR(_FD_, _ARG_) ioctl((_FD_), TCGETA, (_ARG_))
+# define TTY_SETATTR(_FD_, _ARG_) ioctl((_FD_), TCSETAW, (_ARG_))
+#endif
+
+#ifdef HAVE_BSD_TTYS
+/* #error CURRENTLY NO BSD TTY SUPPORT */
+# include "Bletch: BSD TTY not currently supported"
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#ifdef PPS
+#ifdef HAVE_SYS_PPSCLOCK_H
+#include <sys/ppsclock.h>
+#endif
+#ifdef HAVE_TIO_SERIAL_STUFF
+#include <linux/serial.h>
+#endif
+#endif
+
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+#include "parse.h"
+#include "mbg_gps166.h"
+#include "trimble.h"
+#include "binio.h"
+#include "ascii.h"
+#include "ieee754io.h"
+
+static char rcsid[]="refclock_parse.c,v 4.36 1999/11/28 17:18:20 kardel RELEASE_19991128_A";
+
+/**===========================================================================
+ ** external interface to ntp mechanism
+ **/
+
+static void parse_init P((void));
+static int parse_start P((int, struct peer *));
+static void parse_shutdown P((int, struct peer *));
+static void parse_poll P((int, struct peer *));
+static void parse_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));
+
+#define parse_buginfo noentry
+
+struct refclock refclock_parse = {
+ parse_start,
+ parse_shutdown,
+ parse_poll,
+ parse_control,
+ parse_init,
+ parse_buginfo,
+ NOFLAGS
+};
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* maximum number of "PARSE" units permitted */
+#define PARSEDEVICE "/dev/refclock-%d" /* device to open %d is unit number */
+
+#undef ABS
+#define ABS(_X_) (((_X_) < 0) ? -(_X_) : (_X_))
+
+/**===========================================================================
+ ** function vector for dynamically binding io handling mechanism
+ **/
+
+struct parseunit; /* to keep inquiring minds happy */
+
+typedef struct bind
+{
+ const char *bd_description; /* name of type of binding */
+ int (*bd_init) P((struct parseunit *)); /* initialize */
+ void (*bd_end) P((struct parseunit *)); /* end */
+ int (*bd_setcs) P((struct parseunit *, parsectl_t *)); /* set character size */
+ int (*bd_disable) P((struct parseunit *)); /* disable */
+ int (*bd_enable) P((struct parseunit *)); /* enable */
+ int (*bd_getfmt) P((struct parseunit *, parsectl_t *)); /* get format */
+ int (*bd_setfmt) P((struct parseunit *, parsectl_t *)); /* setfmt */
+ int (*bd_timecode) P((struct parseunit *, parsectl_t *)); /* get time code */
+ void (*bd_receive) P((struct recvbuf *)); /* receive operation */
+ int (*bd_io_input) P((struct recvbuf *)); /* input operation */
+} bind_t;
+
+#define PARSE_END(_X_) (*(_X_)->binding->bd_end)(_X_)
+#define PARSE_SETCS(_X_, _CS_) (*(_X_)->binding->bd_setcs)(_X_, _CS_)
+#define PARSE_ENABLE(_X_) (*(_X_)->binding->bd_enable)(_X_)
+#define PARSE_DISABLE(_X_) (*(_X_)->binding->bd_disable)(_X_)
+#define PARSE_GETFMT(_X_, _DCT_) (*(_X_)->binding->bd_getfmt)(_X_, _DCT_)
+#define PARSE_SETFMT(_X_, _DCT_) (*(_X_)->binding->bd_setfmt)(_X_, _DCT_)
+#define PARSE_GETTIMECODE(_X_, _DCT_) (*(_X_)->binding->bd_timecode)(_X_, _DCT_)
+
+/*
+ * io modes
+ */
+#define PARSE_F_PPSPPS 0x0001 /* use loopfilter PPS code (CIOGETEV) */
+#define PARSE_F_PPSONSECOND 0x0002 /* PPS pulses are on second */
+
+
+/**===========================================================================
+ ** error message regression handling
+ **
+ ** there are quite a few errors that can occur in rapid succession such as
+ ** noisy input data or no data at all. in order to reduce the amount of
+ ** syslog messages in such case, we are using a backoff algorithm. We limit
+ ** the number of error messages of a certain class to 1 per time unit. if a
+ ** configurable number of messages is displayed that way, we move on to the
+ ** next time unit / count for that class. a count of messages that have been
+ ** suppressed is held and displayed whenever a corresponding message is
+ ** displayed. the time units for a message class will also be displayed.
+ ** whenever an error condition clears we reset the error message state,
+ ** thus we would still generate much output on pathological conditions
+ ** where the system oscillates between OK and NOT OK states. coping
+ ** with that condition is currently considered too complicated.
+ **/
+
+#define ERR_ALL (unsigned)~0 /* "all" errors */
+#define ERR_BADDATA (unsigned)0 /* unusable input data/conversion errors */
+#define ERR_NODATA (unsigned)1 /* no input data */
+#define ERR_BADIO (unsigned)2 /* read/write/select errors */
+#define ERR_BADSTATUS (unsigned)3 /* unsync states */
+#define ERR_BADEVENT (unsigned)4 /* non nominal events */
+#define ERR_INTERNAL (unsigned)5 /* internal error */
+#define ERR_CNT (unsigned)(ERR_INTERNAL+1)
+
+#define ERR(_X_) if (list_err(parse, (_X_)))
+
+struct errorregression
+{
+ u_long err_count; /* number of repititions per class */
+ u_long err_delay; /* minimum delay between messages */
+};
+
+static struct errorregression
+err_baddata[] = /* error messages for bad input data */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 3, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_nodata[] = /* error messages for missing input data */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 3, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_badstatus[] = /* unsynchronized state messages */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 3, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_badio[] = /* io failures (bad reads, selects, ...) */
+{
+ { 1, 0 }, /* output first message immediately */
+ { 5, 60 }, /* output next five messages in 60 second intervals */
+ { 5, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_badevent[] = /* non nominal events */
+{
+ { 20, 0 }, /* output first message immediately */
+ { 6, 60 }, /* output next five messages in 60 second intervals */
+ { 5, 3600 }, /* output next 3 messages in hour intervals */
+ { 0, 12*3600 } /* repeat messages only every 12 hours */
+};
+
+static struct errorregression
+err_internal[] = /* really bad things - basically coding/OS errors */
+{
+ { 0, 0 }, /* output all messages immediately */
+};
+
+static struct errorregression *
+err_tbl[] =
+{
+ err_baddata,
+ err_nodata,
+ err_badio,
+ err_badstatus,
+ err_badevent,
+ err_internal
+};
+
+struct errorinfo
+{
+ u_long err_started; /* begin time (ntp) of error condition */
+ u_long err_last; /* last time (ntp) error occurred */
+ u_long err_cnt; /* number of error repititions */
+ u_long err_suppressed; /* number of suppressed messages */
+ struct errorregression *err_stage; /* current error stage */
+};
+
+/**===========================================================================
+ ** refclock instance data
+ **/
+
+struct parseunit
+{
+ /*
+ * NTP management
+ */
+ struct peer *peer; /* backlink to peer structure - refclock inactive if 0 */
+ struct refclockproc *generic; /* backlink to refclockproc structure */
+
+ /*
+ * PARSE io
+ */
+ bind_t *binding; /* io handling binding */
+
+ /*
+ * parse state
+ */
+ parse_t parseio; /* io handling structure (user level parsing) */
+
+ /*
+ * type specific parameters
+ */
+ struct parse_clockinfo *parse_type; /* link to clock description */
+
+ /*
+ * clock state handling/reporting
+ */
+ u_char flags; /* flags (leap_control) */
+ u_long lastchange; /* time (ntp) when last state change accured */
+ u_long statetime[CEVNT_MAX+1]; /* accumulated time of clock states */
+ u_long pollneeddata; /* current_time(!=0) for receive sample expected in PPS mode */
+ u_short lastformat; /* last format used */
+ u_long lastsync; /* time (ntp) when clock was last seen fully synchronized */
+ u_long lastmissed; /* time (ntp) when poll didn't get data (powerup heuristic) */
+ u_long ppsserial; /* magic cookie for ppsclock serials (avoids stale ppsclock data) */
+ parsetime_t time; /* last (parse module) data */
+ void *localdata; /* optional local, receiver-specific data */
+ unsigned long localstate; /* private local state */
+ struct errorinfo errors[ERR_CNT]; /* error state table for suppressing excessive error messages */
+ struct ctl_var *kv; /* additional pseudo variables */
+ u_long laststatistic; /* time when staticstics where output */
+};
+
+
+/**===========================================================================
+ ** Clockinfo section all parameter for specific clock types
+ ** includes NTP parameters, TTY parameters and IO handling parameters
+ **/
+
+static void poll_dpoll P((struct parseunit *));
+static void poll_poll P((struct peer *));
+static int poll_init P((struct parseunit *));
+
+typedef struct poll_info
+{
+ u_long rate; /* poll rate - once every "rate" seconds - 0 off */
+ const char *string; /* string to send for polling */
+ u_long count; /* number of characters in string */
+} poll_info_t;
+
+#define NO_CL_FLAGS 0
+#define NO_POLL 0
+#define NO_INIT 0
+#define NO_END 0
+#define NO_EVENT 0
+#define NO_DATA 0
+#define NO_MESSAGE 0
+#define NO_PPSDELAY 0
+
+#define DCF_ID "DCF" /* generic DCF */
+#define DCF_A_ID "DCFa" /* AM demodulation */
+#define DCF_P_ID "DCFp" /* psuedo random phase shift */
+#define GPS_ID "GPS" /* GPS receiver */
+
+#define NOCLOCK_ROOTDELAY 0.0
+#define NOCLOCK_BASEDELAY 0.0
+#define NOCLOCK_DESCRIPTION 0
+#define NOCLOCK_MAXUNSYNC 0
+#define NOCLOCK_CFLAG 0
+#define NOCLOCK_IFLAG 0
+#define NOCLOCK_OFLAG 0
+#define NOCLOCK_LFLAG 0
+#define NOCLOCK_ID "TILT"
+#define NOCLOCK_POLL NO_POLL
+#define NOCLOCK_INIT NO_INIT
+#define NOCLOCK_END NO_END
+#define NOCLOCK_DATA NO_DATA
+#define NOCLOCK_FORMAT ""
+#define NOCLOCK_TYPE CTL_SST_TS_UNSPEC
+#define NOCLOCK_SAMPLES 0
+#define NOCLOCK_KEEP 0
+
+#define DCF_TYPE CTL_SST_TS_LF
+#define GPS_TYPE CTL_SST_TS_UHF
+
+/*
+ * receiver specific constants
+ */
+#define MBG_SPEED (B9600)
+#define MBG_CFLAG (CS7|PARENB|CREAD|CLOCAL|HUPCL)
+#define MBG_IFLAG (IGNBRK|IGNPAR|ISTRIP)
+#define MBG_OFLAG 0
+#define MBG_LFLAG 0
+#define MBG_FLAGS PARSE_F_PPSONSECOND
+
+/*
+ * Meinberg DCF77 receivers
+ */
+#define DCFUA31_ROOTDELAY 0.0 /* 0 */
+#define DCFUA31_BASEDELAY 0.010 /* 10.7421875ms: 10 ms (+/- 3 ms) */
+#define DCFUA31_DESCRIPTION "Meinberg DCF77 C51 or compatible"
+#define DCFUA31_MAXUNSYNC 60*30 /* only trust clock for 1/2 hour */
+#define DCFUA31_SPEED MBG_SPEED
+#define DCFUA31_CFLAG MBG_CFLAG
+#define DCFUA31_IFLAG MBG_IFLAG
+#define DCFUA31_OFLAG MBG_OFLAG
+#define DCFUA31_LFLAG MBG_LFLAG
+#define DCFUA31_SAMPLES 5
+#define DCFUA31_KEEP 3
+#define DCFUA31_FORMAT "Meinberg Standard"
+
+/*
+ * Meinberg DCF PZF535/TCXO (FM/PZF) receiver
+ */
+#define DCFPZF535_ROOTDELAY 0.0
+#define DCFPZF535_BASEDELAY 0.001968 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535_DESCRIPTION "Meinberg DCF PZF 535/509 / TCXO"
+#define DCFPZF535_MAXUNSYNC 60*60*12 /* only trust clock for 12 hours
+ * @ 5e-8df/f we have accumulated
+ * at most 2.16 ms (thus we move to
+ * NTP synchronisation */
+#define DCFPZF535_SPEED MBG_SPEED
+#define DCFPZF535_CFLAG MBG_CFLAG
+#define DCFPZF535_IFLAG MBG_IFLAG
+#define DCFPZF535_OFLAG MBG_OFLAG
+#define DCFPZF535_LFLAG MBG_LFLAG
+#define DCFPZF535_SAMPLES 5
+#define DCFPZF535_KEEP 3
+#define DCFPZF535_FORMAT "Meinberg Standard"
+
+/*
+ * Meinberg DCF PZF535/OCXO receiver
+ */
+#define DCFPZF535OCXO_ROOTDELAY 0.0
+#define DCFPZF535OCXO_BASEDELAY 0.001968 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535OCXO_DESCRIPTION "Meinberg DCF PZF 535/509 / OCXO"
+#define DCFPZF535OCXO_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
+ * @ 5e-9df/f we have accumulated
+ * at most an error of 1.73 ms
+ * (thus we move to NTP synchronisation) */
+#define DCFPZF535OCXO_SPEED MBG_SPEED
+#define DCFPZF535OCXO_CFLAG MBG_CFLAG
+#define DCFPZF535OCXO_IFLAG MBG_IFLAG
+#define DCFPZF535OCXO_OFLAG MBG_OFLAG
+#define DCFPZF535OCXO_LFLAG MBG_LFLAG
+#define DCFPZF535OCXO_SAMPLES 5
+#define DCFPZF535OCXO_KEEP 3
+#define DCFPZF535OCXO_FORMAT "Meinberg Standard"
+
+/*
+ * Meinberg GPS16X receiver
+ */
+static void gps16x_message P((struct parseunit *, parsetime_t *));
+static int gps16x_poll_init P((struct parseunit *));
+
+#define GPS16X_ROOTDELAY 0.0 /* nothing here */
+#define GPS16X_BASEDELAY 0.001968 /* XXX to be fixed ! 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define GPS16X_DESCRIPTION "Meinberg GPS16x receiver"
+#define GPS16X_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
+ * @ 5e-9df/f we have accumulated
+ * at most an error of 1.73 ms
+ * (thus we move to NTP synchronisation) */
+#define GPS16X_SPEED B19200
+#define GPS16X_CFLAG (CS8|CREAD|CLOCAL|HUPCL)
+#define GPS16X_IFLAG (IGNBRK|IGNPAR)
+#define GPS16X_OFLAG MBG_OFLAG
+#define GPS16X_LFLAG MBG_LFLAG
+#define GPS16X_POLLRATE 6
+#define GPS16X_POLLCMD ""
+#define GPS16X_CMDSIZE 0
+
+static poll_info_t gps16x_pollinfo = { GPS16X_POLLRATE, GPS16X_POLLCMD, GPS16X_CMDSIZE };
+
+#define GPS16X_INIT gps16x_poll_init
+#define GPS16X_POLL 0
+#define GPS16X_END 0
+#define GPS16X_DATA ((void *)(&gps16x_pollinfo))
+#define GPS16X_MESSAGE gps16x_message
+#define GPS16X_ID GPS_ID
+#define GPS16X_FORMAT "Meinberg GPS Extended"
+#define GPS16X_SAMPLES 5
+#define GPS16X_KEEP 3
+
+/*
+ * ELV DCF7000 Wallclock-Receiver/Switching Clock (Kit)
+ *
+ * This is really not the hottest clock - but before you have nothing ...
+ */
+#define DCF7000_ROOTDELAY 0.0 /* 0 */
+#define DCF7000_BASEDELAY 0.405 /* slow blow */
+#define DCF7000_DESCRIPTION "ELV DCF7000"
+#define DCF7000_MAXUNSYNC (60*5) /* sorry - but it just was not build as a clock */
+#define DCF7000_SPEED (B9600)
+#define DCF7000_CFLAG (CS8|CREAD|PARENB|PARODD|CLOCAL|HUPCL)
+#define DCF7000_IFLAG (IGNBRK)
+#define DCF7000_OFLAG 0
+#define DCF7000_LFLAG 0
+#define DCF7000_SAMPLES 5
+#define DCF7000_KEEP 3
+#define DCF7000_FORMAT "ELV DCF7000"
+
+/*
+ * Schmid DCF Receiver Kit
+ *
+ * When the WSDCF clock is operating optimally we want the primary clock
+ * distance to come out at 300 ms. Thus, peer.distance in the WSDCF peer
+ * structure is set to 290 ms and we compute delays which are at least
+ * 10 ms long. The following are 290 ms and 10 ms expressed in u_fp format
+ */
+#define WS_POLLRATE 1 /* every second - watch interdependency with poll routine */
+#define WS_POLLCMD "\163"
+#define WS_CMDSIZE 1
+
+static poll_info_t wsdcf_pollinfo = { WS_POLLRATE, WS_POLLCMD, WS_CMDSIZE };
+
+#define WSDCF_INIT poll_init
+#define WSDCF_POLL poll_dpoll
+#define WSDCF_END 0
+#define WSDCF_DATA ((void *)(&wsdcf_pollinfo))
+#define WSDCF_ROOTDELAY 0.0 /* 0 */
+#define WSDCF_BASEDELAY 0.010 /* ~ 10ms */
+#define WSDCF_DESCRIPTION "WS/DCF Receiver"
+#define WSDCF_FORMAT "Schmid"
+#define WSDCF_MAXUNSYNC (60*60) /* assume this beast hold at 1 h better than 2 ms XXX-must verify */
+#define WSDCF_SPEED (B1200)
+#define WSDCF_CFLAG (CS8|CREAD|CLOCAL)
+#define WSDCF_IFLAG 0
+#define WSDCF_OFLAG 0
+#define WSDCF_LFLAG 0
+#define WSDCF_SAMPLES 5
+#define WSDCF_KEEP 3
+
+/*
+ * RAW DCF77 - input of DCF marks via RS232 - many variants
+ */
+#define RAWDCF_FLAGS 0
+#define RAWDCF_ROOTDELAY 0.0 /* 0 */
+#define RAWDCF_BASEDELAY 0.258
+#define RAWDCF_FORMAT "RAW DCF77 Timecode"
+#define RAWDCF_MAXUNSYNC (0) /* sorry - its a true receiver - no signal - no time */
+#define RAWDCF_SPEED (B50)
+#ifdef NO_PARENB_IGNPAR /* Was: defined(SYS_IRIX4) || defined(SYS_IRIX5) */
+/* somehow doesn't grok PARENB & IGNPAR (mj) */
+# define RAWDCF_CFLAG (CS8|CREAD|CLOCAL)
+#else
+# define RAWDCF_CFLAG (CS8|CREAD|CLOCAL|PARENB)
+#endif
+#ifdef RAWDCF_NO_IGNPAR /* Was: defined(SYS_LINUX) && defined(CLOCK_RAWDCF) */
+# define RAWDCF_IFLAG 0
+#else
+# define RAWDCF_IFLAG (IGNPAR)
+#endif
+#define RAWDCF_OFLAG 0
+#define RAWDCF_LFLAG 0
+#define RAWDCF_SAMPLES 20
+#define RAWDCF_KEEP 12
+#define RAWDCF_INIT 0
+
+/*
+ * RAW DCF variants
+ */
+/*
+ * Conrad receiver
+ *
+ * simplest (cheapest) DCF clock - e. g. DCF77 receiver by Conrad
+ * (~40DM - roughly $30 ) followed by a level converter for RS232
+ */
+#define CONRAD_BASEDELAY 0.292 /* Conrad receiver @ 50 Baud on a Sun */
+#define CONRAD_DESCRIPTION "RAW DCF77 CODE (Conrad DCF77 receiver module)"
+
+/*
+ * TimeBrick receiver
+ */
+#define TIMEBRICK_BASEDELAY 0.210 /* TimeBrick @ 50 Baud on a Sun */
+#define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)"
+
+/*
+ * IGEL:clock receiver
+ */
+#define IGELCLOCK_BASEDELAY 0.258 /* IGEL:clock receiver */
+#define IGELCLOCK_DESCRIPTION "RAW DCF77 CODE (IGEL:clock)"
+#define IGELCLOCK_SPEED (B1200)
+#define IGELCLOCK_CFLAG (CS8|CREAD|HUPCL|CLOCAL)
+
+/*
+ * RAWDCF receivers that need to be powered from DTR
+ * (like Expert mouse clock)
+ */
+static int rawdcf_init_1 P((struct parseunit *));
+#define RAWDCFDTRSET_DESCRIPTION "RAW DCF77 CODE (DTR SET/RTS CLR)"
+#define RAWDCFDTRSET_INIT rawdcf_init_1
+
+/*
+ * RAWDCF receivers that need to be powered from
+ * DTR CLR and RTS SET
+ */
+static int rawdcf_init_2 P((struct parseunit *));
+#define RAWDCFDTRCLRRTSSET_DESCRIPTION "RAW DCF77 CODE (DTR CLR/RTS SET)"
+#define RAWDCFDTRCLRRTSSET_INIT rawdcf_init_2
+
+/*
+ * Trimble GPS receivers (TAIP and TSIP protocols)
+ */
+#ifndef TRIM_POLLRATE
+#define TRIM_POLLRATE 0 /* only true direct polling */
+#endif
+
+#define TRIM_TAIPPOLLCMD ">SRM;FR_FLAG=F;EC_FLAG=F<>QTM<"
+#define TRIM_TAIPCMDSIZE (sizeof(TRIM_TAIPPOLLCMD)-1)
+
+static poll_info_t trimbletaip_pollinfo = { TRIM_POLLRATE, TRIM_TAIPPOLLCMD, TRIM_TAIPCMDSIZE };
+static int trimbletaip_init P((struct parseunit *));
+static void trimbletaip_event P((struct parseunit *, int));
+
+/* query time & UTC correction data */
+static char tsipquery[] = { DLE, 0x21, DLE, ETX, DLE, 0x2F, DLE, ETX };
+
+static poll_info_t trimbletsip_pollinfo = { TRIM_POLLRATE, tsipquery, sizeof(tsipquery) };
+static int trimbletsip_init P((struct parseunit *));
+static void trimbletsip_end P((struct parseunit *));
+static void trimbletsip_message P((struct parseunit *, parsetime_t *));
+static void trimbletsip_event P((struct parseunit *, int));
+
+#define TRIMBLETSIP_IDLE_TIME (300) /* 5 minutes silence at most */
+
+#define TRIMBLETAIP_SPEED (B4800)
+#define TRIMBLETAIP_CFLAG (CS8|CREAD|CLOCAL)
+#define TRIMBLETAIP_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)
+#define TRIMBLETAIP_OFLAG (OPOST|ONLCR)
+#define TRIMBLETAIP_LFLAG (0)
+
+#define TRIMBLETSIP_SPEED (B9600)
+#define TRIMBLETSIP_CFLAG (CS8|CLOCAL|CREAD|PARENB|PARODD)
+#define TRIMBLETSIP_IFLAG (IGNBRK)
+#define TRIMBLETSIP_OFLAG (0)
+#define TRIMBLETSIP_LFLAG (ICANON)
+
+#define TRIMBLETSIP_SAMPLES 5
+#define TRIMBLETSIP_KEEP 3
+#define TRIMBLETAIP_SAMPLES 5
+#define TRIMBLETAIP_KEEP 3
+
+#define TRIMBLETAIP_FLAGS (PARSE_F_PPSONSECOND)
+#define TRIMBLETSIP_FLAGS (TRIMBLETAIP_FLAGS)
+
+#define TRIMBLETAIP_POLL poll_dpoll
+#define TRIMBLETSIP_POLL poll_dpoll
+
+#define TRIMBLETAIP_INIT trimbletaip_init
+#define TRIMBLETSIP_INIT trimbletsip_init
+
+#define TRIMBLETAIP_EVENT trimbletaip_event
+
+#define TRIMBLETSIP_EVENT trimbletsip_event
+#define TRIMBLETSIP_MESSAGE trimbletsip_message
+
+#define TRIMBLETAIP_END 0
+#define TRIMBLETSIP_END trimbletsip_end
+
+#define TRIMBLETAIP_DATA ((void *)(&trimbletaip_pollinfo))
+#define TRIMBLETSIP_DATA ((void *)(&trimbletsip_pollinfo))
+
+#define TRIMBLETAIP_ID GPS_ID
+#define TRIMBLETSIP_ID GPS_ID
+
+#define TRIMBLETAIP_FORMAT "Trimble TAIP"
+#define TRIMBLETSIP_FORMAT "Trimble TSIP"
+
+#define TRIMBLETAIP_ROOTDELAY 0x0
+#define TRIMBLETSIP_ROOTDELAY 0x0
+
+#define TRIMBLETAIP_BASEDELAY 0.0
+#define TRIMBLETSIP_BASEDELAY 0.020 /* GPS time message latency */
+
+#define TRIMBLETAIP_DESCRIPTION "Trimble GPS (TAIP) receiver"
+#define TRIMBLETSIP_DESCRIPTION "Trimble GPS (TSIP) receiver"
+
+#define TRIMBLETAIP_MAXUNSYNC 0
+#define TRIMBLETSIP_MAXUNSYNC 0
+
+#define TRIMBLETAIP_EOL '<'
+
+/*
+ * RadioCode Clocks RCC 800 receiver
+ */
+#define RCC_POLLRATE 0 /* only true direct polling */
+#define RCC_POLLCMD "\r"
+#define RCC_CMDSIZE 1
+
+static poll_info_t rcc8000_pollinfo = { RCC_POLLRATE, RCC_POLLCMD, RCC_CMDSIZE };
+#define RCC8000_FLAGS 0
+#define RCC8000_POLL poll_dpoll
+#define RCC8000_INIT poll_init
+#define RCC8000_END 0
+#define RCC8000_DATA ((void *)(&rcc8000_pollinfo))
+#define RCC8000_ROOTDELAY 0.0
+#define RCC8000_BASEDELAY 0.0
+#define RCC8000_ID "MSF"
+#define RCC8000_DESCRIPTION "RCC 8000 MSF Receiver"
+#define RCC8000_FORMAT "Radiocode RCC8000"
+#define RCC8000_MAXUNSYNC (60*60) /* should be ok for an hour */
+#define RCC8000_SPEED (B2400)
+#define RCC8000_CFLAG (CS8|CREAD|CLOCAL)
+#define RCC8000_IFLAG (IGNBRK|IGNPAR)
+#define RCC8000_OFLAG 0
+#define RCC8000_LFLAG 0
+#define RCC8000_SAMPLES 5
+#define RCC8000_KEEP 3
+
+/*
+ * Hopf Radio clock 6021 Format
+ *
+ */
+#define HOPF6021_ROOTDELAY 0.0
+#define HOPF6021_BASEDELAY 0.0
+#define HOPF6021_DESCRIPTION "HOPF 6021"
+#define HOPF6021_FORMAT "hopf Funkuhr 6021"
+#define HOPF6021_MAXUNSYNC (60*60) /* should be ok for an hour */
+#define HOPF6021_SPEED (B9600)
+#define HOPF6021_CFLAG (CS8|CREAD|CLOCAL)
+#define HOPF6021_IFLAG (IGNBRK|ISTRIP)
+#define HOPF6021_OFLAG 0
+#define HOPF6021_LFLAG 0
+#define HOPF6021_FLAGS 0
+#define HOPF6021_SAMPLES 5
+#define HOPF6021_KEEP 3
+
+/*
+ * Diem's Computime Radio Clock Receiver
+ */
+#define COMPUTIME_FLAGS 0
+#define COMPUTIME_ROOTDELAY 0.0
+#define COMPUTIME_BASEDELAY 0.0
+#define COMPUTIME_ID DCF_ID
+#define COMPUTIME_DESCRIPTION "Diem's Computime receiver"
+#define COMPUTIME_FORMAT "Diem's Computime Radio Clock"
+#define COMPUTIME_TYPE DCF_TYPE
+#define COMPUTIME_MAXUNSYNC (60*60) /* only trust clock for 1 hour */
+#define COMPUTIME_SPEED (B9600)
+#define COMPUTIME_CFLAG (CSTOPB|CS7|CREAD|CLOCAL)
+#define COMPUTIME_IFLAG (IGNBRK|IGNPAR|ISTRIP)
+#define COMPUTIME_OFLAG 0
+#define COMPUTIME_LFLAG 0
+#define COMPUTIME_SAMPLES 5
+#define COMPUTIME_KEEP 3
+
+/*
+ * Varitext Radio Clock Receiver
+ */
+#define VARITEXT_FLAGS 0
+#define VARITEXT_ROOTDELAY 0.0
+#define VARITEXT_BASEDELAY 0.0
+#define VARITEXT_ID "MSF"
+#define VARITEXT_DESCRIPTION "Varitext receiver"
+#define VARITEXT_FORMAT "Varitext Radio Clock"
+#define VARITEXT_TYPE DCF_TYPE
+#define VARITEXT_MAXUNSYNC (60*60) /* only trust clock for 1 hour */
+#define VARITEXT_SPEED (B9600)
+#define VARITEXT_CFLAG (CS7|CREAD|CLOCAL|PARENB|PARODD)
+#define VARITEXT_IFLAG (IGNPAR|IGNBRK|INPCK) /*|ISTRIP)*/
+#define VARITEXT_OFLAG 0
+#define VARITEXT_LFLAG 0
+#define VARITEXT_SAMPLES 32
+#define VARITEXT_KEEP 20
+
+static struct parse_clockinfo
+{
+ u_long cl_flags; /* operation flags (io modes) */
+ void (*cl_poll) P((struct parseunit *)); /* active poll routine */
+ int (*cl_init) P((struct parseunit *)); /* active poll init routine */
+ void (*cl_event) P((struct parseunit *, int)); /* special event handling (e.g. reset clock) */
+ void (*cl_end) P((struct parseunit *)); /* active poll end routine */
+ void (*cl_message) P((struct parseunit *, parsetime_t *)); /* process a lower layer message */
+ void *cl_data; /* local data area for "poll" mechanism */
+ double cl_rootdelay; /* rootdelay */
+ double cl_basedelay; /* current offset by which the RS232
+ time code is delayed from the actual time */
+ const char *cl_id; /* ID code */
+ const char *cl_description; /* device name */
+ const char *cl_format; /* fixed format */
+ u_char cl_type; /* clock type (ntp control) */
+ u_long cl_maxunsync; /* time to trust oscillator after losing synch */
+ u_long cl_speed; /* terminal input & output baudrate */
+ u_long cl_cflag; /* terminal control flags */
+ u_long cl_iflag; /* terminal input flags */
+ u_long cl_oflag; /* terminal output flags */
+ u_long cl_lflag; /* terminal local flags */
+ u_long cl_samples; /* samples for median filter */
+ u_long cl_keep; /* samples for median filter to keep */
+} parse_clockinfo[] =
+{
+ { /* mode 0 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ DCFPZF535_ROOTDELAY,
+ DCFPZF535_BASEDELAY,
+ DCF_P_ID,
+ DCFPZF535_DESCRIPTION,
+ DCFPZF535_FORMAT,
+ DCF_TYPE,
+ DCFPZF535_MAXUNSYNC,
+ DCFPZF535_SPEED,
+ DCFPZF535_CFLAG,
+ DCFPZF535_IFLAG,
+ DCFPZF535_OFLAG,
+ DCFPZF535_LFLAG,
+ DCFPZF535_SAMPLES,
+ DCFPZF535_KEEP
+ },
+ { /* mode 1 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ DCFPZF535OCXO_ROOTDELAY,
+ DCFPZF535OCXO_BASEDELAY,
+ DCF_P_ID,
+ DCFPZF535OCXO_DESCRIPTION,
+ DCFPZF535OCXO_FORMAT,
+ DCF_TYPE,
+ DCFPZF535OCXO_MAXUNSYNC,
+ DCFPZF535OCXO_SPEED,
+ DCFPZF535OCXO_CFLAG,
+ DCFPZF535OCXO_IFLAG,
+ DCFPZF535OCXO_OFLAG,
+ DCFPZF535OCXO_LFLAG,
+ DCFPZF535OCXO_SAMPLES,
+ DCFPZF535OCXO_KEEP
+ },
+ { /* mode 2 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ DCFUA31_ROOTDELAY,
+ DCFUA31_BASEDELAY,
+ DCF_A_ID,
+ DCFUA31_DESCRIPTION,
+ DCFUA31_FORMAT,
+ DCF_TYPE,
+ DCFUA31_MAXUNSYNC,
+ DCFUA31_SPEED,
+ DCFUA31_CFLAG,
+ DCFUA31_IFLAG,
+ DCFUA31_OFLAG,
+ DCFUA31_LFLAG,
+ DCFUA31_SAMPLES,
+ DCFUA31_KEEP
+ },
+ { /* mode 3 */
+ MBG_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ DCF7000_ROOTDELAY,
+ DCF7000_BASEDELAY,
+ DCF_A_ID,
+ DCF7000_DESCRIPTION,
+ DCF7000_FORMAT,
+ DCF_TYPE,
+ DCF7000_MAXUNSYNC,
+ DCF7000_SPEED,
+ DCF7000_CFLAG,
+ DCF7000_IFLAG,
+ DCF7000_OFLAG,
+ DCF7000_LFLAG,
+ DCF7000_SAMPLES,
+ DCF7000_KEEP
+ },
+ { /* mode 4 */
+ NO_CL_FLAGS,
+ WSDCF_POLL,
+ WSDCF_INIT,
+ NO_EVENT,
+ WSDCF_END,
+ NO_MESSAGE,
+ WSDCF_DATA,
+ WSDCF_ROOTDELAY,
+ WSDCF_BASEDELAY,
+ DCF_A_ID,
+ WSDCF_DESCRIPTION,
+ WSDCF_FORMAT,
+ DCF_TYPE,
+ WSDCF_MAXUNSYNC,
+ WSDCF_SPEED,
+ WSDCF_CFLAG,
+ WSDCF_IFLAG,
+ WSDCF_OFLAG,
+ WSDCF_LFLAG,
+ WSDCF_SAMPLES,
+ WSDCF_KEEP
+ },
+ { /* mode 5 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCF_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ CONRAD_BASEDELAY,
+ DCF_A_ID,
+ CONRAD_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 6 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCF_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ TIMEBRICK_BASEDELAY,
+ DCF_A_ID,
+ TIMEBRICK_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 7 */
+ MBG_FLAGS,
+ GPS16X_POLL,
+ GPS16X_INIT,
+ NO_EVENT,
+ GPS16X_END,
+ GPS16X_MESSAGE,
+ GPS16X_DATA,
+ GPS16X_ROOTDELAY,
+ GPS16X_BASEDELAY,
+ GPS16X_ID,
+ GPS16X_DESCRIPTION,
+ GPS16X_FORMAT,
+ GPS_TYPE,
+ GPS16X_MAXUNSYNC,
+ GPS16X_SPEED,
+ GPS16X_CFLAG,
+ GPS16X_IFLAG,
+ GPS16X_OFLAG,
+ GPS16X_LFLAG,
+ GPS16X_SAMPLES,
+ GPS16X_KEEP
+ },
+ { /* mode 8 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ IGELCLOCK_BASEDELAY,
+ DCF_A_ID,
+ IGELCLOCK_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ IGELCLOCK_SPEED,
+ IGELCLOCK_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 9 */
+ TRIMBLETAIP_FLAGS,
+#if TRIM_POLLRATE /* DHD940515: Allow user config */
+ NO_POLL,
+#else
+ TRIMBLETAIP_POLL,
+#endif
+ TRIMBLETAIP_INIT,
+ TRIMBLETAIP_EVENT,
+ TRIMBLETAIP_END,
+ NO_MESSAGE,
+ TRIMBLETAIP_DATA,
+ TRIMBLETAIP_ROOTDELAY,
+ TRIMBLETAIP_BASEDELAY,
+ TRIMBLETAIP_ID,
+ TRIMBLETAIP_DESCRIPTION,
+ TRIMBLETAIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETAIP_MAXUNSYNC,
+ TRIMBLETAIP_SPEED,
+ TRIMBLETAIP_CFLAG,
+ TRIMBLETAIP_IFLAG,
+ TRIMBLETAIP_OFLAG,
+ TRIMBLETAIP_LFLAG,
+ TRIMBLETAIP_SAMPLES,
+ TRIMBLETAIP_KEEP
+ },
+ { /* mode 10 */
+ TRIMBLETSIP_FLAGS,
+#if TRIM_POLLRATE /* DHD940515: Allow user config */
+ NO_POLL,
+#else
+ TRIMBLETSIP_POLL,
+#endif
+ TRIMBLETSIP_INIT,
+ TRIMBLETSIP_EVENT,
+ TRIMBLETSIP_END,
+ TRIMBLETSIP_MESSAGE,
+ TRIMBLETSIP_DATA,
+ TRIMBLETSIP_ROOTDELAY,
+ TRIMBLETSIP_BASEDELAY,
+ TRIMBLETSIP_ID,
+ TRIMBLETSIP_DESCRIPTION,
+ TRIMBLETSIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETSIP_MAXUNSYNC,
+ TRIMBLETSIP_SPEED,
+ TRIMBLETSIP_CFLAG,
+ TRIMBLETSIP_IFLAG,
+ TRIMBLETSIP_OFLAG,
+ TRIMBLETSIP_LFLAG,
+ TRIMBLETSIP_SAMPLES,
+ TRIMBLETSIP_KEEP
+ },
+ { /* mode 11 */
+ NO_CL_FLAGS,
+ RCC8000_POLL,
+ RCC8000_INIT,
+ NO_EVENT,
+ RCC8000_END,
+ NO_MESSAGE,
+ RCC8000_DATA,
+ RCC8000_ROOTDELAY,
+ RCC8000_BASEDELAY,
+ RCC8000_ID,
+ RCC8000_DESCRIPTION,
+ RCC8000_FORMAT,
+ DCF_TYPE,
+ RCC8000_MAXUNSYNC,
+ RCC8000_SPEED,
+ RCC8000_CFLAG,
+ RCC8000_IFLAG,
+ RCC8000_OFLAG,
+ RCC8000_LFLAG,
+ RCC8000_SAMPLES,
+ RCC8000_KEEP
+ },
+ { /* mode 12 */
+ HOPF6021_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ HOPF6021_ROOTDELAY,
+ HOPF6021_BASEDELAY,
+ DCF_ID,
+ HOPF6021_DESCRIPTION,
+ HOPF6021_FORMAT,
+ DCF_TYPE,
+ HOPF6021_MAXUNSYNC,
+ HOPF6021_SPEED,
+ HOPF6021_CFLAG,
+ HOPF6021_IFLAG,
+ HOPF6021_OFLAG,
+ HOPF6021_LFLAG,
+ HOPF6021_SAMPLES,
+ HOPF6021_KEEP
+ },
+ { /* mode 13 */
+ COMPUTIME_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ COMPUTIME_ROOTDELAY,
+ COMPUTIME_BASEDELAY,
+ COMPUTIME_ID,
+ COMPUTIME_DESCRIPTION,
+ COMPUTIME_FORMAT,
+ COMPUTIME_TYPE,
+ COMPUTIME_MAXUNSYNC,
+ COMPUTIME_SPEED,
+ COMPUTIME_CFLAG,
+ COMPUTIME_IFLAG,
+ COMPUTIME_OFLAG,
+ COMPUTIME_LFLAG,
+ COMPUTIME_SAMPLES,
+ COMPUTIME_KEEP
+ },
+ { /* mode 14 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCFDTRSET_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFDTRSET_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 15 */
+ 0, /* operation flags (io modes) */
+ NO_POLL, /* active poll routine */
+ NO_INIT, /* active poll init routine */
+ NO_EVENT, /* special event handling (e.g. reset clock) */
+ NO_END, /* active poll end routine */
+ NO_MESSAGE, /* process a lower layer message */
+ NO_DATA, /* local data area for "poll" mechanism */
+ 0, /* rootdelay */
+ 11.0 /* bits */ / 9600, /* current offset by which the RS232
+ time code is delayed from the actual time */
+ DCF_ID, /* ID code */
+ "WHARTON 400A Series clock", /* device name */
+ "WHARTON 400A Series clock Output Format 1", /* fixed format */
+ /* Must match a format-name in a libparse/clk_xxx.c file */
+ DCF_TYPE, /* clock type (ntp control) */
+ (1*60*60), /* time to trust oscillator after losing synch */
+ B9600, /* terminal input & output baudrate */
+ (CS8|CREAD|PARENB|CLOCAL|HUPCL),/* terminal control flags */
+ 0, /* terminal input flags */
+ 0, /* terminal output flags */
+ 0, /* terminal local flags */
+ 5, /* samples for median filter */
+ 3, /* samples for median filter to keep */
+ },
+ { /* mode 16 - RAWDCF RTS set, DTR clr */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCFDTRCLRRTSSET_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFDTRCLRRTSSET_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_KEEP
+ },
+ { /* mode 17 */
+ VARITEXT_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ VARITEXT_ROOTDELAY,
+ VARITEXT_BASEDELAY,
+ VARITEXT_ID,
+ VARITEXT_DESCRIPTION,
+ VARITEXT_FORMAT,
+ VARITEXT_TYPE,
+ VARITEXT_MAXUNSYNC,
+ VARITEXT_SPEED,
+ VARITEXT_CFLAG,
+ VARITEXT_IFLAG,
+ VARITEXT_OFLAG,
+ VARITEXT_LFLAG,
+ VARITEXT_SAMPLES,
+ VARITEXT_KEEP
+ }
+};
+
+static int ncltypes = sizeof(parse_clockinfo) / sizeof(struct parse_clockinfo);
+
+#define CLK_REALTYPE(x) ((int)(((x)->ttl) & 0x7F))
+#define CLK_TYPE(x) ((CLK_REALTYPE(x) >= ncltypes) ? ~0 : CLK_REALTYPE(x))
+#define CLK_UNIT(x) ((int)REFCLOCKUNIT(&(x)->srcadr))
+#define CLK_PPS(x) (((x)->ttl) & 0x80)
+
+/*
+ * Other constant stuff
+ */
+#define PARSEHSREFID 0x7f7f08ff /* 127.127.8.255 refid for hi strata */
+
+#define PARSESTATISTICS (60*60) /* output state statistics every hour */
+
+static struct parseunit *parseunits[MAXUNITS];
+
+static int notice = 0;
+
+#define PARSE_STATETIME(parse, i) ((parse->generic->currentstatus == i) ? parse->statetime[i] + current_time - parse->lastchange : parse->statetime[i])
+
+static void parse_event P((struct parseunit *, int));
+static void parse_process P((struct parseunit *, parsetime_t *));
+static void clear_err P((struct parseunit *, u_long));
+static int list_err P((struct parseunit *, u_long));
+static char * l_mktime P((u_long));
+
+/**===========================================================================
+ ** implementation error message regression module
+ **/
+static void
+clear_err(
+ struct parseunit *parse,
+ u_long lstate
+ )
+{
+ if (lstate == ERR_ALL)
+ {
+ int i;
+
+ for (i = 0; i < ERR_CNT; i++)
+ {
+ parse->errors[i].err_stage = err_tbl[i];
+ parse->errors[i].err_cnt = 0;
+ parse->errors[i].err_last = 0;
+ parse->errors[i].err_started = 0;
+ parse->errors[i].err_suppressed = 0;
+ }
+ }
+ else
+ {
+ parse->errors[lstate].err_stage = err_tbl[lstate];
+ parse->errors[lstate].err_cnt = 0;
+ parse->errors[lstate].err_last = 0;
+ parse->errors[lstate].err_started = 0;
+ parse->errors[lstate].err_suppressed = 0;
+ }
+}
+
+static int
+list_err(
+ struct parseunit *parse,
+ u_long lstate
+ )
+{
+ int do_it;
+ struct errorinfo *err = &parse->errors[lstate];
+
+ if (err->err_started == 0)
+ {
+ err->err_started = current_time;
+ }
+
+ do_it = (current_time - err->err_last) >= err->err_stage->err_delay;
+
+ if (do_it)
+ err->err_cnt++;
+
+ if (err->err_stage->err_count &&
+ (err->err_cnt >= err->err_stage->err_count))
+ {
+ err->err_stage++;
+ err->err_cnt = 0;
+ }
+
+ if (!err->err_cnt && do_it)
+ msyslog(LOG_INFO, "PARSE receiver #%d: interval for following error message class is at least %s",
+ CLK_UNIT(parse->peer), l_mktime(err->err_stage->err_delay));
+
+ if (!do_it)
+ err->err_suppressed++;
+ else
+ err->err_last = current_time;
+
+ if (do_it && err->err_suppressed)
+ {
+ msyslog(LOG_INFO, "PARSE receiver #%d: %ld message%s suppressed, error condition class persists for %s",
+ CLK_UNIT(parse->peer), err->err_suppressed, (err->err_suppressed == 1) ? " was" : "s where",
+ l_mktime(current_time - err->err_started));
+ err->err_suppressed = 0;
+ }
+
+ return do_it;
+}
+
+/*--------------------------------------------------
+ * mkreadable - make a printable ascii string (without
+ * embedded quotes so that the ntpq protocol isn't
+ * fooled
+ */
+#ifndef isprint
+#define isprint(_X_) (((_X_) > 0x1F) && ((_X_) < 0x7F))
+#endif
+
+static char *
+mkreadable(
+ char *buffer,
+ long blen,
+ const char *src,
+ u_long srclen,
+ int hex
+ )
+{
+ char *b = buffer;
+ char *endb = (char *)0;
+
+ if (blen < 4)
+ return (char *)0; /* don't bother with mini buffers */
+
+ endb = buffer + blen - 4;
+
+ blen--; /* account for '\0' */
+
+ while (blen && srclen--)
+ {
+ if (!hex && /* no binary only */
+ (*src != '\\') && /* no plain \ */
+ (*src != '"') && /* no " */
+ isprint((int)*src)) /* only printables */
+ { /* they are easy... */
+ *buffer++ = *src++;
+ blen--;
+ }
+ else
+ {
+ if (blen < 4)
+ {
+ while (blen--)
+ {
+ *buffer++ = '.';
+ }
+ *buffer = '\0';
+ return b;
+ }
+ else
+ {
+ if (*src == '\\')
+ {
+ strcpy(buffer,"\\\\");
+ buffer += 2;
+ blen -= 2;
+ src++;
+ }
+ else
+ {
+ sprintf(buffer, "\\x%02x", *src++);
+ blen -= 4;
+ buffer += 4;
+ }
+ }
+ }
+ if (srclen && !blen && endb) /* overflow - set last chars to ... */
+ strcpy(endb, "...");
+ }
+
+ *buffer = '\0';
+ return b;
+}
+
+
+/*--------------------------------------------------
+ * mkascii - make a printable ascii string
+ * assumes (unless defined better) 7-bit ASCII
+ */
+static char *
+mkascii(
+ char *buffer,
+ long blen,
+ const char *src,
+ u_long srclen
+ )
+{
+ return mkreadable(buffer, blen, src, srclen, 0);
+}
+
+/**===========================================================================
+ ** implementation of i/o handling methods
+ ** (all STREAM, partial STREAM, user level)
+ **/
+
+/*
+ * define possible io handling methods
+ */
+#ifdef STREAM
+static int ppsclock_init P((struct parseunit *));
+static int stream_init P((struct parseunit *));
+static void stream_end P((struct parseunit *));
+static int stream_enable P((struct parseunit *));
+static int stream_disable P((struct parseunit *));
+static int stream_setcs P((struct parseunit *, parsectl_t *));
+static int stream_getfmt P((struct parseunit *, parsectl_t *));
+static int stream_setfmt P((struct parseunit *, parsectl_t *));
+static int stream_timecode P((struct parseunit *, parsectl_t *));
+static void stream_receive P((struct recvbuf *));
+#endif
+
+static int local_init P((struct parseunit *));
+static void local_end P((struct parseunit *));
+static int local_nop P((struct parseunit *));
+static int local_setcs P((struct parseunit *, parsectl_t *));
+static int local_getfmt P((struct parseunit *, parsectl_t *));
+static int local_setfmt P((struct parseunit *, parsectl_t *));
+static int local_timecode P((struct parseunit *, parsectl_t *));
+static void local_receive P((struct recvbuf *));
+static int local_input P((struct recvbuf *));
+
+static bind_t io_bindings[] =
+{
+#ifdef STREAM
+ {
+ "parse STREAM",
+ stream_init,
+ stream_end,
+ stream_setcs,
+ stream_disable,
+ stream_enable,
+ stream_getfmt,
+ stream_setfmt,
+ stream_timecode,
+ stream_receive,
+ 0,
+ },
+ {
+ "ppsclock STREAM",
+ ppsclock_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_timecode,
+ local_receive,
+ local_input,
+ },
+#endif
+ {
+ "normal",
+ local_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_timecode,
+ local_receive,
+ local_input,
+ },
+ {
+ (char *)0,
+ }
+};
+
+#ifdef STREAM
+
+#define fix_ts(_X_) \
+ if ((&(_X_))->tv.tv_usec >= 1000000) \
+ { \
+ (&(_X_))->tv.tv_usec -= 1000000; \
+ (&(_X_))->tv.tv_sec += 1; \
+ }
+
+#define cvt_ts(_X_, _Y_) \
+ { \
+ l_fp ts; \
+ fix_ts((_X_)); \
+ if (!buftvtots((const char *)&(&(_X_))->tv, &ts)) \
+ { \
+ ERR(ERR_BADDATA) \
+ msyslog(LOG_ERR,"parse: stream_receive: timestamp conversion error (buftvtots) (%s) (%ld.%06ld) ", (_Y_), (long)(&(_X_))->tv.tv_sec, (long)(&(_X_))->tv.tv_usec);\
+ return; \
+ } \
+ else \
+ { \
+ (&(_X_))->fp = ts; \
+ } \
+ }
+
+/*--------------------------------------------------
+ * ppsclock STREAM init
+ */
+static int
+ppsclock_init(
+ struct parseunit *parse
+ )
+{
+ static char m1[] = "ppsclocd";
+ static char m2[] = "ppsclock";
+
+ /*
+ * now push the parse streams module
+ * it will ensure exclusive access to the device
+ */
+ if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)m1) == -1 &&
+ ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)m2) == -1)
+ {
+ if (errno != EINVAL)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: ppsclock_init: ioctl(fd, I_PUSH, \"ppsclock\"): %m",
+ CLK_UNIT(parse->peer));
+ }
+ return 0;
+ }
+ if (!local_init(parse))
+ {
+ (void)ioctl(parse->generic->io.fd, I_POP, (caddr_t)0);
+ return 0;
+ }
+
+ parse->flags |= PARSE_PPSCLOCK;
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse STREAM init
+ */
+static int
+stream_init(
+ struct parseunit *parse
+ )
+{
+ static char m1[] = "parse";
+ /*
+ * now push the parse streams module
+ * to test whether it is there (neat interface 8-( )
+ */
+ if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)m1) == -1)
+ {
+ if (errno != EINVAL) /* accept non-existence */
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+ }
+ else
+ {
+ while(ioctl(parse->generic->io.fd, I_POP, (caddr_t)0) == 0)
+ /* empty loop */;
+
+ /*
+ * now push it a second time after we have removed all
+ * module garbage
+ */
+ if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)m1) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * parse STREAM end
+ */
+static void
+stream_end(
+ struct parseunit *parse
+ )
+{
+ while(ioctl(parse->generic->io.fd, I_POP, (caddr_t)0) == 0)
+ /* empty loop */;
+}
+
+/*--------------------------------------------------
+ * STREAM setcs
+ */
+static int
+stream_setcs(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETCS;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_setcs: ioctl(fd, I_STR, PARSEIOC_SETCS): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM enable
+ */
+static int
+stream_enable(
+ struct parseunit *parse
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_ENABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_enable: ioctl(fd, I_STR, PARSEIOC_ENABLE): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ parse->generic->io.clock_recv = stream_receive; /* ok - parse input in kernel */
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM disable
+ */
+static int
+stream_disable(
+ struct parseunit *parse
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_DISABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_disable: ioctl(fd, I_STR, PARSEIOC_DISABLE): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ parse->generic->io.clock_recv = local_receive; /* ok - parse input in daemon */
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM getfmt
+ */
+static int
+stream_getfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_GETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: ioctl(fd, I_STR, PARSEIOC_GETFMT): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM setfmt
+ */
+static int
+stream_setfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_setfmt: ioctl(fd, I_STR, PARSEIOC_SETFMT): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ return 1;
+}
+
+
+/*--------------------------------------------------
+ * STREAM timecode
+ */
+static int
+stream_timecode(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_TIMECODE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: stream_timecode: ioctl(fd, I_STR, PARSEIOC_TIMECODE): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ clear_err(parse, ERR_INTERNAL);
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM receive
+ */
+static void
+stream_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct parseunit *parse = (struct parseunit *)((void *)rbufp->recv_srcclock);
+ parsetime_t parsetime;
+
+ if (!parse->peer)
+ return;
+
+ if (rbufp->recv_length != sizeof(parsetime_t))
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR,"PARSE receiver #%d: stream_receive: bad size (got %d expected %d)",
+ CLK_UNIT(parse->peer), rbufp->recv_length, (int)sizeof(parsetime_t));
+ parse->generic->baddata++;
+ parse_event(parse, CEVNT_BADREPLY);
+ return;
+ }
+ clear_err(parse, ERR_BADIO);
+
+ memmove((caddr_t)&parsetime,
+ (caddr_t)rbufp->recv_buffer,
+ sizeof(parsetime_t));
+
+#ifdef DEBUG
+ if (debug > 3)
+ {
+ printf("PARSE receiver #%d: status %06x, state %08x, time %lx.%08lx, stime %lx.%08lx, ptime %lx.%08lx\n",
+ CLK_UNIT(parse->peer),
+ (unsigned int)parsetime.parse_status,
+ (unsigned int)parsetime.parse_state,
+ (long)parsetime.parse_time.tv.tv_sec,
+ (long)parsetime.parse_time.tv.tv_usec,
+ (long)parsetime.parse_stime.tv.tv_sec,
+ (long)parsetime.parse_stime.tv.tv_usec,
+ (long)parsetime.parse_ptime.tv.tv_sec,
+ (long)parsetime.parse_ptime.tv.tv_usec);
+ }
+#endif
+
+ /*
+ * switch time stamp world - be sure to normalize small usec field
+ * errors.
+ */
+
+ cvt_ts(parsetime.parse_stime, "parse_stime");
+
+ if (PARSE_TIMECODE(parsetime.parse_state))
+ {
+ cvt_ts(parsetime.parse_time, "parse_time");
+ }
+
+ if (PARSE_PPS(parsetime.parse_state))
+ cvt_ts(parsetime.parse_ptime, "parse_ptime");
+
+ parse_process(parse, &parsetime);
+}
+#endif
+
+/*--------------------------------------------------
+ * local init
+ */
+static int
+local_init(
+ struct parseunit *parse
+ )
+{
+ return parse_ioinit(&parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local end
+ */
+static void
+local_end(
+ struct parseunit *parse
+ )
+{
+ parse_ioend(&parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local nop
+ */
+static int
+local_nop(
+ struct parseunit *parse
+ )
+{
+ return 1;
+}
+
+/*--------------------------------------------------
+ * local setcs
+ */
+static int
+local_setcs(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_setcs(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local getfmt
+ */
+static int
+local_getfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_getfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local setfmt
+ */
+static int
+local_setfmt(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_setfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local timecode
+ */
+static int
+local_timecode(
+ struct parseunit *parse,
+ parsectl_t *tcl
+ )
+{
+ return parse_timecode(tcl, &parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local input
+ */
+static int
+local_input(
+ struct recvbuf *rbufp
+ )
+{
+ struct parseunit *parse = (struct parseunit *)((void *)rbufp->recv_srcclock);
+ int count;
+ unsigned char *s;
+ timestamp_t ts;
+
+ if (!parse->peer)
+ return 0;
+
+ /*
+ * eat all characters, parsing then and feeding complete samples
+ */
+ count = rbufp->recv_length;
+ s = (unsigned char *)rbufp->recv_buffer;
+ ts.fp = rbufp->recv_time;
+
+ while (count--)
+ {
+ if (parse_ioread(&parse->parseio, (unsigned int)(*s++), &ts))
+ {
+ struct recvbuf buf;
+
+ /*
+ * got something good to eat
+ */
+ if (!PARSE_PPS(parse->parseio.parse_dtime.parse_state))
+ {
+#ifdef TIOCDCDTIMESTAMP
+ struct timeval dcd_time;
+
+ if (ioctl(rbufp->fd, TIOCDCDTIMESTAMP, &dcd_time) != -1)
+ {
+ l_fp tstmp;
+
+ TVTOTS(&dcd_time, &tstmp);
+ tstmp.l_ui += JAN_1970;
+ L_SUB(&ts.fp, &tstmp);
+ if (ts.fp.l_ui == 0)
+ {
+#ifdef DEBUG
+ if (debug)
+ {
+ printf(
+ "parse: local_receive: fd %d DCDTIMESTAMP %s\n",
+ rbufp->fd,
+ lfptoa(&tstmp, 6));
+ printf(" sigio %s\n",
+ lfptoa(&ts.fp, 6));
+ }
+#endif
+ parse->parseio.parse_dtime.parse_ptime.fp = tstmp;
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+ }
+#else /* TIOCDCDTIMESTAMP */
+#if defined(HAVE_STRUCT_PPSCLOCKEV) && (defined(HAVE_CIOGETEV) || defined(HAVE_TIOCGPPSEV))
+ if (parse->flags & PARSE_PPSCLOCK)
+ {
+ l_fp tts;
+ struct ppsclockev ev;
+
+#ifdef HAVE_CIOGETEV
+ if (ioctl(parse->generic->io.fd, CIOGETEV, (caddr_t)&ev) == 0)
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ if (ioctl(parse->generic->io.fd, TIOCGPPSEV, (caddr_t)&ev) == 0)
+#endif
+ {
+ if (ev.serial != parse->ppsserial)
+ {
+ /*
+ * add PPS time stamp if available via ppsclock module
+ * and not supplied already.
+ */
+ if (!buftvtots((const char *)&ev.tv, &tts))
+ {
+ ERR(ERR_BADDATA)
+ msyslog(LOG_ERR,"parse: local_receive: timestamp conversion error (buftvtots) (ppsclockev.tv)");
+ }
+ else
+ {
+ parse->parseio.parse_dtime.parse_ptime.fp = tts;
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+ }
+ parse->ppsserial = ev.serial;
+ }
+ }
+#endif
+#endif /* TIOCDCDTIMESTAMP */
+ }
+ if (count)
+ { /* simulate receive */
+ memmove((caddr_t)buf.recv_buffer,
+ (caddr_t)&parse->parseio.parse_dtime,
+ sizeof(parsetime_t));
+ parse_iodone(&parse->parseio);
+ buf.recv_length = sizeof(parsetime_t);
+ buf.recv_time = rbufp->recv_time;
+ buf.srcadr = rbufp->srcadr;
+ buf.dstadr = rbufp->dstadr;
+ buf.fd = rbufp->fd;
+ buf.next = 0;
+ buf.X_from_where = rbufp->X_from_where;
+ rbufp->receiver(&buf);
+ }
+ else
+ {
+ memmove((caddr_t)rbufp->recv_buffer,
+ (caddr_t)&parse->parseio.parse_dtime,
+ sizeof(parsetime_t));
+ parse_iodone(&parse->parseio);
+ rbufp->recv_length = sizeof(parsetime_t);
+ return 1; /* got something & in place return */
+ }
+ }
+ }
+ return 0; /* nothing to pass up */
+}
+
+/*--------------------------------------------------
+ * local receive
+ */
+static void
+local_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct parseunit *parse = (struct parseunit *)((void *)rbufp->recv_srcclock);
+ parsetime_t parsetime;
+
+ if (!parse->peer)
+ return;
+
+ if (rbufp->recv_length != sizeof(parsetime_t))
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR,"PARSE receiver #%d: local_receive: bad size (got %d expected %d)",
+ CLK_UNIT(parse->peer), rbufp->recv_length, (int)sizeof(parsetime_t));
+ parse->generic->baddata++;
+ parse_event(parse, CEVNT_BADREPLY);
+ return;
+ }
+ clear_err(parse, ERR_BADIO);
+
+ memmove((caddr_t)&parsetime,
+ (caddr_t)rbufp->recv_buffer,
+ sizeof(parsetime_t));
+
+#ifdef DEBUG
+ if (debug > 3)
+ {
+ printf("PARSE receiver #%d: status %06x, state %08x, time %lx.%08lx, stime %lx.%08lx, ptime %lx.%08lx\n",
+ CLK_UNIT(parse->peer),
+ (unsigned int)parsetime.parse_status,
+ (unsigned int)parsetime.parse_state,
+ (long)parsetime.parse_time.tv.tv_sec,
+ (long)parsetime.parse_time.tv.tv_usec,
+ (long)parsetime.parse_stime.tv.tv_sec,
+ (long)parsetime.parse_stime.tv.tv_usec,
+ (long)parsetime.parse_ptime.tv.tv_sec,
+ (long)parsetime.parse_ptime.tv.tv_usec);
+ }
+#endif
+
+ parse_process(parse, &parsetime);
+}
+
+/*--------------------------------------------------
+ * init_iobinding - find and initialize lower layers
+ */
+static bind_t *
+init_iobinding(
+ struct parseunit *parse
+ )
+{
+ bind_t *b = io_bindings;
+
+ while (b->bd_description != (char *)0)
+ {
+ if ((*b->bd_init)(parse))
+ {
+ return b;
+ }
+ b++;
+ }
+ return (bind_t *)0;
+}
+
+/**===========================================================================
+ ** support routines
+ **/
+
+/*--------------------------------------------------
+ * convert a flag field to a string
+ */
+static char *
+parsestate(
+ u_long lstate,
+ char *buffer
+ )
+{
+ static struct bits
+ {
+ u_long bit;
+ const char *name;
+ } flagstrings[] =
+ {
+ { PARSEB_ANNOUNCE, "DST SWITCH WARNING" },
+ { PARSEB_POWERUP, "NOT SYNCHRONIZED" },
+ { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" },
+ { PARSEB_DST, "DST" },
+ { PARSEB_UTC, "UTC DISPLAY" },
+ { PARSEB_LEAPADD, "LEAP ADD WARNING" },
+ { PARSEB_LEAPDEL, "LEAP DELETE WARNING" },
+ { PARSEB_LEAPSECOND, "LEAP SECOND" },
+ { PARSEB_ALTERNATE, "ALTERNATE ANTENNA" },
+ { PARSEB_TIMECODE, "TIME CODE" },
+ { PARSEB_PPS, "PPS" },
+ { PARSEB_POSITION, "POSITION" },
+ { 0 }
+ };
+
+ static struct sbits
+ {
+ u_long bit;
+ const char *name;
+ } sflagstrings[] =
+ {
+ { PARSEB_S_LEAP, "LEAP INDICATION" },
+ { PARSEB_S_PPS, "PPS SIGNAL" },
+ { PARSEB_S_ANTENNA, "ANTENNA" },
+ { PARSEB_S_POSITION, "POSITION" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & lstate)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ if (lstate & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION))
+ {
+ char *s, *t;
+
+ if (buffer[0])
+ strcat(buffer, "; ");
+
+ strcat(buffer, "(");
+
+ t = s = buffer + strlen(buffer);
+
+ i = 0;
+ while (sflagstrings[i].bit)
+ {
+ if (sflagstrings[i].bit & lstate)
+ {
+ if (t != s)
+ {
+ strcpy(t, "; ");
+ t += 2;
+ }
+
+ strcpy(t, sflagstrings[i].name);
+ t += strlen(t);
+ }
+ i++;
+ }
+ strcpy(t, ")");
+ }
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a status flag field to a string
+ */
+static char *
+parsestatus(
+ u_long lstate,
+ char *buffer
+ )
+{
+ static struct bits
+ {
+ u_long bit;
+ const char *name;
+ } flagstrings[] =
+ {
+ { CVT_OK, "CONVERSION SUCCESSFUL" },
+ { CVT_NONE, "NO CONVERSION" },
+ { CVT_FAIL, "CONVERSION FAILED" },
+ { CVT_BADFMT, "ILLEGAL FORMAT" },
+ { CVT_BADDATE, "DATE ILLEGAL" },
+ { CVT_BADTIME, "TIME ILLEGAL" },
+ { CVT_ADDITIONAL, "ADDITIONAL DATA" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & lstate)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a clock status flag field to a string
+ */
+static const char *
+clockstatus(
+ u_long lstate
+ )
+{
+ static char buffer[20];
+ static struct status
+ {
+ u_long value;
+ const char *name;
+ } flagstrings[] =
+ {
+ { CEVNT_NOMINAL, "NOMINAL" },
+ { CEVNT_TIMEOUT, "NO RESPONSE" },
+ { CEVNT_BADREPLY,"BAD FORMAT" },
+ { CEVNT_FAULT, "FAULT" },
+ { CEVNT_PROP, "PROPAGATION DELAY" },
+ { CEVNT_BADDATE, "ILLEGAL DATE" },
+ { CEVNT_BADTIME, "ILLEGAL TIME" },
+ { (unsigned)~0L }
+ };
+ int i;
+
+ i = 0;
+ while (flagstrings[i].value != ~0)
+ {
+ if (flagstrings[i].value == lstate)
+ {
+ return flagstrings[i].name;
+ }
+ i++;
+ }
+
+ sprintf(buffer, "unknown #%ld", (u_long)lstate);
+
+ return buffer;
+}
+
+
+/*--------------------------------------------------
+ * l_mktime - make representation of a relative time
+ */
+static char *
+l_mktime(
+ u_long delta
+ )
+{
+ u_long tmp, m, s;
+ static char buffer[40];
+
+ buffer[0] = '\0';
+
+ if ((tmp = delta / (60*60*24)) != 0)
+ {
+ sprintf(buffer, "%ldd+", (u_long)tmp);
+ delta -= tmp * 60*60*24;
+ }
+
+ s = delta % 60;
+ delta /= 60;
+ m = delta % 60;
+ delta /= 60;
+
+ sprintf(buffer+strlen(buffer), "%02d:%02d:%02d",
+ (int)delta, (int)m, (int)s);
+
+ return buffer;
+}
+
+
+/*--------------------------------------------------
+ * parse_statistics - list summary of clock states
+ */
+static void
+parse_statistics(
+ struct parseunit *parse
+ )
+{
+ int i;
+
+ NLOG(NLOG_CLOCKSTATIST) /* conditional if clause for conditional syslog */
+ {
+ msyslog(LOG_INFO, "PARSE receiver #%d: running time: %s",
+ CLK_UNIT(parse->peer),
+ l_mktime(current_time - parse->generic->timestarted));
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: current status: %s",
+ CLK_UNIT(parse->peer),
+ clockstatus(parse->generic->currentstatus));
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ u_long s_time;
+ u_long percent, d = current_time - parse->generic->timestarted;
+
+ percent = s_time = PARSE_STATETIME(parse, i);
+
+ while (((u_long)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ d /= 10;
+ }
+
+ if (d)
+ percent = (percent * 10000) / d;
+ else
+ percent = 10000;
+
+ if (s_time)
+ msyslog(LOG_INFO, "PARSE receiver #%d: state %18s: %13s (%3ld.%02ld%%)",
+ CLK_UNIT(parse->peer),
+ clockstatus((unsigned int)i),
+ l_mktime(s_time),
+ percent / 100, percent % 100);
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * cparse_statistics - wrapper for statistics call
+ */
+static void
+cparse_statistics(
+ register struct parseunit *parse
+ )
+{
+ if (parse->laststatistic + PARSESTATISTICS < current_time)
+ parse_statistics(parse);
+ parse->laststatistic = current_time;
+}
+
+/**===========================================================================
+ ** ntp interface routines
+ **/
+
+/*--------------------------------------------------
+ * parse_init - initialize internal parse driver data
+ */
+static void
+parse_init(void)
+{
+ memset((caddr_t)parseunits, 0, sizeof parseunits);
+}
+
+
+/*--------------------------------------------------
+ * parse_shutdown - shut down a PARSE clock
+ */
+static void
+parse_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+
+ if (parse && !parse->peer)
+ {
+ msyslog(LOG_ERR,
+ "PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit not in use", unit);
+ return;
+ }
+
+ /*
+ * print statistics a last time and
+ * stop statistics machine
+ */
+ parse_statistics(parse);
+
+ if (parse->parse_type->cl_end)
+ {
+ parse->parse_type->cl_end(parse);
+ }
+
+ if (parse->binding)
+ PARSE_END(parse);
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ io_closeclock(&parse->generic->io);
+
+ free_varlist(parse->kv);
+
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" removed",
+ CLK_UNIT(parse->peer), parse->parse_type->cl_description);
+
+ parse->peer = (struct peer *)0; /* unused now */
+ free(parse);
+}
+
+/*--------------------------------------------------
+ * parse_start - open the PARSE devices and initialize data for processing
+ */
+static int
+parse_start(
+ int sysunit,
+ struct peer *peer
+ )
+{
+ u_int unit;
+ int fd232;
+#ifdef HAVE_TERMIOS
+ struct termios tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+ struct parseunit * parse;
+ char parsedev[sizeof(PARSEDEVICE)+20];
+ parsectl_t tmp_ctl;
+ u_int type;
+
+ type = CLK_TYPE(peer);
+ unit = CLK_UNIT(peer);
+
+ if ((type == ~0) || (parse_clockinfo[type].cl_description == (char *)0))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: unsupported clock type %d (max %d)",
+ unit, CLK_REALTYPE(peer), ncltypes-1);
+ return 0;
+ }
+
+ /*
+ * Unit okay, attempt to open the device.
+ */
+ (void) sprintf(parsedev, PARSEDEVICE, unit);
+
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+ fd232 = open(parsedev, O_RDWR | O_NOCTTY
+#ifdef O_NONBLOCK
+ | O_NONBLOCK
+#endif
+ , 0777);
+
+ if (fd232 == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: open of %s failed: %m", unit, parsedev);
+ return 0;
+ }
+
+ parse = (struct parseunit *)emalloc(sizeof(struct parseunit));
+
+ memset((char *)parse, 0, sizeof(struct parseunit));
+
+ parse->generic = peer->procptr; /* link up */
+ parse->generic->unitptr = (caddr_t)parse; /* link down */
+
+ /*
+ * Set up the structures
+ */
+ parse->generic->timestarted = current_time;
+ parse->lastchange = current_time;
+
+ parse->generic->currentstatus = CEVNT_TIMEOUT; /* expect the worst */
+
+ parse->flags = 0;
+ parse->pollneeddata = 0;
+ parse->laststatistic = current_time;
+ parse->lastformat = (unsigned short)~0; /* assume no format known */
+ parse->time.parse_status = (unsigned short)~0; /* be sure to mark initial status change */
+ parse->lastmissed = 0; /* assume got everything */
+ parse->ppsserial = 0;
+ parse->localdata = (void *)0;
+ parse->localstate = 0;
+ parse->kv = (struct ctl_var *)0;
+
+ clear_err(parse, ERR_ALL);
+
+ parse->parse_type = &parse_clockinfo[type];
+
+ parse->generic->fudgetime1 = parse->parse_type->cl_basedelay;
+
+ parse->generic->fudgetime2 = 0.0;
+
+ parse->generic->clockdesc = parse->parse_type->cl_description;
+
+ peer->rootdelay = parse->parse_type->cl_rootdelay;
+ peer->sstclktype = parse->parse_type->cl_type;
+ peer->precision = sys_precision;
+
+ peer->stratum = STRATUM_REFCLOCK;
+ if (peer->stratum <= 1)
+ memmove((char *)&parse->generic->refid, parse->parse_type->cl_id, 4);
+ else
+ parse->generic->refid = htonl(PARSEHSREFID);
+
+ parse->generic->io.fd = fd232;
+
+ parse->peer = peer; /* marks it also as busy */
+
+ /*
+ * configure terminal line
+ */
+ if (TTY_GETATTR(fd232, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcgetattr(%d, &tio): %m", unit, fd232);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ else
+ {
+#ifndef _PC_VDISABLE
+ memset((char *)tio.c_cc, 0, sizeof(tio.c_cc));
+#else
+ int disablec;
+ errno = 0; /* pathconf can deliver -1 without changing errno ! */
+
+ disablec = fpathconf(parse->generic->io.fd, _PC_VDISABLE);
+ if (disablec == -1 && errno)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: fpathconf(fd, _PC_VDISABLE): %m", CLK_UNIT(parse->peer));
+ memset((char *)tio.c_cc, 0, sizeof(tio.c_cc)); /* best guess */
+ }
+ else
+ if (disablec != -1)
+ memset((char *)tio.c_cc, disablec, sizeof(tio.c_cc));
+#endif
+
+#if defined (VMIN) || defined(VTIME)
+ if ((parse_clockinfo[type].cl_lflag & ICANON) == 0)
+ {
+#ifdef VMIN
+ tio.c_cc[VMIN] = 1;
+#endif
+#ifdef VTIME
+ tio.c_cc[VTIME] = 0;
+#endif
+ }
+#endif
+
+ tio.c_cflag = parse_clockinfo[type].cl_cflag;
+ tio.c_iflag = parse_clockinfo[type].cl_iflag;
+ tio.c_oflag = parse_clockinfo[type].cl_oflag;
+ tio.c_lflag = parse_clockinfo[type].cl_lflag;
+
+
+#ifdef HAVE_TERMIOS
+ if ((cfsetospeed(&tio, parse_clockinfo[type].cl_speed) == -1) ||
+ (cfsetispeed(&tio, parse_clockinfo[type].cl_speed) == -1))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcset{i,o}speed(&tio, speed): %m", unit);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+#else
+ tio.c_cflag |= parse_clockinfo[type].cl_speed;
+#endif
+
+#if defined(HAVE_TIO_SERIAL_STUFF) /* Linux hack: define PPS interface */
+ {
+ struct serial_struct ss;
+ if (ioctl(fd232, TIOCGSERIAL, &ss) < 0 ||
+ (
+#ifdef ASYNC_LOW_LATENCY
+ ss.flags |= ASYNC_LOW_LATENCY,
+#endif
+#ifdef ASYNC_PPS_CD_NEG
+ ss.flags |= ASYNC_PPS_CD_NEG,
+#endif
+ ioctl(fd232, TIOCSSERIAL, &ss)) < 0) {
+ msyslog(LOG_NOTICE, "refclock_parse: TIOCSSERIAL fd %d, %m", fd232);
+ msyslog(LOG_NOTICE,
+ "refclock_parse: optional PPS processing not available");
+ } else {
+ parse->flags |= PARSE_PPSCLOCK;
+ msyslog(LOG_INFO,
+ "refclock_parse: PPS detection on");
+ }
+ }
+#endif
+#ifdef HAVE_TIOCSPPS /* SUN PPS support */
+ if (CLK_PPS(parse->peer))
+ {
+ int i = 1;
+
+ if (ioctl(fd232, TIOCSPPS, (caddr_t)&i) == 0)
+ {
+ parse->flags |= PARSE_PPSCLOCK;
+ }
+ }
+#endif
+
+ if (TTY_SETATTR(fd232, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcsetattr(%d, &tio): %m", unit, fd232);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ }
+
+ /*
+ * Insert in async io device list.
+ */
+ parse->generic->io.srcclock = (caddr_t)parse;
+ parse->generic->io.datalen = 0;
+
+ if (!io_addclock(&parse->generic->io))
+ {
+ msyslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: addclock %s fails (ABORT - clock type requires async io)", CLK_UNIT(parse->peer), parsedev);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+
+ parse->binding = init_iobinding(parse);
+ parse->generic->io.clock_recv = parse->binding->bd_receive; /* pick correct receive routine */
+ parse->generic->io.io_input = parse->binding->bd_io_input; /* pick correct input routine */
+
+ if (parse->binding == (bind_t *)0)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: io sub system initialisation failed.", CLK_UNIT(parse->peer));
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ /*
+ * as we always(?) get 8 bit chars we want to be
+ * sure, that the upper bits are zero for less
+ * than 8 bit I/O - so we pass that information on.
+ * note that there can be only one bit count format
+ * per file descriptor
+ */
+
+ switch (tio.c_cflag & CSIZE)
+ {
+ case CS5:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS5;
+ break;
+
+ case CS6:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS6;
+ break;
+
+ case CS7:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS7;
+ break;
+
+ case CS8:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS8;
+ break;
+ }
+
+ if (!PARSE_SETCS(parse, &tmp_ctl))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setcs() FAILED.", unit);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ strcpy(tmp_ctl.parseformat.parse_buffer, parse->parse_type->cl_format);
+ tmp_ctl.parseformat.parse_count = strlen(tmp_ctl.parseformat.parse_buffer);
+
+ if (!PARSE_SETFMT(parse, &tmp_ctl))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setfmt() FAILED.", unit);
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ /*
+ * get rid of all IO accumulated so far
+ */
+#ifdef HAVE_TERMIOS
+ (void) tcflush(parse->generic->io.fd, TCIOFLUSH);
+#else
+#ifdef TCFLSH
+ {
+#ifndef TCIOFLUSH
+#define TCIOFLUSH 2
+#endif
+ int flshcmd = TCIOFLUSH;
+
+ (void) ioctl(parse->generic->io.fd, TCFLSH, (caddr_t)&flshcmd);
+ }
+#endif
+#endif
+
+ /*
+ * try to do any special initializations
+ */
+ if (parse->parse_type->cl_init)
+ {
+ if (parse->parse_type->cl_init(parse))
+ {
+ parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+ }
+
+ /*
+ * get out Copyright information once
+ */
+ if (!notice)
+ {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "NTP PARSE support: Copyright (c) 1989-1999, Frank Kardel");
+ notice = 1;
+ }
+
+ /*
+ * print out configuration
+ */
+ NLOG(NLOG_CLOCKINFO)
+ {
+ /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" (device %s) added",
+ CLK_UNIT(parse->peer),
+ parse->parse_type->cl_description, parsedev);
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: Stratum %d, %sPPS support, trust time %s, precision %d",
+ CLK_UNIT(parse->peer),
+ parse->peer->stratum, CLK_PPS(parse->peer) ? "" : "no ",
+ l_mktime(parse->parse_type->cl_maxunsync), parse->peer->precision);
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: rootdelay %.6f s, phaseadjust %.6f s, %s IO handling",
+ CLK_UNIT(parse->peer),
+ parse->parse_type->cl_rootdelay,
+ parse->generic->fudgetime1,
+ parse->binding->bd_description);
+
+ msyslog(LOG_INFO, "PARSE receiver #%d: Format recognition: %s", CLK_UNIT(parse->peer),
+ parse->parse_type->cl_format);
+#ifdef PPS
+ msyslog(LOG_INFO, "PARSE receiver #%d: %sPPS ioctl support", CLK_UNIT(parse->peer),
+ (parse->flags & PARSE_PPSCLOCK) ? "" : "NO ");
+#endif
+ }
+
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse_poll - called by the transmit procedure
+ */
+static void
+parse_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+
+ if (peer != parse->peer)
+ {
+ msyslog(LOG_ERR,
+ "PARSE receiver #%d: poll: INTERNAL: peer incorrect",
+ unit);
+ return;
+ }
+
+ /*
+ * Update clock stat counters
+ */
+ parse->generic->polls++;
+
+ if (parse->pollneeddata &&
+ ((current_time - parse->pollneeddata) > (1<<(max(min(parse->peer->hpoll, parse->peer->ppoll), parse->peer->minpoll)))))
+ {
+ /*
+ * start worrying when exceeding a poll inteval
+ * bad news - didn't get a response last time
+ */
+ parse->generic->noreply++;
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+
+ ERR(ERR_NODATA)
+ msyslog(LOG_WARNING, "PARSE receiver #%d: no data from device within poll interval (check receiver / cableling)", CLK_UNIT(parse->peer));
+ }
+
+ /*
+ * we just mark that we want the next sample for the clock filter
+ */
+ parse->pollneeddata = current_time;
+
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+
+ cparse_statistics(parse);
+
+ return;
+}
+
+#define LEN_STATES 300 /* length of state string */
+
+/*--------------------------------------------------
+ * parse_control - set fudge factors, return statistics
+ */
+static void
+parse_control(
+ int unit,
+ struct refclockstat *in,
+ struct refclockstat *out,
+ struct peer *peer
+ )
+{
+ register struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+ parsectl_t tmpctl;
+
+ static char outstatus[400]; /* status output buffer */
+
+ if (out)
+ {
+ out->lencode = 0;
+ out->p_lastcode = 0;
+ out->kv_list = (struct ctl_var *)0;
+ }
+
+ if (!parse || !parse->peer)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (UNIT INACTIVE)",
+ unit);
+ return;
+ }
+
+ unit = CLK_UNIT(parse->peer);
+
+ if (in)
+ {
+ if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))
+ {
+ parse->flags = in->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4);
+ }
+ }
+
+ if (out)
+ {
+ u_long sum = 0;
+ char *t, *tt, *start;
+ int i;
+
+ outstatus[0] = '\0';
+
+ out->type = REFCLK_PARSE;
+ out->haveflags |= CLK_HAVETIME2;
+
+ /*
+ * figure out skew between PPS and RS232 - just for informational
+ * purposes - returned in time2 value
+ */
+ if (PARSE_SYNC(parse->time.parse_state))
+ {
+ if (PARSE_PPS(parse->time.parse_state) && PARSE_TIMECODE(parse->time.parse_state))
+ {
+ l_fp off;
+
+ /*
+ * we have a PPS and RS232 signal - calculate the skew
+ * WARNING: assumes on TIMECODE == PULSE (timecode after pulse)
+ */
+ off = parse->time.parse_stime.fp;
+ L_SUB(&off, &parse->time.parse_ptime.fp); /* true offset */
+ tt = add_var(&out->kv_list, 80, RO);
+ sprintf(tt, "refclock_ppsskew=%s", lfptoms(&off, 6));
+ }
+ }
+
+ if (PARSE_PPS(parse->time.parse_state))
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ sprintf(tt, "refclock_ppstime=\"%s\"", gmprettydate(&parse->time.parse_ptime.fp));
+ }
+
+ tt = add_var(&out->kv_list, 128, RO|DEF);
+ sprintf(tt, "refclock_time=\"");
+ tt += strlen(tt);
+
+ if (parse->time.parse_time.fp.l_ui == 0)
+ {
+ strcpy(tt, "<UNDEFINED>\"");
+ }
+ else
+ {
+ sprintf(tt, "%s\"", gmprettydate(&parse->time.parse_time.fp));
+ t = tt + strlen(tt);
+ }
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit);
+ }
+ else
+ {
+ tt = add_var(&out->kv_list, 512, RO|DEF);
+ sprintf(tt, "refclock_status=\"");
+ tt += strlen(tt);
+
+ /*
+ * copy PPS flags from last read transaction (informational only)
+ */
+ tmpctl.parsegettc.parse_state |= parse->time.parse_state &
+ (PARSEB_PPS|PARSEB_S_PPS);
+
+ (void) parsestate(tmpctl.parsegettc.parse_state, tt);
+
+ strcat(tt, "\"");
+
+ if (tmpctl.parsegettc.parse_count)
+ mkascii(outstatus+strlen(outstatus), (int)(sizeof(outstatus)- strlen(outstatus) - 1),
+ tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count - 1));
+
+ parse->generic->badformat += tmpctl.parsegettc.parse_badformat;
+ }
+
+ tmpctl.parseformat.parse_format = tmpctl.parsegettc.parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_getfmt() FAILED", unit);
+ }
+ else
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ sprintf(tt, "refclock_format=\"");
+
+ strncat(tt, tmpctl.parseformat.parse_buffer, tmpctl.parseformat.parse_count);
+ strcat(tt,"\"");
+ }
+
+ /*
+ * gather state statistics
+ */
+
+ start = tt = add_var(&out->kv_list, LEN_STATES, RO|DEF);
+ strcpy(tt, "refclock_states=\"");
+ tt += strlen(tt);
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ u_long s_time;
+ u_long d = current_time - parse->generic->timestarted;
+ u_long percent;
+
+ percent = s_time = PARSE_STATETIME(parse, i);
+
+ while (((u_long)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ d /= 10;
+ }
+
+ if (d)
+ percent = (percent * 10000) / d;
+ else
+ percent = 10000;
+
+ if (s_time)
+ {
+ char item[80];
+ int count;
+
+ sprintf(item, "%s%s%s: %s (%d.%02d%%)",
+ sum ? "; " : "",
+ (parse->generic->currentstatus == i) ? "*" : "",
+ clockstatus((unsigned int)i),
+ l_mktime(s_time),
+ (int)(percent / 100), (int)(percent % 100));
+ if ((count = strlen(item)) < (LEN_STATES - 40 - (tt - start)))
+ {
+ strcpy(tt, item);
+ tt += count;
+ }
+ sum += s_time;
+ }
+ }
+
+ sprintf(tt, "; running time: %s\"", l_mktime(sum));
+
+ tt = add_var(&out->kv_list, 32, RO);
+ sprintf(tt, "refclock_id=\"%s\"", parse->parse_type->cl_id);
+
+ tt = add_var(&out->kv_list, 80, RO);
+ sprintf(tt, "refclock_iomode=\"%s\"", parse->binding->bd_description);
+
+ tt = add_var(&out->kv_list, 128, RO);
+ sprintf(tt, "refclock_driver_version=\"%s\"", rcsid);
+
+ {
+ struct ctl_var *k;
+
+ k = parse->kv;
+ while (k && !(k->flags & EOV))
+ {
+ set_var(&out->kv_list, k->text, strlen(k->text)+1, k->flags);
+ k++;
+ }
+ }
+
+ out->lencode = strlen(outstatus);
+ out->p_lastcode = outstatus;
+ }
+}
+
+/**===========================================================================
+ ** processing routines
+ **/
+
+/*--------------------------------------------------
+ * event handling - note that nominal events will also be posted
+ */
+static void
+parse_event(
+ struct parseunit *parse,
+ int event
+ )
+{
+ if (parse->generic->currentstatus != (u_char) event)
+ {
+ parse->statetime[parse->generic->currentstatus] += current_time - parse->lastchange;
+ parse->lastchange = current_time;
+
+ parse->generic->currentstatus = (u_char)event;
+
+ if (parse->parse_type->cl_event)
+ parse->parse_type->cl_event(parse, event);
+
+ if (event != CEVNT_NOMINAL)
+ {
+ parse->generic->lastevent = parse->generic->currentstatus;
+ }
+ else
+ {
+ NLOG(NLOG_CLOCKSTATUS)
+ msyslog(LOG_INFO, "PARSE receiver #%d: SYNCHRONIZED",
+ CLK_UNIT(parse->peer));
+ }
+
+ if (event == CEVNT_FAULT)
+ {
+ NLOG(NLOG_CLOCKEVENT) /* conditional if clause for conditional syslog */
+ ERR(ERR_BADEVENT)
+ msyslog(LOG_ERR,
+ "clock %s fault '%s' (0x%02x)", refnumtoa(&parse->peer->srcadr), ceventstr(event),
+ (u_int)event);
+ }
+ else
+ {
+ NLOG(NLOG_CLOCKEVENT) /* conditional if clause for conditional syslog */
+ if (event == CEVNT_NOMINAL || list_err(parse, ERR_BADEVENT))
+ msyslog(LOG_INFO,
+ "clock %s event '%s' (0x%02x)", refnumtoa(&parse->peer->srcadr), ceventstr(event),
+ (u_int)event);
+ }
+
+ report_event(EVNT_PEERCLOCK, parse->peer);
+ report_event(EVNT_CLOCKEXCPT, parse->peer);
+ }
+}
+
+/*--------------------------------------------------
+ * process a PARSE time sample
+ */
+static void
+parse_process(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{
+ l_fp off, rectime, reftime;
+ double fudge;
+
+ /*
+ * check for changes in conversion status
+ * (only one for each new status !)
+ */
+ if (((parsetime->parse_status & CVT_MASK) != CVT_OK) &&
+ ((parsetime->parse_status & CVT_MASK) != CVT_NONE) &&
+ (parse->time.parse_status != parsetime->parse_status))
+ {
+ char buffer[400];
+
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_WARNING, "PARSE receiver #%d: conversion status \"%s\"",
+ CLK_UNIT(parse->peer), parsestatus(parsetime->parse_status, buffer));
+
+ if ((parsetime->parse_status & CVT_MASK) == CVT_FAIL)
+ {
+ /*
+ * tell more about the story - list time code
+ * there is a slight change for a race condition and
+ * the time code might be overwritten by the next packet
+ */
+ parsectl_t tmpctl;
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_process: parse_timecode() FAILED", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ ERR(ERR_BADDATA)
+ msyslog(LOG_WARNING, "PARSE receiver #%d: FAILED TIMECODE: \"%s\" (check receiver configuration / cableling)",
+ CLK_UNIT(parse->peer), mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count - 1)));
+ parse->generic->badformat += tmpctl.parsegettc.parse_badformat;
+ }
+ }
+ }
+
+ /*
+ * examine status and post appropriate events
+ */
+ if ((parsetime->parse_status & CVT_MASK) != CVT_OK)
+ {
+ /*
+ * got bad data - tell the rest of the system
+ */
+ switch (parsetime->parse_status & CVT_MASK)
+ {
+ case CVT_NONE:
+ if ((parsetime->parse_status & CVT_ADDITIONAL) &&
+ parse->parse_type->cl_message)
+ parse->parse_type->cl_message(parse, parsetime);
+ break; /* well, still waiting - timeout is handled at higher levels */
+
+ case CVT_FAIL:
+ parse->generic->badformat++;
+ if (parsetime->parse_status & CVT_BADFMT)
+ {
+ parse_event(parse, CEVNT_BADREPLY);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADDATE)
+ {
+ parse_event(parse, CEVNT_BADDATE);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADTIME)
+ {
+ parse_event(parse, CEVNT_BADTIME);
+ }
+ else
+ {
+ parse_event(parse, CEVNT_BADREPLY); /* for the lack of something better */
+ }
+ }
+ return; /* skip the rest - useless */
+ }
+
+ /*
+ * check for format changes
+ * (in case somebody has swapped clocks 8-)
+ */
+ if (parse->lastformat != parsetime->parse_format)
+ {
+ parsectl_t tmpctl;
+
+ tmpctl.parseformat.parse_format = parsetime->parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ ERR(ERR_INTERNAL)
+ msyslog(LOG_ERR, "PARSE receiver #%d: parse_getfmt() FAILED", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "PARSE receiver #%d: packet format \"%s\"",
+ CLK_UNIT(parse->peer), tmpctl.parseformat.parse_buffer);
+ }
+ parse->lastformat = parsetime->parse_format;
+ }
+
+ /*
+ * now, any changes ?
+ */
+ if (parse->time.parse_state != parsetime->parse_state)
+ {
+ char tmp1[200];
+ char tmp2[200];
+ /*
+ * something happend
+ */
+
+ (void) parsestate(parsetime->parse_state, tmp1);
+ (void) parsestate(parse->time.parse_state, tmp2);
+
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO,"PARSE receiver #%d: STATE CHANGE: %s -> %s",
+ CLK_UNIT(parse->peer), tmp2, tmp1);
+ }
+
+ /*
+ * remember for future
+ */
+ parse->time = *parsetime;
+
+ /*
+ * check to see, whether the clock did a complete powerup or lost PZF signal
+ * and post correct events for current condition
+ */
+ if (PARSE_POWERUP(parsetime->parse_state))
+ {
+ /*
+ * this is bad, as we have completely lost synchronisation
+ * well this is a problem with the receiver here
+ * for PARSE Meinberg DCF77 receivers the lost synchronisation
+ * is true as it is the powerup state and the time is taken
+ * from a crude real time clock chip
+ * for the PZF series this is only partly true, as
+ * PARSE_POWERUP only means that the pseudo random
+ * phase shift sequence cannot be found. this is only
+ * bad, if we have never seen the clock in the SYNC
+ * state, where the PHASE and EPOCH are correct.
+ * for reporting events the above business does not
+ * really matter, but we can use the time code
+ * even in the POWERUP state after having seen
+ * the clock in the synchronized state (PZF class
+ * receivers) unless we have had a telegram disruption
+ * after having seen the clock in the SYNC state. we
+ * thus require having seen the clock in SYNC state
+ * *after* having missed telegrams (noresponse) from
+ * the clock. one problem remains: we might use erroneously
+ * POWERUP data if the disruption is shorter than 1 polling
+ * interval. fortunately powerdowns last usually longer than 64
+ * seconds and the receiver is at least 2 minutes in the
+ * POWERUP or NOSYNC state before switching to SYNC
+ */
+ parse_event(parse, CEVNT_FAULT);
+ NLOG(NLOG_CLOCKSTATUS)
+ ERR(ERR_BADSTATUS)
+ msyslog(LOG_ERR,"PARSE receiver #%d: NOT SYNCHRONIZED",
+ CLK_UNIT(parse->peer));
+ }
+ else
+ {
+ /*
+ * we have two states left
+ *
+ * SYNC:
+ * this state means that the EPOCH (timecode) and PHASE
+ * information has be read correctly (at least two
+ * successive PARSE timecodes were received correctly)
+ * this is the best possible state - full trust
+ *
+ * NOSYNC:
+ * The clock should be on phase with respect to the second
+ * signal, but the timecode has not been received correctly within
+ * at least the last two minutes. this is a sort of half baked state
+ * for PARSE Meinberg DCF77 clocks this is bad news (clock running
+ * without timecode confirmation)
+ * PZF 535 has also no time confirmation, but the phase should be
+ * very precise as the PZF signal can be decoded
+ */
+
+ if (PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * currently completely synchronized - best possible state
+ */
+ parse->lastsync = current_time;
+ clear_err(parse, ERR_BADSTATUS);
+ }
+ else
+ {
+ /*
+ * we have had some problems receiving the time code
+ */
+ parse_event(parse, CEVNT_PROP);
+ NLOG(NLOG_CLOCKSTATUS)
+ ERR(ERR_BADSTATUS)
+ msyslog(LOG_ERR,"PARSE receiver #%d: TIMECODE NOT CONFIRMED",
+ CLK_UNIT(parse->peer));
+ }
+ }
+
+ fudge = parse->generic->fudgetime1; /* standard RS232 Fudgefactor */
+
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ rectime = parsetime->parse_stime.fp;
+ off = reftime = parsetime->parse_time.fp;
+
+ L_SUB(&off, &rectime); /* prepare for PPS adjustments logic */
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: Reftime %s, Recvtime %s - initial offset %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime),
+ lfptoa(&off,6));
+#endif
+ }
+
+ if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer))
+ {
+ l_fp offset;
+
+ /*
+ * we have a PPS signal - much better than the RS232 stuff (we hope)
+ */
+ offset = parsetime->parse_ptime.fp;
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: PPStime %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&offset));
+#endif
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ if (M_ISGEQ(off.l_i, off.l_f, -1, 0x80000000) &&
+ M_ISGEQ(0, 0x7fffffff, off.l_i, off.l_f))
+ {
+ fudge = parse->generic->fudgetime2; /* pick PPS fudge factor */
+
+ /*
+ * RS232 offsets within [-0.5..0.5[ - take PPS offsets
+ */
+
+ if (parse->parse_type->cl_flags & PARSE_F_PPSONSECOND)
+ {
+ reftime = off = offset;
+ if (reftime.l_uf & (unsigned)0x80000000)
+ reftime.l_ui++;
+ reftime.l_uf = 0;
+
+
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
+ }
+ else
+ {
+ /*
+ * time code describes pulse
+ */
+ reftime = off = parsetime->parse_time.fp;
+
+ L_SUB(&off, &offset); /* true offset */
+ }
+ }
+ /*
+ * take RS232 offset when PPS when out of bounds
+ */
+ }
+ else
+ {
+ fudge = parse->generic->fudgetime2; /* pick PPS fudge factor */
+ /*
+ * Well, no time code to guide us - assume on second pulse
+ * and pray, that we are within [-0.5..0.5[
+ */
+ off = offset;
+ reftime = offset;
+ if (reftime.l_uf & (unsigned)0x80000000)
+ reftime.l_ui++;
+ reftime.l_uf = 0;
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
+ }
+ }
+ else
+ {
+ if (!PARSE_TIMECODE(parsetime->parse_state))
+ {
+ /*
+ * Well, no PPS, no TIMECODE, no more work ...
+ */
+ if ((parsetime->parse_status & CVT_ADDITIONAL) &&
+ parse->parse_type->cl_message)
+ parse->parse_type->cl_message(parse, parsetime);
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: Reftime %s, Recvtime %s - final offset %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime),
+ lfptoa(&off,6));
+#endif
+
+
+ rectime = reftime;
+ L_SUB(&rectime, &off); /* just to keep the ntp interface happy */
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("PARSE receiver #%d: calculated Reftime %s, Recvtime %s\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime));
+#endif
+
+ if ((parsetime->parse_status & CVT_ADDITIONAL) &&
+ parse->parse_type->cl_message)
+ parse->parse_type->cl_message(parse, parsetime);
+
+ if (PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * log OK status
+ */
+ parse_event(parse, CEVNT_NOMINAL);
+ }
+
+ clear_err(parse, ERR_BADIO);
+ clear_err(parse, ERR_BADDATA);
+ clear_err(parse, ERR_NODATA);
+ clear_err(parse, ERR_INTERNAL);
+
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ printf("PARSE receiver #%d: refclock_process_offset(reftime=%s, rectime=%s, Fudge=%f)\n",
+ CLK_UNIT(parse->peer),
+ prettydate(&reftime),
+ prettydate(&rectime),
+ fudge);
+ }
+#endif
+
+ refclock_process_offset(parse->generic, reftime, rectime, fudge);
+ if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer))
+ {
+ (void) pps_sample(&parse->time.parse_ptime.fp);
+ }
+
+
+ /*
+ * and now stick it into the clock machine
+ * samples are only valid iff lastsync is not too old and
+ * we have seen the clock in sync at least once
+ * after the last time we didn't see an expected data telegram
+ * see the clock states section above for more reasoning
+ */
+ if (((current_time - parse->lastsync) > parse->parse_type->cl_maxunsync) ||
+ (parse->lastsync <= parse->lastmissed))
+ {
+ parse->generic->leap = LEAP_NOTINSYNC;
+ }
+ else
+ {
+ if (PARSE_LEAPADD(parsetime->parse_state))
+ {
+ /*
+ * we pick this state also for time code that pass leap warnings
+ * without direction information (as earth is currently slowing
+ * down).
+ */
+ parse->generic->leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND;
+ }
+ else
+ if (PARSE_LEAPDEL(parsetime->parse_state))
+ {
+ parse->generic->leap = LEAP_DELSECOND;
+ }
+ else
+ {
+ parse->generic->leap = LEAP_NOWARNING;
+ }
+ }
+
+ /*
+ * ready, unless the machine wants a sample
+ */
+ if (!parse->pollneeddata)
+ return;
+
+ parse->pollneeddata = 0;
+
+ refclock_receive(parse->peer);
+}
+
+/**===========================================================================
+ ** special code for special clocks
+ **/
+
+static void
+mk_utcinfo(
+ char *t,
+ int wnt,
+ int wnlsf,
+ int dn,
+ int dtls,
+ int dtlsf
+ )
+{
+ l_fp leapdate;
+
+ sprintf(t, "current correction %d sec", dtls);
+ t += strlen(t);
+
+ if (wnlsf < 990)
+ wnlsf += 1024;
+
+ if (wnt < 990)
+ wnt += 1024;
+
+ gpstolfp((unsigned short)wnlsf, (unsigned short)dn, 0, &leapdate);
+
+ if ((dtlsf != dtls) &&
+ ((wnlsf - wnt) < 52))
+ {
+ sprintf(t, ", next correction %d sec on %s, new GPS-UTC offset %d",
+ dtlsf - dtls, gmprettydate(&leapdate), dtlsf);
+ }
+ else
+ {
+ sprintf(t, ", last correction on %s",
+ gmprettydate(&leapdate));
+ }
+}
+
+#ifdef CLOCK_MEINBERG
+/**===========================================================================
+ ** Meinberg GPS166/GPS167 support
+ **/
+
+/*------------------------------------------------------------
+ * gps16x_message - process GPS16x messages
+ */
+static void
+gps16x_message(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{
+ if (parse->time.parse_msglen && parsetime->parse_msg[0] == SOH)
+ {
+ GPS_MSG_HDR header;
+ unsigned char *bufp = (unsigned char *)parsetime->parse_msg + 1;
+
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ char msgbuffer[600];
+
+ mkreadable(msgbuffer, sizeof(msgbuffer), (char *)parsetime->parse_msg, parsetime->parse_msglen, 1);
+ printf("PARSE receiver #%d: received message (%d bytes) >%s<\n",
+ CLK_UNIT(parse->peer),
+ parsetime->parse_msglen,
+ msgbuffer);
+ }
+#endif
+ get_mbg_header(&bufp, &header);
+ if (header.gps_hdr_csum == mbg_csum(parsetime->parse_msg + 1, 6) &&
+ (header.gps_len == 0 ||
+ (header.gps_len < sizeof(parsetime->parse_msg) &&
+ header.gps_data_csum == mbg_csum(bufp, header.gps_len))))
+ {
+ /*
+ * clean message
+ */
+ switch (header.gps_cmd)
+ {
+ case GPS_SW_REV:
+ {
+ char buffer[64];
+ SW_REV gps_sw_rev;
+
+ get_mbg_sw_rev(&bufp, &gps_sw_rev);
+ sprintf(buffer, "meinberg_gps_version=\"%x.%02x%s%s\"",
+ (gps_sw_rev.code >> 8) & 0xFF,
+ gps_sw_rev.code & 0xFF,
+ gps_sw_rev.name[0] ? " " : "",
+ gps_sw_rev.name);
+ set_var(&parse->kv, buffer, 64, RO|DEF);
+ }
+ break;
+
+ case GPS_STAT:
+ {
+ static struct state
+ {
+ unsigned short flag; /* status flag */
+ unsigned const char *string; /* bit name */
+ } states[] =
+ {
+ { TM_ANT_DISCONN, (const unsigned char *)"ANTENNA FAULTY" },
+ { TM_SYN_FLAG, (const unsigned char *)"NO SYNC SIGNAL" },
+ { TM_NO_SYNC, (const unsigned char *)"NO SYNC POWERUP" },
+ { TM_NO_POS, (const unsigned char *)"NO POSITION" },
+ { 0, (const unsigned char *)"" }
+ };
+ unsigned short status;
+ struct state *s = states;
+ char buffer[512];
+ char *p, *b;
+
+ status = get_lsb_short(&bufp);
+ sprintf(buffer, "meinberg_gps_status=\"[0x%04x] ", status);
+
+ if (status)
+ {
+ p = b = buffer + strlen(buffer);
+ while (s->flag)
+ {
+ if (status & s->flag)
+ {
+ if (p != b)
+ {
+ *p++ = ',';
+ *p++ = ' ';
+ }
+
+ strcat(p, (const char *)s->string);
+ }
+ s++;
+ }
+
+ *p++ = '"';
+ *p = '\0';
+ }
+ else
+ {
+ strcat(buffer, "<OK>\"");
+ }
+
+ set_var(&parse->kv, buffer, 64, RO|DEF);
+ }
+ break;
+
+ case GPS_POS_XYZ:
+ {
+ XYZ xyz;
+ char buffer[256];
+
+ get_mbg_xyz(&bufp, xyz);
+ sprintf(buffer, "gps_position(XYZ)=\"%s m, %s m, %s m\"",
+ mfptoa(xyz[XP].l_ui, xyz[XP].l_uf, 1),
+ mfptoa(xyz[YP].l_ui, xyz[YP].l_uf, 1),
+ mfptoa(xyz[ZP].l_ui, xyz[ZP].l_uf, 1));
+
+ set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF);
+ }
+ break;
+
+ case GPS_POS_LLA:
+ {
+ LLA lla;
+ char buffer[256];
+
+ get_mbg_lla(&bufp, lla);
+
+ sprintf(buffer, "gps_position(LLA)=\"%s deg, %s deg, %s m\"",
+ mfptoa(lla[LAT].l_ui, lla[LAT].l_uf, 4),
+ mfptoa(lla[LON].l_ui, lla[LON].l_uf, 4),
+ mfptoa(lla[ALT].l_ui, lla[ALT].l_uf, 1));
+
+ set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF);
+ }
+ break;
+
+ case GPS_TZDL:
+ break;
+
+ case GPS_PORT_PARM:
+ break;
+
+ case GPS_SYNTH:
+ break;
+
+ case GPS_ANT_INFO:
+ {
+ ANT_INFO antinfo;
+ u_char buffer[512];
+ u_char *p;
+
+ get_mbg_antinfo(&bufp, &antinfo);
+ sprintf(buffer, "meinberg_antenna_status=\"");
+ p = buffer + strlen(buffer);
+
+ switch (antinfo.status)
+ {
+ case ANT_INVALID:
+ strcat(p, "<OK>");
+ p += strlen(p);
+ break;
+
+ case ANT_DISCONN:
+ strcat(p, "DISCONNECTED since ");
+ NLOG(NLOG_CLOCKSTATUS)
+ ERR(ERR_BADSTATUS)
+ msyslog(LOG_ERR,"PARSE receiver #%d: ANTENNA FAILURE: %s",
+ CLK_UNIT(parse->peer), p);
+
+ p += strlen(p);
+ mbg_tm_str(&p, &antinfo.tm_disconn);
+ *p = '\0';
+ break;
+
+ case ANT_RECONN:
+ strcat(p, "RECONNECTED on ");
+ p += strlen(p);
+ mbg_tm_str(&p, &antinfo.tm_reconn);
+ sprintf(p, ", reconnect clockoffset %c%ld.%07ld s, disconnect time ",
+ (antinfo.delta_t < 0) ? '-' : '+',
+ ABS(antinfo.delta_t) / 10000,
+ ABS(antinfo.delta_t) % 10000);
+ p += strlen(p);
+ mbg_tm_str(&p, &antinfo.tm_disconn);
+ *p = '\0';
+ break;
+
+ default:
+ sprintf(p, "bad status 0x%04x", antinfo.status);
+ p += strlen(p);
+ break;
+ }
+
+ *p++ = '"';
+ *p = '\0';
+
+ set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF);
+ }
+ break;
+
+ case GPS_UCAP:
+ break;
+
+ case GPS_CFGH:
+ {
+ CFGH cfgh;
+ u_char buffer[512];
+ u_char *p;
+
+ get_mbg_cfgh(&bufp, &cfgh);
+ if (cfgh.valid)
+ {
+ int i;
+
+ p = buffer;
+ strcpy(buffer, "gps_tot_51=\"");
+ p += strlen(p);
+ mbg_tgps_str(&p, &cfgh.tot_51);
+ *p++ = '"';
+ *p = '\0';
+ set_var(&parse->kv, buffer, sizeof(buffer), RO);
+
+ p = buffer;
+ strcpy(buffer, "gps_tot_63=\"");
+ p += strlen(p);
+ mbg_tgps_str(&p, &cfgh.tot_63);
+ *p++ = '"';
+ *p = '\0';
+ set_var(&parse->kv, buffer, sizeof(buffer), RO);
+
+ p = buffer;
+ strcpy(buffer, "gps_t0a=\"");
+ p += strlen(p);
+ mbg_tgps_str(&p, &cfgh.t0a);
+ *p++ = '"';
+ *p = '\0';
+ set_var(&parse->kv, buffer, sizeof(buffer), RO);
+
+ for (i = MIN_SVNO; i <= MAX_SVNO; i++)
+ {
+ p = buffer;
+ sprintf(p, "gps_cfg[%d]=\"[0x%x] ", i, cfgh.cfg[i]);
+ p += strlen(p);
+ switch (cfgh.cfg[i] & 0x7)
+ {
+ case 0:
+ strcpy(p, "BLOCK I");
+ break;
+ case 1:
+ strcpy(p, "BLOCK II");
+ break;
+ default:
+ sprintf(p, "bad CFG");
+ break;
+ }
+ strcat(p, "\"");
+ set_var(&parse->kv, buffer, sizeof(buffer), RO);
+
+ p = buffer;
+ sprintf(p, "gps_health[%d]=\"[0x%x] ", i, cfgh.health[i]);
+ p += strlen(p);
+ switch ((cfgh.health[i] >> 5) & 0x7 )
+ {
+ case 0:
+ strcpy(p, "OK;");
+ break;
+ case 1:
+ strcpy(p, "PARITY;");
+ break;
+ case 2:
+ strcpy(p, "TLM/HOW;");
+ break;
+ case 3:
+ strcpy(p, "Z-COUNT;");
+ break;
+ case 4:
+ strcpy(p, "SUBFRAME 1,2,3;");
+ break;
+ case 5:
+ strcpy(p, "SUBFRAME 4,5;");
+ break;
+ case 6:
+ strcpy(p, "UPLOAD BAD;");
+ break;
+ case 7:
+ strcpy(p, "DATA BAD;");
+ break;
+ }
+
+ p += strlen(p);
+
+ switch (cfgh.health[i] & 0x1F)
+ {
+ case 0:
+ strcpy(p, "SIGNAL OK");
+ break;
+ case 0x1C:
+ strcpy(p, "SV TEMP OUT");
+ break;
+ case 0x1D:
+ strcpy(p, "SV WILL BE TEMP OUT");
+ break;
+ case 0x1E:
+ break;
+ case 0x1F:
+ strcpy(p, "MULTIPLE ERRS");
+ break;
+ default:
+ strcpy(p, "TRANSMISSION PROBLEMS");
+ break;
+ }
+
+ strcat(p, "\"");
+ set_var(&parse->kv, buffer, sizeof(buffer), RO);
+ }
+ }
+ }
+ break;
+
+ case GPS_ALM:
+ break;
+
+ case GPS_EPH:
+ break;
+
+ case GPS_UTC:
+ {
+ UTC utc;
+ char buffer[512];
+ char *p;
+
+ p = buffer;
+
+ get_mbg_utc(&bufp, &utc);
+
+ if (utc.valid)
+ {
+ strcpy(p, "gps_utc_correction=\"");
+ p += strlen(p);
+ mk_utcinfo(p, utc.t0t.wn, utc.WNlsf, utc.DNt, utc.delta_tls, utc.delta_tlsf);
+ strcat(p, "\"");
+ }
+ else
+ {
+ strcpy(p, "gps_utc_correction=\"<NO UTC DATA>\"");
+ }
+ set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF);
+ }
+ break;
+
+ case GPS_IONO:
+ break;
+
+ case GPS_ASCII_MSG:
+ {
+ ASCII_MSG gps_ascii_msg;
+ char buffer[128];
+
+ get_mbg_ascii_msg(&bufp, &gps_ascii_msg);
+
+ if (gps_ascii_msg.valid)
+ {
+ char buffer1[128];
+ mkreadable(buffer1, sizeof(buffer1), gps_ascii_msg.s, strlen(gps_ascii_msg.s), (int)0);
+
+ sprintf(buffer, "gps_message=\"%s\"", buffer1);
+ }
+ else
+ strcpy(buffer, "gps_message=<NONE>");
+
+ set_var(&parse->kv, buffer, 128, RO|DEF);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ msyslog(LOG_DEBUG, "PARSE receiver #%d: gps16x_message: message checksum error: hdr_csum = 0x%x (expected 0x%lx), data_len = %d, data_csum = 0x%x (expected 0x%lx)",
+ CLK_UNIT(parse->peer),
+ header.gps_hdr_csum, mbg_csum(parsetime->parse_msg + 1, 6),
+ header.gps_len,
+ header.gps_data_csum, mbg_csum(bufp, (unsigned)((header.gps_len < sizeof(parsetime->parse_msg)) ? header.gps_len : 0)));
+ }
+ }
+
+ return;
+}
+
+/*------------------------------------------------------------
+ * gps16x_poll - query the reciver peridically
+ */
+static void
+gps16x_poll(
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+
+ static GPS_MSG_HDR sequence[] =
+ {
+ { GPS_SW_REV, 0, 0, 0 },
+ { GPS_STAT, 0, 0, 0 },
+ { GPS_UTC, 0, 0, 0 },
+ { GPS_ASCII_MSG, 0, 0, 0 },
+ { GPS_ANT_INFO, 0, 0, 0 },
+ { GPS_CFGH, 0, 0, 0 },
+ { GPS_POS_XYZ, 0, 0, 0 },
+ { GPS_POS_LLA, 0, 0, 0 },
+ { (unsigned short)~0, 0, 0, 0 }
+ };
+
+ int rtc;
+ unsigned char cmd_buffer[64];
+ unsigned char *outp = cmd_buffer;
+ GPS_MSG_HDR *header;
+
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->nextaction = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate;
+ }
+
+ if (sequence[parse->localstate].gps_cmd == (unsigned short)~0)
+ parse->localstate = 0;
+
+ header = sequence + parse->localstate++;
+
+ *outp++ = SOH; /* start command */
+
+ put_mbg_header(&outp, header);
+ outp = cmd_buffer + 1;
+
+ header->gps_hdr_csum = (short)mbg_csum(outp, 6);
+ put_mbg_header(&outp, header);
+
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ char buffer[128];
+
+ mkreadable(buffer, sizeof(buffer), (char *)cmd_buffer, (unsigned)(outp - cmd_buffer), 1);
+ printf("PARSE receiver #%d: transmitted message #%ld (%d bytes) >%s<\n",
+ CLK_UNIT(parse->peer),
+ parse->localstate - 1,
+ (int)(outp - cmd_buffer),
+ buffer);
+ }
+#endif
+
+ rtc = write(parse->generic->io.fd, cmd_buffer, (unsigned long)(outp - cmd_buffer));
+
+ if (rtc < 0)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: gps16x_poll: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ if (rtc != outp - cmd_buffer)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: gps16x_poll: failed to send cmd incomplete (%d of %d bytes sent)", CLK_UNIT(parse->peer), rtc, (int)(outp - cmd_buffer));
+ }
+
+ clear_err(parse, ERR_BADIO);
+ return;
+}
+
+/*--------------------------------------------------
+ * init routine - setup timer
+ */
+static int
+gps16x_poll_init(
+ struct parseunit *parse
+ )
+{
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->action = gps16x_poll;
+ gps16x_poll(parse->peer);
+ }
+
+ return 0;
+}
+
+#else
+static void
+gps16x_message(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{}
+static int
+gps16x_poll_init(
+ struct parseunit *parse
+ )
+{
+ return 1;
+}
+#endif /* CLOCK_MEINBERG */
+
+/**===========================================================================
+ ** clock polling support
+ **/
+
+/*--------------------------------------------------
+ * direct poll routine
+ */
+static void
+poll_dpoll(
+ struct parseunit *parse
+ )
+{
+ int rtc;
+ const char *ps = ((poll_info_t *)parse->parse_type->cl_data)->string;
+ int ct = ((poll_info_t *)parse->parse_type->cl_data)->count;
+
+ rtc = write(parse->generic->io.fd, ps, (unsigned long)ct);
+ if (rtc < 0)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ if (rtc != ct)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd incomplete (%d of %d bytes sent)", CLK_UNIT(parse->peer), rtc, ct);
+ }
+ clear_err(parse, ERR_BADIO);
+}
+
+/*--------------------------------------------------
+ * periodic poll routine
+ */
+static void
+poll_poll(
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+
+ if (parse->parse_type->cl_poll)
+ parse->parse_type->cl_poll(parse);
+
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->nextaction = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate;
+ }
+}
+
+/*--------------------------------------------------
+ * init routine - setup timer
+ */
+static int
+poll_init(
+ struct parseunit *parse
+ )
+{
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->peer->action = poll_poll;
+ poll_poll(parse->peer);
+ }
+
+ return 0;
+}
+
+/**===========================================================================
+ ** Trimble support
+ **/
+
+/*-------------------------------------------------------------
+ * trimble TAIP init routine - setup EOL and then do poll_init.
+ */
+static int
+trimbletaip_init(
+ struct parseunit *parse
+ )
+{
+#ifdef HAVE_TERMIOS
+ struct termios tio;
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tio;
+#endif
+ /*
+ * configure terminal line for trimble receiver
+ */
+ if (TTY_GETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcgetattr(fd, &tio): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ else
+ {
+ tio.c_cc[VEOL] = TRIMBLETAIP_EOL;
+
+ if (TTY_SETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcsetattr(fd, &tio): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+ }
+ return poll_init(parse);
+}
+
+/*--------------------------------------------------
+ * trimble TAIP event routine - reset receiver upon data format trouble
+ */
+static const char *taipinit[] = {
+ ">FPV00000000<",
+ ">SRM;ID_FLAG=F;CS_FLAG=T;EC_FLAG=F;FR_FLAG=T;CR_FLAG=F<",
+ ">FTM00020001<",
+ (char *)0
+};
+
+static void
+trimbletaip_event(
+ struct parseunit *parse,
+ int event
+ )
+{
+ switch (event)
+ {
+ case CEVNT_BADREPLY: /* reset on garbled input */
+ case CEVNT_TIMEOUT: /* reset on no input */
+ {
+ const char **iv;
+
+ iv = taipinit;
+ while (*iv)
+ {
+ int rtc = write(parse->generic->io.fd, *iv, strlen(*iv));
+ if (rtc < 0)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ return;
+ }
+ else
+ {
+ if (rtc != strlen(*iv))
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: failed to send cmd incomplete (%d of %d bytes sent)",
+ CLK_UNIT(parse->peer), rtc, (int)strlen(*iv));
+ return;
+ }
+ }
+ iv++;
+ }
+
+ NLOG(NLOG_CLOCKINFO)
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: RECEIVER INITIALIZED",
+ CLK_UNIT(parse->peer));
+ }
+ break;
+
+ default: /* ignore */
+ break;
+ }
+}
+
+/*
+ * This driver supports the Trimble SVee Six Plus GPS receiver module.
+ * It should support other Trimble receivers which use the Trimble Standard
+ * Interface Protocol (see below).
+ *
+ * The module has a serial I/O port for command/data and a 1 pulse-per-second
+ * output, about 1 microsecond wide. The leading edge of the pulse is
+ * coincident with the change of the GPS second. This is the same as
+ * the change of the UTC second +/- ~1 microsecond. Some other clocks
+ * specifically use a feature in the data message as a timing reference, but
+ * the SVee Six Plus does not do this. In fact there is considerable jitter
+ * on the timing of the messages, so this driver only supports the use
+ * of the PPS pulse for accurate timing. Where it is determined that
+ * the offset is way off, when first starting up ntpd for example,
+ * the timing of the data stream is used until the offset becomes low enough
+ * (|offset| < CLOCK_MAX), at which point the pps offset is used.
+ *
+ * It can use either option for receiving PPS information - the 'ppsclock'
+ * stream pushed onto the serial data interface to timestamp the Carrier
+ * Detect interrupts, where the 1PPS connects to the CD line. This only
+ * works on SunOS 4.1.x currently. To select this, define PPSPPS in
+ * Config.local. The other option is to use a pulse-stretcher/level-converter
+ * to convert the PPS pulse into a RS232 start pulse & feed this into another
+ * tty port. To use this option, define PPSCLK in Config.local. The pps input,
+ * by whichever method, is handled in ntp_loopfilter.c
+ *
+ * The receiver uses a serial message protocol called Trimble Standard
+ * Interface Protocol (it can support others but this driver only supports
+ * TSIP). Messages in this protocol have the following form:
+ *
+ * <DLE><id> ... <data> ... <DLE><ETX>
+ *
+ * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
+ * on transmission and compressed back to one on reception. Otherwise
+ * the values of data bytes can be anything. The serial interface is RS-422
+ * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
+ * in total!), and 1 stop bit. The protocol supports byte, integer, single,
+ * and double datatypes. Integers are two bytes, sent most significant first.
+ * Singles are IEEE754 single precision floating point numbers (4 byte) sent
+ * sign & exponent first. Doubles are IEEE754 double precision floating point
+ * numbers (8 byte) sent sign & exponent first.
+ * The receiver supports a large set of messages, only a small subset of
+ * which are used here. From driver to receiver the following are used:
+ *
+ * ID Description
+ *
+ * 21 Request current time
+ * 22 Mode Select
+ * 2C Set/Request operating parameters
+ * 2F Request UTC info
+ * 35 Set/Request I/O options
+
+ * From receiver to driver the following are recognised:
+ *
+ * ID Description
+ *
+ * 41 GPS Time
+ * 44 Satellite selection, PDOP, mode
+ * 46 Receiver health
+ * 4B Machine code/status
+ * 4C Report operating parameters (debug only)
+ * 4F UTC correction data (used to get leap second warnings)
+ * 55 I/O options (debug only)
+ *
+ * All others are accepted but ignored.
+ *
+ */
+
+#define PI 3.1415926535898 /* lots of sig figs */
+#define D2R PI/180.0
+
+/*-------------------------------------------------------------------
+ * sendcmd, sendbyte, sendetx, sendflt, sendint implement the command
+ * interface to the receiver.
+ *
+ * CAVEAT: the sendflt, sendint routines are byte order dependend and
+ * float implementation dependend - these must be converted to portable
+ * versions !
+ *
+ * CURRENT LIMITATION: float implementation. This runs only on systems
+ * with IEEE754 floats as native floats
+ */
+
+typedef struct trimble
+{
+ u_long last_msg; /* last message received */
+ u_char qtracking; /* query tracking status */
+ u_long ctrack; /* current tracking set */
+ u_long ltrack; /* last tracking set */
+} trimble_t;
+
+union uval {
+ u_char bd[8];
+ int iv;
+ float fv;
+ double dv;
+};
+
+struct txbuf
+{
+ short idx; /* index to first unused byte */
+ u_char *txt; /* pointer to actual data buffer */
+};
+
+void sendcmd P((struct txbuf *buf, int c));
+void sendbyte P((struct txbuf *buf, int b));
+void sendetx P((struct txbuf *buf, struct parseunit *parse));
+void sendint P((struct txbuf *buf, int a));
+void sendflt P((struct txbuf *buf, double a));
+
+void
+sendcmd(
+ struct txbuf *buf,
+ int c
+ )
+{
+ buf->txt[0] = DLE;
+ buf->txt[1] = (u_char)c;
+ buf->idx = 2;
+}
+
+void
+sendbyte(
+ struct txbuf *buf,
+ int b
+ )
+{
+ if (b == DLE)
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = (u_char)b;
+}
+
+void
+sendetx(
+ struct txbuf *buf,
+ struct parseunit *parse
+ )
+{
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = ETX;
+
+ if (write(parse->generic->io.fd, buf->txt, (unsigned long)buf->idx) != buf->idx)
+ {
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: sendetx: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
+ }
+ else
+ {
+#ifdef DEBUG
+ if (debug > 2)
+ {
+ char buffer[256];
+
+ mkreadable(buffer, sizeof(buffer), (char *)buf->txt, (unsigned)buf->idx, 1);
+ printf("PARSE receiver #%d: transmitted message (%d bytes) >%s<\n",
+ CLK_UNIT(parse->peer),
+ buf->idx, buffer);
+ }
+#endif
+ clear_err(parse, ERR_BADIO);
+ }
+}
+
+void
+sendint(
+ struct txbuf *buf,
+ int a
+ )
+{
+ /* send 16bit int, msbyte first */
+ sendbyte(buf, (u_char)((a>>8) & 0xff));
+ sendbyte(buf, (u_char)(a & 0xff));
+}
+
+void
+sendflt(
+ struct txbuf *buf,
+ double a
+ )
+{
+ int i;
+ union uval uval;
+
+ uval.fv = a;
+#ifdef WORDS_BIGENDIAN
+ for (i=0; i<=3; i++)
+#else
+ for (i=3; i>=0; i--)
+#endif
+ sendbyte(buf, uval.bd[i]);
+}
+
+#define TRIM_POS_OPT 0x13 /* output position with high precision */
+#define TRIM_TIME_OPT 0x03 /* use UTC time stamps, on second */
+
+/*--------------------------------------------------
+ * trimble TSIP setup routine
+ */
+static int
+trimbletsip_setup(
+ struct parseunit *parse,
+ const char *reason
+ )
+{
+ u_char buffer[256];
+ struct txbuf buf;
+
+ buf.txt = buffer;
+
+ sendcmd(&buf, CMD_CVERSION); /* request software versions */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_COPERPARAM); /* set operating parameters */
+ sendbyte(&buf, 4); /* static */
+ sendflt(&buf, 5.0*D2R); /* elevation angle mask = 10 deg XXX */
+ sendflt(&buf, 4.0); /* s/n ratio mask = 6 XXX */
+ sendflt(&buf, 12.0); /* PDOP mask = 12 */
+ sendflt(&buf, 8.0); /* PDOP switch level = 8 */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CMODESEL); /* fix mode select */
+ sendbyte(&buf, 0); /* automatic */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CMESSAGE); /* request system message */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CSUPER); /* superpacket fix */
+ sendbyte(&buf, 0x2); /* binary mode */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CIOOPTIONS); /* set I/O options */
+ sendbyte(&buf, TRIM_POS_OPT); /* position output */
+ sendbyte(&buf, 0x00); /* no velocity output */
+ sendbyte(&buf, TRIM_TIME_OPT); /* UTC, compute on seconds */
+ sendbyte(&buf, 0x00); /* no raw measurements */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, CMD_CUTCPARAM); /* request UTC correction data */
+ sendetx(&buf, parse);
+
+ NLOG(NLOG_CLOCKINFO)
+ ERR(ERR_BADIO)
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletsip_setup: RECEIVER RE-INITIALIZED (%s)", CLK_UNIT(parse->peer), reason);
+
+ return 0;
+}
+
+/*--------------------------------------------------
+ * TRIMBLE TSIP check routine
+ */
+static void
+trimble_check(
+ struct peer *peer
+ )
+{
+ struct parseunit *parse = (struct parseunit *)peer->procptr->unitptr;
+ trimble_t *t = parse->localdata;
+ u_char buffer[256];
+ struct txbuf buf;
+ buf.txt = buffer;
+
+ if (t)
+ {
+ if (current_time > t->last_msg + TRIMBLETSIP_IDLE_TIME)
+ (void)trimbletsip_setup(parse, "message timeout");
+ }
+ poll_poll(parse->peer); /* emit query string and re-arm timer */
+
+ if (t->qtracking)
+ {
+ u_long oldsats = t->ltrack & ~t->ctrack;
+
+ t->qtracking = 0;
+ t->ltrack = t->ctrack;
+
+ if (oldsats)
+ {
+ int i;
+
+ for (i = 0; oldsats; i++)
+ if (oldsats & (1 << i))
+ {
+ sendcmd(&buf, CMD_CSTATTRACK);
+ sendbyte(&buf, i+1); /* old sat */
+ sendetx(&buf, parse);
+ }
+ oldsats &= ~(1 << i);
+ }
+
+ sendcmd(&buf, CMD_CSTATTRACK);
+ sendbyte(&buf, 0x00); /* current tracking set */
+ sendetx(&buf, parse);
+ }
+}
+
+/*--------------------------------------------------
+ * TRIMBLE TSIP end routine
+ */
+static void
+trimbletsip_end(
+ struct parseunit *parse
+ )
+{ trimble_t *t = parse->localdata;
+
+ if (t)
+ {
+ free(t);
+ parse->localdata = (void *)0;
+ }
+ parse->peer->nextaction = 0;
+ parse->peer->action = (void (*) P((struct peer *)))0;
+}
+
+/*--------------------------------------------------
+ * TRIMBLE TSIP init routine
+ */
+static int
+trimbletsip_init(
+ struct parseunit *parse
+ )
+{
+#if defined(VEOL) || defined(VEOL2)
+#ifdef HAVE_TERMIOS
+ struct termios tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tio; /* NEEDED FOR A LONG TIME ! */
+#endif
+ /*
+ * allocate local data area
+ */
+ if (!parse->localdata)
+ {
+ trimble_t *t;
+
+ t = (trimble_t *)(parse->localdata = emalloc(sizeof(trimble_t)));
+
+ if (t)
+ {
+ memset((char *)t, 0, sizeof(trimble_t));
+ t->last_msg = current_time;
+ }
+ }
+
+ parse->peer->action = trimble_check;
+ parse->peer->nextaction = current_time;
+
+ /*
+ * configure terminal line for ICANON mode with VEOL characters
+ */
+ if (TTY_GETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletsip_init: tcgetattr(%d, &tio): %m", CLK_UNIT(parse->peer), parse->generic->io.fd);
+ return 0;
+ }
+ else
+ {
+ if ((parse_clockinfo[CLK_TYPE(parse->peer)].cl_lflag & ICANON))
+ {
+#ifdef VEOL
+ tio.c_cc[VEOL] = ETX;
+#endif
+#ifdef VEOL2
+ tio.c_cc[VEOL2] = DLE;
+#endif
+ }
+
+ if (TTY_SETATTR(parse->generic->io.fd, &tio) == -1)
+ {
+ msyslog(LOG_ERR, "PARSE receiver #%d: trimbletsip_init: tcsetattr(%d, &tio): %m", CLK_UNIT(parse->peer), parse->generic->io.fd);
+ return 0;
+ }
+ }
+#endif
+ return trimbletsip_setup(parse, "initial startup");
+}
+
+/*------------------------------------------------------------
+ * trimbletsip_event - handle Trimble events
+ * simple evente handler - attempt to re-initialize receiver
+ */
+static void
+trimbletsip_event(
+ struct parseunit *parse,
+ int event
+ )
+{
+ switch (event)
+ {
+ case CEVNT_BADREPLY: /* reset on garbled input */
+ case CEVNT_TIMEOUT: /* reset on no input */
+ (void)trimbletsip_setup(parse, "event BAD_REPLY/TIMEOUT");
+ break;
+
+ default: /* ignore */
+ break;
+ }
+}
+
+/*
+ * getflt, getint convert fields in the incoming data into the
+ * appropriate type of item
+ *
+ * CAVEAT: these routines are currently definitely byte order dependent
+ * and assume Representation(float) == IEEE754
+ * These functions MUST be converted to portable versions (especially
+ * converting the float representation into ntp_fp formats in order
+ * to avoid floating point operations at all!
+ */
+
+static float
+getflt(
+ u_char *bp
+ )
+{
+ union uval uval;
+
+#ifdef WORDS_BIGENDIAN
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp;
+#else /* ! WORDS_BIGENDIAN */
+ uval.bd[3] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[0] = *bp;
+#endif /* ! WORDS_BIGENDIAN */
+ return uval.fv;
+}
+
+static double
+getdbl(
+ u_char *bp
+ )
+{
+ union uval uval;
+
+#ifdef WORDS_BIGENDIAN
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp++;
+ uval.bd[4] = *bp++;
+ uval.bd[5] = *bp++;
+ uval.bd[6] = *bp++;
+ uval.bd[7] = *bp;
+#else /* ! WORDS_BIGENDIAN */
+ uval.bd[7] = *bp++;
+ uval.bd[6] = *bp++;
+ uval.bd[5] = *bp++;
+ uval.bd[4] = *bp++;
+ uval.bd[3] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[0] = *bp;
+#endif /* ! WORDS_BIGENDIAN */
+ return uval.dv;
+}
+
+static int
+getshort(
+ unsigned char *p
+ )
+{
+ return get_msb_short(&p);
+}
+
+/*--------------------------------------------------
+ * trimbletsip_message - process trimble messages
+ */
+#define RTOD (180.0 / 3.1415926535898)
+#define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */
+
+static void
+trimbletsip_message(
+ struct parseunit *parse,
+ parsetime_t *parsetime
+ )
+{
+ unsigned char *buffer = parsetime->parse_msg;
+ unsigned int size = parsetime->parse_msglen;
+
+ if ((size < 4) ||
+ (buffer[0] != DLE) ||
+ (buffer[size-1] != ETX) ||
+ (buffer[size-2] != DLE))
+ {
+#ifdef DEBUG
+ if (debug > 2) {
+ int i;
+
+ printf("TRIMBLE BAD packet, size %d:\n ", size);
+ for (i = 0; i < size; i++) {
+ printf ("%2.2x, ", buffer[i]&0xff);
+ if (i%16 == 15) printf("\n\t");
+ }
+ printf("\n");
+ }
+#endif
+ return;
+ }
+ else
+ {
+ int var_flag;
+ trimble_t *tr = parse->localdata;
+ unsigned int cmd = buffer[1];
+ char pbuffer[200];
+ char *t = pbuffer;
+ cmd_info_t *s;
+
+#ifdef DEBUG
+ if (debug > 3) {
+ int i;
+
+ printf("TRIMBLE packet 0x%02x, size %d:\n ", cmd, size);
+ for (i = 0; i < size; i++) {
+ printf ("%2.2x, ", buffer[i]&0xff);
+ if (i%16 == 15) printf("\n\t");
+ }
+ printf("\n");
+ }
+#endif
+
+ if (tr)
+ tr->last_msg = current_time;
+
+ s = trimble_convert(cmd, trimble_rcmds);
+
+ if (s)
+ {
+ sprintf(t, "%s=\"", s->varname);
+ }
+ else
+ {
+ printf("TRIMBLE unknown command 0x%02x\n", cmd);
+ return;
+ }
+
+ var_flag = s->varmode;
+
+ t += strlen(t);
+
+ switch(cmd)
+ {
+ case CMD_RCURTIME:
+ sprintf(t, "%f, %d, %f",
+ getflt((unsigned char *)&mb(0)), getshort((unsigned char *)&mb(4)),
+ getflt((unsigned char *)&mb(6)));
+ break;
+
+ case CMD_RBEST4:
+ strcpy(t, "mode: ");
+ t += strlen(t);
+ switch (mb(0) & 0xF)
+ {
+ default:
+ sprintf(t, "0x%x", mb(0) & 0x7);
+ break;
+
+ case 1:
+ strcat(t, "0D");
+ break;
+
+ case 3:
+ strcat(t, "2D");
+ break;
+
+ case 4:
+ strcat(t, "3D");
+ break;
+ }
+ t += strlen(t);
+ if (mb(0) & 0x10)
+ strcpy(t, "-MANUAL, ");
+ else
+ strcpy(t, "-AUTO, ");
+ t += strlen(t);
+
+ sprintf(t, "satellites %02d %02d %02d %02d, PDOP %.2f, HDOP %.2f, VDOP %.2f, TDOP %.2f",
+ mb(1), mb(2), mb(3), mb(4),
+ getflt((unsigned char *)&mb(5)),
+ getflt((unsigned char *)&mb(9)),
+ getflt((unsigned char *)&mb(13)),
+ getflt((unsigned char *)&mb(17)));
+
+ break;
+
+ case CMD_RVERSION:
+ sprintf(t, "%d.%d (%d/%d/%d)",
+ mb(0)&0xff, mb(1)&0xff, 1900+(mb(4)&0xff), mb(2)&0xff, mb(3)&0xff);
+ break;
+
+ case CMD_RRECVHEALTH:
+ {
+ static const char *msgs[] =
+ {
+ "Battery backup failed",
+ "Signal processor error",
+ "Alignment error, channel or chip 1",
+ "Alignment error, channel or chip 2",
+ "Antenna feed line fault",
+ "Excessive ref freq. error",
+ "<BIT 6>",
+ "<BIT 7>"
+ };
+
+ int i, bits;
+
+ switch (mb(0) & 0xFF)
+ {
+ default:
+ sprintf(t, "illegal value 0x%02x", mb(0) & 0xFF);
+ break;
+ case 0x00:
+ strcpy(t, "doing position fixes");
+ break;
+ case 0x01:
+ strcpy(t, "no GPS time yet");
+ break;
+ case 0x03:
+ strcpy(t, "PDOP too high");
+ break;
+ case 0x08:
+ strcpy(t, "no usable satellites");
+ break;
+ case 0x09:
+ strcpy(t, "only ONE usable satellite");
+ break;
+ case 0x0A:
+ strcpy(t, "only TWO usable satellites");
+ break;
+ case 0x0B:
+ strcpy(t, "only THREE usable satellites");
+ break;
+ case 0x0C:
+ strcpy(t, "the chosen satellite is unusable");
+ break;
+ }
+
+ t += strlen(t);
+
+ bits = mb(1) & 0xFF;
+
+ for (i = 0; i < 8; i++)
+ if (bits & (0x1<<i))
+ {
+ sprintf(t, ", %s", msgs[i]);
+ t += strlen(t);
+ }
+ }
+ break;
+
+ case CMD_RMESSAGE:
+ mkreadable(t, (int)(sizeof(pbuffer) - (t - pbuffer)), (char *)&mb(0), (unsigned)(size - 2 - (&mb(0) - buffer)), 0);
+ break;
+
+ case CMD_RMACHSTAT:
+ {
+ static const char *msgs[] =
+ {
+ "Synthesizer Fault",
+ "Battery Powered Time Clock Fault",
+ "A-to-D Converter Fault",
+ "The almanac stored in the receiver is not complete and current",
+ "<BIT 4>",
+ "<BIT 5",
+ "<BIT 6>",
+ "<BIT 7>"
+ };
+
+ int i, bits;
+
+ sprintf(t, "machine id 0x%02x", mb(0) & 0xFF);
+ t += strlen(t);
+
+ bits = mb(1) & 0xFF;
+
+ for (i = 0; i < 8; i++)
+ if (bits & (0x1<<i))
+ {
+ sprintf(t, ", %s", msgs[i]);
+ t += strlen(t);
+ }
+
+ sprintf(t, ", Superpackets %ssupported", (mb(2) & 0xFF) ? "" :"un" );
+ }
+ break;
+
+ case CMD_ROPERPARAM:
+ sprintf(t, "%2x %.1f %.1f %.1f %.1f",
+ mb(0), getflt((unsigned char *)&mb(1)), getflt((unsigned char *)&mb(5)),
+ getflt((unsigned char *)&mb(9)), getflt((unsigned char *)&mb(13)));
+ break;
+
+ case CMD_RUTCPARAM:
+ {
+ float t0t = getflt((unsigned char *)&mb(14));
+ short wnt = getshort((unsigned char *)&mb(18));
+ short dtls = getshort((unsigned char *)&mb(12));
+ short wnlsf = getshort((unsigned char *)&mb(20));
+ short dn = getshort((unsigned char *)&mb(22));
+ short dtlsf = getshort((unsigned char *)&mb(24));
+
+ if ((int)t0t != 0)
+ {
+ mk_utcinfo(t, wnt, wnlsf, dn, dtls, dtlsf);
+ }
+ else
+ {
+ strcpy(t, "<NO UTC DATA>");
+ }
+ }
+ break;
+
+ case CMD_RSAT1BIAS:
+ sprintf(t, "%.1fm %.2fm/s at %.1fs",
+ getflt(&mb(0)), getflt(&mb(4)), getflt(&mb(8)));
+ break;
+
+ case CMD_RIOOPTIONS:
+ {
+ sprintf(t, "%02x %02x %02x %02x",
+ mb(0), mb(1), mb(2), mb(3));
+ if (mb(0) != TRIM_POS_OPT ||
+ mb(2) != TRIM_TIME_OPT)
+ {
+ (void)trimbletsip_setup(parse, "bad io options");
+ }
+ }
+ break;
+
+ case CMD_RSPOSXYZ:
+ {
+ double x = getflt((unsigned char *)&mb(0));
+ double y = getflt((unsigned char *)&mb(4));
+ double z = getflt((unsigned char *)&mb(8));
+ double f = getflt((unsigned char *)&mb(12));
+
+ if (f > 0.0)
+ sprintf(t, "x= %.1fm, y= %.1fm, z= %.1fm, time_of_fix= %f sec",
+ x, y, z,
+ f);
+ else
+ return;
+ }
+ break;
+
+ case CMD_RSLLAPOS:
+ {
+ double lat = getflt((unsigned char *)&mb(0));
+ double lng = getflt((unsigned char *)&mb(4));
+ double f = getflt((unsigned char *)&mb(12));
+
+ if (f > 0.0)
+ sprintf(t, "lat %f %c, long %f %c, alt %.2fm",
+ ((lat < 0.0) ? (-lat) : (lat))*RTOD, (lat < 0.0 ? 'S' : 'N'),
+ ((lng < 0.0) ? (-lng) : (lng))*RTOD, (lng < 0.0 ? 'W' : 'E'),
+ getflt((unsigned char *)&mb(8)));
+ else
+ return;
+ }
+ break;
+
+ case CMD_RDOUBLEXYZ:
+ {
+ double x = getdbl((unsigned char *)&mb(0));
+ double y = getdbl((unsigned char *)&mb(8));
+ double z = getdbl((unsigned char *)&mb(16));
+ sprintf(t, "x= %.1fm, y= %.1fm, z= %.1fm",
+ x, y, z);
+ }
+ break;
+
+ case CMD_RDOUBLELLA:
+ {
+ double lat = getdbl((unsigned char *)&mb(0));
+ double lng = getdbl((unsigned char *)&mb(8));
+ sprintf(t, "lat %f %c, lon %f %c, alt %.2fm",
+ ((lat < 0.0) ? (-lat) : (lat))*RTOD, (lat < 0.0 ? 'S' : 'N'),
+ ((lng < 0.0) ? (-lng) : (lng))*RTOD, (lng < 0.0 ? 'W' : 'E'),
+ getdbl((unsigned char *)&mb(16)));
+ }
+ break;
+
+ case CMD_RALLINVIEW:
+ {
+ int i, sats;
+
+ strcpy(t, "mode: ");
+ t += strlen(t);
+ switch (mb(0) & 0x7)
+ {
+ default:
+ sprintf(t, "0x%x", mb(0) & 0x7);
+ break;
+
+ case 3:
+ strcat(t, "2D");
+ break;
+
+ case 4:
+ strcat(t, "3D");
+ break;
+ }
+ t += strlen(t);
+ if (mb(0) & 0x8)
+ strcpy(t, "-MANUAL, ");
+ else
+ strcpy(t, "-AUTO, ");
+ t += strlen(t);
+
+ sats = (mb(0)>>4) & 0xF;
+
+ sprintf(t, "PDOP %.2f, HDOP %.2f, VDOP %.2f, TDOP %.2f, %d satellite%s in view: ",
+ getflt((unsigned char *)&mb(1)),
+ getflt((unsigned char *)&mb(5)),
+ getflt((unsigned char *)&mb(9)),
+ getflt((unsigned char *)&mb(13)),
+ sats, (sats == 1) ? "" : "s");
+ t += strlen(t);
+
+ for (i=0; i < sats; i++)
+ {
+ sprintf(t, "%s%02d", i ? ", " : "", mb(17+i));
+ t += strlen(t);
+ if (tr)
+ tr->ctrack |= (1 << (mb(17+i)-1));
+ }
+
+ if (tr)
+ { /* mark for tracking status query */
+ tr->qtracking = 1;
+ }
+ }
+ break;
+
+ case CMD_RSTATTRACK:
+ {
+ sprintf(t-2, "[%02d]=\"", mb(0)); /* add index to var name */
+ t += strlen(t);
+
+ if (getflt((unsigned char *)&mb(4)) < 0.0)
+ {
+ strcpy(t, "<NO MEASUREMENTS>");
+ var_flag &= ~DEF;
+ }
+ else
+ {
+ sprintf(t, "ch=%d, acq=%s, eph=%d, signal_level= %5.2f, elevation= %5.2f, azimuth= %6.2f",
+ (mb(1) & 0xFF)>>3,
+ mb(2) ? ((mb(2) == 1) ? "ACQ" : "SRCH") : "NEVER",
+ mb(3),
+ getflt((unsigned char *)&mb(4)),
+ getflt((unsigned char *)&mb(12)) * RTOD,
+ getflt((unsigned char *)&mb(16)) * RTOD);
+ t += strlen(t);
+ if (mb(20))
+ {
+ var_flag &= ~DEF;
+ strcpy(t, ", OLD");
+ }
+ t += strlen(t);
+ if (mb(22))
+ {
+ if (mb(22) == 1)
+ strcpy(t, ", BAD PARITY");
+ else
+ if (mb(22) == 2)
+ strcpy(t, ", BAD EPH HEALTH");
+ }
+ t += strlen(t);
+ if (mb(23))
+ strcpy(t, ", collecting data");
+ }
+ }
+ break;
+
+ default:
+ strcpy(t, "<UNDECODED>");
+ break;
+ }
+ strcat(t,"\"");
+ set_var(&parse->kv, pbuffer, sizeof(pbuffer), var_flag);
+ }
+}
+
+
+/**============================================================
+ ** RAWDCF support
+ **/
+
+/*--------------------------------------------------
+ * rawdcf_init_1 - set up modem lines for RAWDCF receivers
+ * SET DTR line
+ */
+#if defined(TIOCMSET) && (defined(TIOCM_DTR) || defined(CIOCM_DTR))
+static int
+rawdcf_init_1(
+ struct parseunit *parse
+ )
+{
+ /* fixed 2000 for using with Linux by Wolfram Pienkoss <wp@bszh.de> */
+ /*
+ * You can use the RS232 to supply the power for a DCF77 receiver.
+ * Here a voltage between the DTR and the RTS line is used. Unfortunately
+ * the name has changed from CIOCM_DTR to TIOCM_DTR recently.
+ */
+ int sl232;
+
+ if (ioctl(parse->generic->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_1: WARNING: ioctl(fd, TIOCMGET, [C|T]IOCM_DTR): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+
+#ifdef TIOCM_DTR
+ sl232 = (sl232 & ~TIOCM_RTS) | TIOCM_DTR; /* turn on DTR, clear RTS for power supply */
+#else
+ sl232 = (sl232 & ~CIOCM_RTS) | CIOCM_DTR; /* turn on DTR, clear RTS for power supply */
+#endif
+
+ if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_1: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_DTR): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+}
+#else
+static int
+rawdcfdtr_init_1(
+ struct parseunit *parse
+ )
+{
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_1: WARNING: OS interface incapable of setting DTR to power DCF modules", CLK_UNIT(parse->peer));
+ return 0;
+}
+#endif /* DTR initialisation type */
+
+/*--------------------------------------------------
+ * rawdcf_init_2 - set up modem lines for RAWDCF receivers
+ * CLR DTR line, SET RTS line
+ */
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+static int
+rawdcf_init_2(
+ struct parseunit *parse
+ )
+{
+ /* fixed 2000 for using with Linux by Wolfram Pienkoss <wp@bszh.de> */
+ /*
+ * You can use the RS232 to supply the power for a DCF77 receiver.
+ * Here a voltage between the DTR and the RTS line is used. Unfortunately
+ * the name has changed from CIOCM_DTR to TIOCM_DTR recently.
+ */
+ int sl232;
+
+ if (ioctl(parse->generic->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_2: WARNING: ioctl(fd, TIOCMGET, [C|T]IOCM_RTS): %m", CLK_UNIT(parse->peer));
+ return 0;
+ }
+
+#ifdef TIOCM_RTS
+ sl232 = (sl232 & ~TIOCM_DTR) | TIOCM_RTS; /* turn on RTS, clear DTR for power supply */
+#else
+ sl232 = (sl232 & ~CIOCM_DTR) | CIOCM_RTS; /* turn on RTS, clear DTR for power supply */
+#endif
+
+ if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_2: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_RTS): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+}
+#else
+static int
+rawdcf_init_2(
+ struct parseunit *parse
+ )
+{
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init_2: WARNING: OS interface incapable of setting RTS to power DCF modules", CLK_UNIT(parse->peer));
+ return 0;
+}
+#endif /* DTR initialisation type */
+
+#else /* defined(REFCLOCK) && defined(PARSE) */
+int refclock_parse_bs;
+#endif /* defined(REFCLOCK) && defined(PARSE) */
+
+/*
+ * History:
+ *
+ * refclock_parse.c,v
+ * Revision 4.36 1999/11/28 17:18:20 kardel
+ * disabled burst mode
+ *
+ * Revision 4.35 1999/11/28 09:14:14 kardel
+ * RECON_4_0_98F
+ *
+ * Revision 4.34 1999/05/14 06:08:05 kardel
+ * store current_time in a suitable container (u_long)
+ *
+ * Revision 4.33 1999/05/13 21:48:38 kardel
+ * double the no response timeout interval
+ *
+ * Revision 4.32 1999/05/13 20:09:13 kardel
+ * complain only about missing polls after a full poll interval
+ *
+ * Revision 4.31 1999/05/13 19:59:32 kardel
+ * add clock type 16 for RTS set DTR clr in RAWDCF
+ *
+ * Revision 4.30 1999/02/28 20:36:43 kardel
+ * fixed printf fmt
+ *
+ * Revision 4.29 1999/02/28 19:58:23 kardel
+ * updated copyright information
+ *
+ * Revision 4.28 1999/02/28 19:01:50 kardel
+ * improved debug out on sent Meinberg messages
+ *
+ * Revision 4.27 1999/02/28 18:05:55 kardel
+ * no linux/ppsclock.h stuff
+ *
+ * Revision 4.26 1999/02/28 15:27:27 kardel
+ * wharton clock integration
+ *
+ * Revision 4.25 1999/02/28 14:04:46 kardel
+ * added missing double quotes to UTC information string
+ *
+ * Revision 4.24 1999/02/28 12:06:50 kardel
+ * (parse_control): using gmprettydate instead of prettydate()
+ * (mk_utcinfo): new function for formatting GPS derived UTC information
+ * (gps16x_message): changed to use mk_utcinfo()
+ * (trimbletsip_message): changed to use mk_utcinfo()
+ * ignoring position information in unsynchronized mode
+ * (parse_start): augument linux support for optional ASYNC_LOW_LATENCY
+ *
+ * Revision 4.23 1999/02/23 19:47:53 kardel
+ * fixed #endifs
+ * (stream_receive): fixed formats
+ *
+ * Revision 4.22 1999/02/22 06:21:02 kardel
+ * use new autoconfig symbols
+ *
+ * Revision 4.21 1999/02/21 12:18:13 kardel
+ * 4.91f reconcilation
+ *
+ * Revision 4.20 1999/02/21 10:53:36 kardel
+ * initial Linux PPSkit version
+ *
+ * Revision 4.19 1999/02/07 09:10:45 kardel
+ * clarify STREAMS mitigation rules in comment
+ *
+ * Revision 4.18 1998/12/20 23:45:34 kardel
+ * fix types and warnings
+ *
+ * Revision 4.17 1998/11/15 21:24:51 kardel
+ * cannot access mbg_ routines when CLOCK_MEINBERG
+ * is not defined
+ *
+ * Revision 4.16 1998/11/15 20:28:17 kardel
+ * Release 4.0.73e13 reconcilation
+ *
+ * Revision 4.15 1998/08/22 21:56:08 kardel
+ * fixed IO handling for non-STREAM IO
+ *
+ * Revision 4.14 1998/08/16 19:00:48 kardel
+ * (gps16x_message): reduced UTC parameter information (dropped A0,A1)
+ * made uval a local variable (killed one of the last globals)
+ * (sendetx): added logging of messages when in debug mode
+ * (trimble_check): added periodic checks to facilitate re-initialization
+ * (trimbletsip_init): made use of EOL character if in non-kernel operation
+ * (trimbletsip_message): extended message interpretation
+ * (getdbl): fixed data conversion
+ *
+ * Revision 4.13 1998/08/09 22:29:13 kardel
+ * Trimble TSIP support
+ *
+ * Revision 4.12 1998/07/11 10:05:34 kardel
+ * Release 4.0.73d reconcilation
+ *
+ * Revision 4.11 1998/06/14 21:09:42 kardel
+ * Sun acc cleanup
+ *
+ * Revision 4.10 1998/06/13 12:36:45 kardel
+ * signed/unsigned, name clashes
+ *
+ * Revision 4.9 1998/06/12 15:30:00 kardel
+ * prototype fixes
+ *
+ * Revision 4.8 1998/06/12 11:19:42 kardel
+ * added direct input processing routine for refclocks in
+ * order to avaiod that single character io gobbles up all
+ * receive buffers and drops input data. (Problem started
+ * with fast machines so a character a buffer was possible
+ * one of the few cases where faster machines break existing
+ * allocation algorithms)
+ *
+ * Revision 4.7 1998/06/06 18:35:20 kardel
+ * (parse_start): added BURST mode initialisation
+ *
+ * Revision 4.6 1998/05/27 06:12:46 kardel
+ * RAWDCF_BASEDELAY default added
+ * old comment removed
+ * casts for ioctl()
+ *
+ * Revision 4.5 1998/05/25 22:05:09 kardel
+ * RAWDCF_SETDTR option removed
+ * clock type 14 attempts to set DTR for
+ * power supply of RAWDCF receivers
+ *
+ * Revision 4.4 1998/05/24 16:20:47 kardel
+ * updated comments referencing Meinberg clocks
+ * added RAWDCF clock with DTR set option as type 14
+ *
+ * Revision 4.3 1998/05/24 10:48:33 kardel
+ * calibrated CONRAD RAWDCF default fudge factor
+ *
+ * Revision 4.2 1998/05/24 09:59:35 kardel
+ * corrected version information (ntpq support)
+ *
+ * Revision 4.1 1998/05/24 09:52:31 kardel
+ * use fixed format only (new IO model)
+ * output debug to stdout instead of msyslog()
+ * don't include >"< in ASCII output in order not to confuse
+ * ntpq parsing
+ *
+ * Revision 4.0 1998/04/10 19:52:11 kardel
+ * Start 4.0 release version numbering
+ *
+ * Revision 1.2 1998/04/10 19:28:04 kardel
+ * initial NTP VERSION 4 integration of PARSE with GPS166 binary support
+ * derived from 3.105.1.2 from V3 tree
+ *
+ * Revision information 3.1 - 3.105 from log deleted 1998/04/10 kardel
+ *
+ */
diff --git a/ntpd/refclock_pcf.c b/ntpd/refclock_pcf.c
new file mode 100644
index 0000000..d4e9fd1
--- /dev/null
+++ b/ntpd/refclock_pcf.c
@@ -0,0 +1,224 @@
+/*
+ * refclock_pcf - clock driver for the Conrad parallel port radio clock
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PCF)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the parallel port radio clock sold by Conrad
+ * Electronic under order numbers 967602 and 642002.
+ *
+ * It requires that the local timezone be CET/CEST and that the pcfclock
+ * device driver be installed. A device driver for Linux is available at
+ * http://home.pages.de/~voegele/pcf.html. Information about a FreeBSD
+ * driver is available at http://schumann.cx/pcfclock/.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/pcfclocks/%d"
+#define OLDDEVICE "/dev/pcfclock%d"
+#define PRECISION (-1) /* precision assumed (about 0.5 s) */
+#define REFID "PCF"
+#define DESCRIPTION "Conrad parallel port radio clock"
+
+#define LENPCF 18 /* timecode length */
+
+/*
+ * Function prototypes
+ */
+static int pcf_start P((int, struct peer *));
+static void pcf_shutdown P((int, struct peer *));
+static void pcf_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_pcf = {
+ pcf_start, /* start up driver */
+ pcf_shutdown, /* shut down driver */
+ pcf_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * pcf_start - open the device and initialize data for processing
+ */
+static int
+pcf_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ int fd;
+ char device[128];
+
+ /*
+ * Open device file for reading.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDONLY);
+ if (fd == -1) {
+ (void)sprintf(device, OLDDEVICE, unit);
+ fd = open(device, O_RDONLY);
+ }
+#ifdef DEBUG
+ if (debug)
+ printf ("starting PCF with device %s\n",device);
+#endif
+ if (fd == -1) {
+ return (0);
+ }
+
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ /* one transmission takes 172.5 milliseconds since the radio clock
+ transmits 69 bits with a period of 2.5 milliseconds per bit */
+ pp->fudgetime1 = 0.1725;
+ memcpy((char *)&pp->refid, REFID, 4);
+
+ return (1);
+}
+
+
+/*
+ * pcf_shutdown - shut down the clock
+ */
+static void
+pcf_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ (void)close(pp->io.fd);
+}
+
+
+/*
+ * pcf_poll - called by the transmit procedure
+ */
+static void
+pcf_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ char buf[LENPCF];
+ struct tm tm, *tp;
+ time_t t;
+
+ pp = peer->procptr;
+
+ buf[0] = 0;
+ if (read(pp->io.fd, buf, sizeof(buf)) < sizeof(buf) || buf[0] != 9) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ tm.tm_mday = buf[11] * 10 + buf[10];
+ tm.tm_mon = buf[13] * 10 + buf[12] - 1;
+ tm.tm_year = buf[15] * 10 + buf[14];
+ tm.tm_hour = buf[7] * 10 + buf[6];
+ tm.tm_min = buf[5] * 10 + buf[4];
+ tm.tm_sec = buf[3] * 10 + buf[2];
+ tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1;
+
+ /*
+ * Y2K convert the 2-digit year
+ */
+ if (tm.tm_year < 99)
+ tm.tm_year += 100;
+
+ t = mktime(&tm);
+ if (t == (time_t) -1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+#if defined(__GLIBC__) && defined(_BSD_SOURCE)
+ if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200)
+ || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600)
+ || tm.tm_isdst < 0) {
+#ifdef DEBUG
+ if (debug)
+ printf ("local time zone not set to CET/CEST\n");
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+#endif
+
+ pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm);
+
+#if defined(_REENTRANT) || defined(_THREAD_SAFE)
+ tp = gmtime_r(&t, &tm);
+#else
+ tp = gmtime(&t);
+#endif
+ if (!tp) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+ pp->year = tp->tm_year + 1900;
+ pp->day = tp->tm_yday + 1;
+ pp->hour = tp->tm_hour;
+ pp->minute = tp->tm_min;
+ pp->second = tp->tm_sec;
+ pp->nsec = buf[16] * 31250000;
+ if (buf[17] & 1)
+ pp->nsec += 500000000;
+
+#ifdef DEBUG
+ if (debug)
+ printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+ unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour,
+ pp->minute, pp->second);
+#endif
+
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2))
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+#else
+int refclock_pcf_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_pst.c b/ntpd/refclock_pst.c
new file mode 100644
index 0000000..2443b2c
--- /dev/null
+++ b/ntpd/refclock_pst.c
@@ -0,0 +1,321 @@
+/*
+ * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PST)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
+ * Receivers. No specific claim of accuracy is made for these receiver,
+ * but actual experience suggests that 10 ms would be a conservative
+ * assumption.
+ *
+ * The DIPswitches should be set for 9600 bps line speed, 24-hour day-
+ * of-year format and UTC time zone. Automatic correction for DST should
+ * be disabled. It is very important that the year be set correctly in
+ * the DIPswitches; otherwise, the day of year will be incorrect after
+ * 28 April of a normal or leap year. The propagation delay DIPswitches
+ * should be set according to the distance from the transmitter for both
+ * WWV and WWVH, as described in the instructions. While the delay can
+ * be set only to within 11 ms, the fudge time1 parameter can be used
+ * for vernier corrections.
+ *
+ * Using the poll sequence QTQDQM, the response timecode is in three
+ * sections totalling 50 ASCII printing characters, as concatenated by
+ * the driver, in the following format:
+ *
+ * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
+ *
+ * on-time = first <cr>
+ * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ * a = AM/PM indicator (' ' for 24-hour mode)
+ * yy = year (from internal switches)
+ * dd/mm/ddd = day of month, month, day of year
+ * s = daylight-saving indicator (' ' for 24-hour mode)
+ * f = frequency enable (O = all frequencies enabled)
+ * r = baud rate (3 = 1200, 6 = 9600)
+ * d = features indicator (@ = month/day display enabled)
+ * z = time zone (0 = UTC)
+ * y = year (5 = 91)
+ * cc = WWV propagation delay (52 = 22 ms)
+ * hh = WWVH propagation delay (81 = 33 ms)
+ * SS = status (80 or 82 = operating correctly)
+ * F = current receive frequency (4 = 15 MHz)
+ * T = transmitter (C = WWV, H = WWVH)
+ * tttt = time since last update (0000 = minutes)
+ * uu = flush character (03 = ^c)
+ * xx = 94 (unknown)
+ *
+ * The alarm condition is indicated by other than '8' at A, which occurs
+ * during initial synchronization and when received signal is lost for
+ * an extended period; unlock condition is indicated by other than
+ * "0000" in the tttt subfield at Q.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwv%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define WWVREFID "WWV\0" /* WWV reference ID */
+#define WWVHREFID "WWVH" /* WWVH reference ID */
+#define DESCRIPTION "PSTI/Traconex WWV/WWVH Receiver" /* WRU */
+#define PST_PHI (10e-6) /* max clock oscillator offset */
+#define LENPST 46 /* min timecode length */
+
+/*
+ * Unit control structure
+ */
+struct pstunit {
+ int tcswitch; /* timecode switch */
+ char *lastptr; /* pointer to timecode data */
+};
+
+/*
+ * Function prototypes
+ */
+static int pst_start P((int, struct peer *));
+static void pst_shutdown P((int, struct peer *));
+static void pst_receive P((struct recvbuf *));
+static void pst_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_pst = {
+ pst_start, /* start up driver */
+ pst_shutdown, /* shut down driver */
+ pst_poll, /* transmit poll message */
+ noentry, /* not used (old pst_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old pst_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * pst_start - open the devices and initialize data for processing
+ */
+static int
+pst_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct pstunit *)emalloc(sizeof(struct pstunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct pstunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = pst_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ peer->burst = MAXSTAGE;
+ return (1);
+}
+
+
+/*
+ * pst_shutdown - shut down the clock
+ */
+static void
+pst_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * pst_receive - receive data from the serial interface
+ */
+static void
+pst_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ u_long ltemp;
+ char ampmchar; /* AM/PM indicator */
+ char daychar; /* standard/daylight indicator */
+ char junque[10]; /* "yy/dd/mm/" discard */
+ char info[14]; /* "frdzycchhSSFT" clock info */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->a_lastcode
+ + BMAX - 2 - up->lastptr, &trtmp);
+ *up->lastptr++ = ' ';
+ *up->lastptr = '\0';
+
+ /*
+ * Note we get a buffer and timestamp for each <cr>, but only
+ * the first timestamp is retained.
+ */
+ if (up->tcswitch == 0)
+ pp->lastrec = trtmp;
+ up->tcswitch++;
+ pp->lencode = up->lastptr - pp->a_lastcode;
+ if (up->tcswitch < 3)
+ return;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENPST) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format:
+ * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx"
+ */
+ if (sscanf(pp->a_lastcode,
+ "%c%2d:%2d:%2d.%3ld%c %9s%3d%13s%4ld",
+ &ampmchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec,
+ &daychar, junque, &pp->day, info, &ltemp) != 10) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ pp->nsec *= 1000000;
+
+ /*
+ * Decode synchronization, quality and last update. If
+ * unsynchronized, set the leap bits accordingly and exit. Once
+ * synchronized, the dispersion depends only on when the clock
+ * was last heard, which depends on the time since last update,
+ * as reported by the clock.
+ */
+ if (info[9] != '8')
+ pp->leap = LEAP_NOTINSYNC;
+ if (info[12] == 'H')
+ memcpy((char *)&pp->refid, WWVHREFID, 4);
+ else
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ if (ltemp == 0)
+ pp->lastref = pp->lastrec;
+ pp->disp = PST_PHI * ltemp * 60;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+
+}
+
+
+/*
+ * pst_poll - called by the transmit procedure
+ */
+static void
+pst_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The PSTI/Traconex clock responds to a
+ * "QTQDQMT" by returning a timecode in the format specified
+ * above. Note there is no checking on state, since this may not
+ * be the only customer reading the clock. Only one customer
+ * need poll the clock; all others just listen in. If the clock
+ * becomes unreachable, declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ up->tcswitch = 0;
+ up->lastptr = pp->a_lastcode;
+ if (write(pp->io.fd, "QTQDQMT", 6) != 6)
+ refclock_report(peer, CEVNT_FAULT);
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("pst: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+ peer->burst = MAXSTAGE;
+ pp->polls++;
+}
+
+#else
+int refclock_pst_int;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_ptbacts.c b/ntpd/refclock_ptbacts.c
new file mode 100644
index 0000000..09d7bf4
--- /dev/null
+++ b/ntpd/refclock_ptbacts.c
@@ -0,0 +1,16 @@
+/*
+ * crude hack to avoid hard links in distribution
+ * and keep only one ACTS type source for different
+ * ACTS refclocks
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_PTBACTS)
+# define KEEPPTBACTS
+# include "refclock_acts.c"
+#else /* not (REFCLOCK && CLOCK_PTBACTS) */
+int refclock_ptbacts_bs;
+#endif /* not (REFCLOCK && CLOCK_PTBACTS) */
diff --git a/ntpd/refclock_ripencc.c b/ntpd/refclock_ripencc.c
new file mode 100644
index 0000000..6337f63
--- /dev/null
+++ b/ntpd/refclock_ripencc.c
@@ -0,0 +1,4870 @@
+/*
+ * $Id: refclock_ripencc.c,v 1.13 2002/06/18 14:20:55 marks Exp marks $
+ *
+ * Copyright (c) 2002 RIPE NCC
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of the author not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+ * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ *
+ * This driver was developed for use with the RIPE NCC TTM project.
+ *
+ *
+ * The initial driver was developed by Daniel Karrenberg <dfk@ripe.net>
+ * using the code made available by Trimble. This was for xntpd-3.x.x
+ *
+ * Rewrite of the driver for ntpd-4.x.x by Mark Santcroos <marks@ripe.net>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#if defined(REFCLOCK) && defined(CLOCK_RIPENCC)
+
+#include "ntp_stdlib.h"
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_io.h"
+
+#ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+#else /* HAVE_TIMEPPS_H */
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# endif /* HAVE_SYS_TIMEPPS_H */
+#endif /* HAVE_TIMEPPS_H */
+
+/*
+ * Definitions
+ */
+
+/* we are on little endian */
+#define BYTESWAP
+
+/*
+ * DEBUG statements: uncomment if necessary
+ */
+/* #define DEBUG_NCC */ /* general debug statements */
+/* #define DEBUG_PPS */ /* debug pps */
+/* #define DEBUG_RAW */ /* print raw packets */
+
+#define TRIMBLE_OUTPUT_FUNC
+#define TSIP_VERNUM "7.12a"
+
+#ifndef FALSE
+#define FALSE (0)
+#define TRUE (!FALSE)
+#endif /* FALSE */
+
+#define GPS_PI (3.1415926535898)
+#define GPS_C (299792458.)
+#define D2R (GPS_PI/180.0)
+#define R2D (180.0/GPS_PI)
+#define WEEK (604800.)
+#define MAXCHAN (8)
+
+/* control characters for TSIP packets */
+#define DLE (0x10)
+#define ETX (0x03)
+
+#define MAX_RPTBUF (256)
+
+/* values of TSIPPKT.status */
+#define TSIP_PARSED_EMPTY 0
+#define TSIP_PARSED_FULL 1
+#define TSIP_PARSED_DLE_1 2
+#define TSIP_PARSED_DATA 3
+#define TSIP_PARSED_DLE_2 4
+
+#define UTCF_UTC_AVAIL (unsigned char) (1) /* UTC available */
+#define UTCF_LEAP_SCHD (unsigned char) (1<<4) /* Leap scheduled */
+#define UTCF_LEAP_PNDG (unsigned char) (1<<5) /* Leap pending, will occur at end of day */
+
+#define DEVICE "/dev/gps%d" /* name of radio device */
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference id */
+#define REFID_LEN 4
+#define DESCRIPTION "RIPE NCC GPS (Palisade)" /* Description */
+#define SPEED232 B9600 /* 9600 baud */
+
+#define NSAMPLES 3 /* stages of median filter */
+
+/* Structures */
+
+/* TSIP packets have the following structure, whether report or command. */
+typedef struct {
+ short
+ counter, /* counter */
+ len; /* size of buf; < MAX_RPTBUF unsigned chars */
+ unsigned char
+ status, /* TSIP packet format/parse status */
+ code, /* TSIP code */
+ buf[MAX_RPTBUF];/* report or command string */
+} TSIPPKT;
+
+/* TSIP binary data structures */
+typedef struct {
+ unsigned char
+ t_oa_raw, SV_health;
+ float
+ e, t_oa, i_0, OMEGADOT, sqrt_A,
+ OMEGA_0, omega, M_0, a_f0, a_f1,
+ Axis, n, OMEGA_n, ODOT_n, t_zc;
+ short
+ weeknum, wn_oa;
+} ALM_INFO;
+
+typedef struct { /* Almanac health page (25) parameters */
+ unsigned char
+ WN_a, SV_health[32], t_oa;
+} ALH_PARMS;
+
+typedef struct { /* Universal Coordinated Time (UTC) parms */
+ double
+ A_0;
+ float
+ A_1;
+ short
+ delta_t_LS;
+ float
+ t_ot;
+ short
+ WN_t, WN_LSF, DN, delta_t_LSF;
+} UTC_INFO;
+
+typedef struct { /* Ionospheric info (float) */
+ float
+ alpha_0, alpha_1, alpha_2, alpha_3,
+ beta_0, beta_1, beta_2, beta_3;
+} ION_INFO;
+
+typedef struct { /* Subframe 1 info (float) */
+ short
+ weeknum;
+ unsigned char
+ codeL2, L2Pdata, SVacc_raw, SV_health;
+ short
+ IODC;
+ float
+ T_GD, t_oc, a_f2, a_f1, a_f0, SVacc;
+} EPHEM_CLOCK;
+
+typedef struct { /* Ephemeris info (float) */
+ unsigned char
+ IODE, fit_interval;
+ float
+ C_rs, delta_n;
+ double
+ M_0;
+ float
+ C_uc;
+ double
+ e;
+ float
+ C_us;
+ double
+ sqrt_A;
+ float
+ t_oe, C_ic;
+ double
+ OMEGA_0;
+ float
+ C_is;
+ double
+ i_0;
+ float
+ C_rc;
+ double
+ omega;
+ float
+ OMEGADOT, IDOT;
+ double
+ Axis, n, r1me2, OMEGA_n, ODOT_n;
+} EPHEM_ORBIT;
+
+typedef struct { /* Navigation data structure */
+ short
+ sv_number; /* SV number (0 = no entry) */
+ float
+ t_ephem; /* time of ephemeris collection */
+ EPHEM_CLOCK
+ ephclk; /* subframe 1 data */
+ EPHEM_ORBIT
+ ephorb; /* ephemeris data */
+} NAV_INFO;
+
+typedef struct {
+ unsigned char
+ bSubcode,
+ operating_mode,
+ dgps_mode,
+ dyn_code,
+ trackmode;
+ float
+ elev_mask,
+ cno_mask,
+ dop_mask,
+ dop_switch;
+ unsigned char
+ dgps_age_limit;
+} TSIP_RCVR_CFG;
+
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+static char
+ *dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"},
+ old_baudnum[] = {0, 1, 4, 5, 6, 8, 9, 11, 28, 12},
+ *st_baud_text_app [] = {"", "", " 300", " 600", " 1200", " 2400",
+ " 4800", " 9600", "19200", "38400"},
+ *old_parity_text[] = {"EVEN", "ODD", "", "", "NONE"},
+ *parity_text [] = {"NONE", "ODD", "EVEN"},
+ *old_input_ch[] = { "TSIP", "RTCM (6 of 8 bits)"},
+ *old_output_ch[] = { "TSIP", "No output", "", "", "", "NMEA 0183"},
+ *protocols_in_text[] = { "", "TSIP", "", ""},
+ *protocols_out_text[] = { "", "TSIP", "NMEA"},
+ *rcvr_port_text [] = { "Port A ", "Port B ", "Current Port"},
+ *dyn_text [] = {"Unchanged", "Land", "Sea", "Air", "Static"},
+ *NavModeText0xBB[] = {"automatic", "time only (0-D)", "", "2-D",
+ "3-D", "", "", "OverDetermined Time"},
+ *PPSTimeBaseText[] = {"GPS", "UTC", "USER"},
+ *PPSPolarityText[] = {"Positive", "Negative"},
+ *MaskText[] = { "Almanac ", "Ephemeris", "UTC ", "Iono ",
+ "GPS Msg ", "Alm Hlth ", "Time Fix ", "SV Select",
+ "Ext Event", "Pos Fix ", "Raw Meas "};
+
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+/*
+ * Unit control structure
+ */
+struct ripencc_unit {
+ int unit; /* unit number */
+ int pollcnt; /* poll message counter */
+ int polled; /* Hand in a sample? */
+ char leapdelta; /* delta of next leap event */
+ unsigned char utcflags; /* delta of next leap event */
+ l_fp tstamp; /* timestamp of last poll */
+
+ struct timespec ts; /* last timestamp */
+ pps_params_t pps_params; /* pps parameters */
+ pps_info_t pps_info; /* last pps data */
+ pps_handle_t handle; /* pps handlebars */
+
+};
+
+
+/******************* PROTOYPES *****************/
+
+/* prototypes for report parsing primitives */
+short rpt_0x3D (TSIPPKT *rpt, unsigned char *tx_baud_index,
+ unsigned char *rx_baud_index, unsigned char *char_format_index,
+ unsigned char *stop_bits, unsigned char *tx_mode_index,
+ unsigned char *rx_mode_index);
+short rpt_0x40 (TSIPPKT *rpt, unsigned char *sv_prn, short *week_num,
+ float *t_zc, float *eccentricity, float *t_oa, float *i_0,
+ float *OMEGA_dot, float *sqrt_A, float *OMEGA_0, float *omega,
+ float *M_0);
+short rpt_0x41 (TSIPPKT *rpt, float *time_of_week, float *UTC_offset,
+ short *week_num);
+short rpt_0x42 (TSIPPKT *rpt, float ECEF_pos[3], float *time_of_fix);
+short rpt_0x43 (TSIPPKT *rpt, float ECEF_vel[3], float *freq_offset,
+ float *time_of_fix);
+short rpt_0x45 (TSIPPKT *rpt, unsigned char *major_nav_version,
+ unsigned char *minor_nav_version, unsigned char *nav_day,
+ unsigned char *nav_month, unsigned char *nav_year,
+ unsigned char *major_dsp_version, unsigned char *minor_dsp_version,
+ unsigned char *dsp_day, unsigned char *dsp_month,
+ unsigned char *dsp_year);
+short rpt_0x46 (TSIPPKT *rpt, unsigned char *status1, unsigned char *status2);
+short rpt_0x47 (TSIPPKT *rpt, unsigned char *nsvs, unsigned char *sv_prn,
+ float *snr);
+short rpt_0x48 (TSIPPKT *rpt, unsigned char *message);
+short rpt_0x49 (TSIPPKT *rpt, unsigned char *sv_health);
+short rpt_0x4A (TSIPPKT *rpt, float *lat, float *lon, float *alt,
+ float *clock_bias, float *time_of_fix);
+short rpt_0x4A_2 (TSIPPKT *rpt, float *alt, float *dummy,
+ unsigned char *alt_flag);
+short rpt_0x4B (TSIPPKT *rpt, unsigned char *machine_id,
+ unsigned char *status3, unsigned char *status4);
+short rpt_0x4C (TSIPPKT *rpt, unsigned char *dyn_code, float *el_mask,
+ float *snr_mask, float *dop_mask, float *dop_switch);
+short rpt_0x4D (TSIPPKT *rpt, float *osc_offset);
+short rpt_0x4E (TSIPPKT *rpt, unsigned char *response);
+short rpt_0x4F (TSIPPKT *rpt, double *a0, float *a1, float *time_of_data,
+ short *dt_ls, short *wn_t, short *wn_lsf, short *dn, short *dt_lsf);
+short rpt_0x54 (TSIPPKT *rpt, float *clock_bias, float *freq_offset,
+ float *time_of_fix);
+short rpt_0x55 (TSIPPKT *rpt, unsigned char *pos_code, unsigned char *vel_code,
+ unsigned char *time_code, unsigned char *aux_code);
+short rpt_0x56 (TSIPPKT *rpt, float vel_ENU[3], float *freq_offset,
+ float *time_of_fix);
+short rpt_0x57 (TSIPPKT *rpt, unsigned char *source_code,
+ unsigned char *diag_code, short *week_num, float *time_of_fix);
+short rpt_0x58 (TSIPPKT *rpt, unsigned char *op_code, unsigned char *data_type,
+ unsigned char *sv_prn, unsigned char *data_length,
+ unsigned char *data_packet);
+short rpt_0x59 (TSIPPKT *rpt, unsigned char *code_type,
+ unsigned char status_code[32]);
+short rpt_0x5A (TSIPPKT *rpt, unsigned char *sv_prn, float *sample_length,
+ float *signal_level, float *code_phase, float *Doppler,
+ double *time_of_fix);
+short rpt_0x5B (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *sv_health,
+ unsigned char *sv_iode, unsigned char *fit_interval_flag,
+ float *time_of_collection, float *time_of_eph, float *sv_accy);
+short rpt_0x5C (TSIPPKT *rpt, unsigned char *sv_prn, unsigned char *slot,
+ unsigned char *chan, unsigned char *acq_flag, unsigned char *eph_flag,
+ float *signal_level, float *time_of_last_msmt, float *elev,
+ float *azim, unsigned char *old_msmt_flag,
+ unsigned char *integer_msec_flag, unsigned char *bad_data_flag,
+ unsigned char *data_collect_flag);
+short rpt_0x6D (TSIPPKT *rpt, unsigned char *manual_mode, unsigned char *nsvs,
+ unsigned char *ndim, unsigned char sv_prn[], float *pdop,
+ float *hdop, float *vdop, float *tdop);
+short rpt_0x82 (TSIPPKT *rpt, unsigned char *diff_mode);
+short rpt_0x83 (TSIPPKT *rpt, double ECEF_pos[3], double *clock_bias,
+ float *time_of_fix);
+short rpt_0x84 (TSIPPKT *rpt, double *lat, double *lon, double *alt,
+ double *clock_bias, float *time_of_fix);
+short rpt_Paly0xBB(TSIPPKT *rpt, TSIP_RCVR_CFG *TsipxBB);
+short rpt_0xBC (TSIPPKT *rpt, unsigned char *port_num,
+ unsigned char *in_baud, unsigned char *out_baud,
+ unsigned char *data_bits, unsigned char *parity,
+ unsigned char *stop_bits, unsigned char *flow_control,
+ unsigned char *protocols_in, unsigned char *protocols_out,
+ unsigned char *reserved);
+
+/* prototypes for superpacket parsers */
+
+short rpt_0x8F0B (TSIPPKT *rpt, unsigned short *event, double *tow,
+ unsigned char *date, unsigned char *month, short *year,
+ unsigned char *dim_mode, short *utc_offset, double *bias, double *drift,
+ float *bias_unc, float *dr_unc, double *lat, double *lon, double *alt,
+ char sv_id[8]);
+short rpt_0x8F14 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]);
+short rpt_0x8F15 (TSIPPKT *rpt, short *datum_idx, double datum_coeffs[5]);
+short rpt_0x8F20 (TSIPPKT *rpt, unsigned char *info, double *lat,
+ double *lon, double *alt, double vel_enu[], double *time_of_fix,
+ short *week_num, unsigned char *nsvs, unsigned char sv_prn[],
+ short sv_IODC[], short *datum_index);
+short rpt_0x8F41 (TSIPPKT *rpt, unsigned char *bSearchRange,
+ unsigned char *bBoardOptions, unsigned long *iiSerialNumber,
+ unsigned char *bBuildYear, unsigned char *bBuildMonth,
+ unsigned char *bBuildDay, unsigned char *bBuildHour,
+ float *fOscOffset, unsigned short *iTestCodeId);
+short rpt_0x8F42 (TSIPPKT *rpt, unsigned char *bProdOptionsPre,
+ unsigned char *bProdNumberExt, unsigned short *iCaseSerialNumberPre,
+ unsigned long *iiCaseSerialNumber, unsigned long *iiProdNumber,
+ unsigned short *iPremiumOptions, unsigned short *iMachineID,
+ unsigned short *iKey);
+short rpt_0x8F45 (TSIPPKT *rpt, unsigned char *bSegMask);
+short rpt_0x8F4A_16 (TSIPPKT *rpt, unsigned char *pps_enabled,
+ unsigned char *pps_timebase, unsigned char *pos_polarity,
+ double *pps_offset, float *bias_unc_threshold);
+short rpt_0x8F4B (TSIPPKT *rpt, unsigned long *decorr_max);
+short rpt_0x8F4D (TSIPPKT *rpt, unsigned long *event_mask);
+short rpt_0x8FA5 (TSIPPKT *rpt, unsigned char *spktmask);
+short rpt_0x8FAD (TSIPPKT *rpt, unsigned short *COUNT, double *FracSec,
+ unsigned char *Hour, unsigned char *Minute, unsigned char *Second,
+ unsigned char *Day, unsigned char *Month, unsigned short *Year,
+ unsigned char *Status, unsigned char *Flags);
+
+/**/
+/* prototypes for command-encode primitives with suffix convention: */
+/* c = clear, s = set, q = query, e = enable, d = disable */
+void cmd_0x1F (TSIPPKT *cmd);
+void cmd_0x26 (TSIPPKT *cmd);
+void cmd_0x2F (TSIPPKT *cmd);
+void cmd_0x35s (TSIPPKT *cmd, unsigned char pos_code, unsigned char vel_code,
+ unsigned char time_code, unsigned char opts_code);
+void cmd_0x3C (TSIPPKT *cmd, unsigned char sv_prn);
+void cmd_0x3Ds (TSIPPKT *cmd, unsigned char baud_out, unsigned char baud_inp,
+ unsigned char char_code, unsigned char stopbitcode,
+ unsigned char output_mode, unsigned char input_mode);
+void cmd_0xBBq (TSIPPKT *cmd, unsigned char subcode) ;
+
+/* prototypes 8E commands */
+void cmd_0x8E0Bq (TSIPPKT *cmd);
+void cmd_0x8E41q (TSIPPKT *cmd);
+void cmd_0x8E42q (TSIPPKT *cmd);
+void cmd_0x8E4Aq (TSIPPKT *cmd);
+void cmd_0x8E4As (TSIPPKT *cmd, unsigned char PPSOnOff, unsigned char TimeBase,
+ unsigned char Polarity, double PPSOffset, float Uncertainty);
+void cmd_0x8E4Bq (TSIPPKT *cmd);
+void cmd_0x8E4Ds (TSIPPKT *cmd, unsigned long AutoOutputMask);
+void cmd_0x8EADq (TSIPPKT *cmd);
+
+/* header/source border XXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+/* Trimble parse functions */
+static int parse0x8FAD P((TSIPPKT *, struct peer *));
+static int parse0x8F0B P((TSIPPKT *, struct peer *));
+#ifdef TRIMBLE_OUTPUT_FUNC
+static int parseany P((TSIPPKT *, struct peer *));
+static void TranslateTSIPReportToText P((TSIPPKT *, char *));
+#endif /* TRIMBLE_OUTPUT_FUNC */
+static int parse0x5C P((TSIPPKT *, struct peer *));
+static int parse0x4F P((TSIPPKT *, struct peer *));
+static void tsip_input_proc P((TSIPPKT *, int));
+
+/* Trimble helper functions */
+static void bPutFloat P((float *, unsigned char *));
+static void bPutDouble P((double *, unsigned char *));
+static void bPutULong P((unsigned long *, unsigned char *));
+static int print_msg_table_header P((int rptcode, char *HdrStr, int force));
+static char * show_time P((float time_of_week));
+
+/* RIPE NCC functions */
+static void ripencc_control P((int, struct refclockstat *, struct
+ refclockstat *, struct peer *));
+static int ripencc_ppsapi P((struct peer *, int, int));
+static int ripencc_get_pps_ts P((struct ripencc_unit *, l_fp *));
+static int ripencc_start P((int, struct peer *));
+static void ripencc_shutdown P((int, struct peer *));
+static void ripencc_poll P((int, struct peer *));
+static void ripencc_send P((struct peer *, TSIPPKT spt));
+static void ripencc_receive P((struct recvbuf *));
+
+/* fill in reflock structure for our clock */
+struct refclock refclock_ripencc = {
+ ripencc_start, /* start up driver */
+ ripencc_shutdown, /* shut down driver */
+ ripencc_poll, /* transmit poll message */
+ ripencc_control, /* control function */
+ noentry, /* initialize driver */
+ noentry, /* debug info */
+ NOFLAGS /* clock flags */
+};
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+
+/*
+ * ripencc_start - open the GPS devices and initialize data for processing
+ */
+static int
+ripencc_start(int unit, struct peer *peer)
+{
+ register struct ripencc_unit *up;
+ struct refclockproc *pp;
+ char device[40];
+ int fd;
+ struct termios tio;
+ TSIPPKT spt;
+
+ /*
+ * Open serial port
+ */
+ (void)snprintf(device, sizeof(device), DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_RAW)))
+ return (0);
+
+ /* from refclock_palisade.c */
+ if (tcgetattr(fd, &tio) < 0) {
+ msyslog(LOG_ERR, "Palisade(%d) tcgetattr(fd, &tio): %m",unit);
+ return (0);
+ }
+
+ /*
+ * set flags
+ */
+ tio.c_cflag |= (PARENB|PARODD);
+ tio.c_iflag &= ~ICRNL;
+ if (tcsetattr(fd, TCSANOW, &tio) == -1) {
+ msyslog(LOG_ERR, "Palisade(%d) tcsetattr(fd, &tio): %m",unit);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct ripencc_unit *)
+ emalloc(sizeof(struct ripencc_unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct ripencc_unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = ripencc_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, REFID_LEN);
+ up->pollcnt = 2;
+ up->unit = unit;
+ up->leapdelta = 0;
+ up->utcflags = 0;
+
+ /*
+ * Initialize the Clock
+ */
+
+ /* query software versions */
+ cmd_0x1F(&spt);
+ ripencc_send(peer, spt);
+
+ /* query receiver health */
+ cmd_0x26(&spt);
+ ripencc_send(peer, spt);
+
+ /* query serial numbers */
+ cmd_0x8E42q(&spt);
+ ripencc_send(peer, spt);
+
+ /* query manuf params */
+ cmd_0x8E41q(&spt);
+ ripencc_send(peer, spt);
+
+ /* i/o opts */ /* trimble manual page A30 */
+ cmd_0x35s(&spt,
+ 0x1C, /* position */
+ 0x00, /* velocity */
+ 0x05, /* timing */
+ 0x0a); /* auxilary */
+ ripencc_send(peer, spt);
+
+ /* turn off port A */
+ cmd_0x3Ds (&spt,
+ 0x0B, /* baud_out */
+ 0x0B, /* baud_inp */
+ 0x07, /* char_code */
+ 0x07, /* stopbitcode */
+ 0x01, /* output_mode */
+ 0x00); /* input_mode */
+ ripencc_send(peer, spt);
+
+ /* set i/o options */
+ cmd_0x8E4As (&spt,
+ 0x01, /* PPS on */
+ 0x01, /* Timebase UTC */
+ 0x00, /* polarity positive */
+ 0., /* 100 ft. cable XXX make flag */
+ 1e-6 * GPS_C); /* turn of biasuncert. > (1us) */
+ ripencc_send(peer,spt);
+
+ /* all outomatic packet output off */
+ cmd_0x8E4Ds(&spt,
+ 0x00000000); /* AutoOutputMask */
+ ripencc_send(peer, spt);
+
+ cmd_0xBBq (&spt,
+ 0x00); /* query primary configuration */
+ ripencc_send(peer,spt);
+
+
+ /* query PPS parameters */
+ cmd_0x8E4Aq (&spt); /* query PPS params */
+ ripencc_send(peer,spt);
+
+ /* query survey limit */
+ cmd_0x8E4Bq (&spt); /* query survey limit */
+ ripencc_send(peer,spt);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ printf("ripencc_start: success\n");
+#endif /* DEBUG_NCC */
+
+ /*
+ * Start the PPSAPI interface if it is there. Default to use
+ * the assert edge and do not enable the kernel hardpps.
+ */
+ if (time_pps_create(fd, &up->handle) < 0) {
+ up->handle = 0;
+ msyslog(LOG_ERR, "refclock_ripencc: time_pps_create failed: %m");
+ return (1);
+ }
+
+ return(ripencc_ppsapi(peer, 0, 0));
+}
+
+/*
+ * ripencc_control - fudge control
+ */
+static void
+ripencc_control(
+ int unit, /* unit (not used) */
+ struct refclockstat *in, /* input parameters (not used) */
+ struct refclockstat *out, /* output parameters (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+
+#ifdef DEBUG_NCC
+ msyslog(LOG_INFO,"%s()",__FUNCTION__);
+#endif /* DEBUG_NCC */
+
+ pp = peer->procptr;
+ ripencc_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
+ pp->sloppyclockflag & CLK_FLAG3);
+}
+
+
+/*
+ * Initialize PPSAPI
+ */
+int
+ripencc_ppsapi(
+ struct peer *peer, /* peer structure pointer */
+ int enb_clear, /* clear enable */
+ int enb_hardpps /* hardpps enable */
+ )
+{
+ struct refclockproc *pp;
+ struct ripencc_unit *up;
+ int capability;
+
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+ if (time_pps_getcap(up->handle, &capability) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: time_pps_getcap failed: %m");
+ return (0);
+ }
+ memset(&up->pps_params, 0, sizeof(pps_params_t));
+ if (enb_clear)
+ up->pps_params.mode = capability & PPS_CAPTURECLEAR;
+ else
+ up->pps_params.mode = capability & PPS_CAPTUREASSERT;
+ if (!up->pps_params.mode) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: invalid capture edge %d",
+ !enb_clear);
+ return (0);
+ }
+ up->pps_params.mode |= PPS_TSFMT_TSPEC;
+ if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (enb_hardpps) {
+ if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
+ up->pps_params.mode & ~PPS_TSFMT_TSPEC,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ripencc: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ pps_enable = 1;
+ }
+ peer->precision = PPS_PRECISION;
+
+#if DEBUG_NCC
+ if (debug) {
+ time_pps_getparams(up->handle, &up->pps_params);
+ printf(
+ "refclock_ripencc: capability 0x%x version %d mode 0x%x kern %d\n",
+ capability, up->pps_params.api_version,
+ up->pps_params.mode, enb_hardpps);
+ }
+#endif /* DEBUG_NCC */
+
+ return (1);
+}
+
+/*
+ * This function is called every 64 seconds from ripencc_receive
+ * It will fetch the pps time
+ *
+ * Return 0 on failure and 1 on success.
+ */
+static int
+ripencc_get_pps_ts(
+ struct ripencc_unit *up,
+ l_fp *tsptr
+ )
+{
+ pps_info_t pps_info;
+ struct timespec timeout, ts;
+ double dtemp;
+ l_fp tstmp;
+
+#ifdef DEBUG_PPS
+ msyslog(LOG_INFO,"ripencc_get_pps_ts\n");
+#endif /* DEBUG_PPS */
+
+
+ /*
+ * Convert the timespec nanoseconds field to ntp l_fp units.
+ */
+ if (up->handle == 0)
+ return (0);
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
+ if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
+ &timeout) < 0)
+ return (0);
+ if (up->pps_params.mode & PPS_CAPTUREASSERT) {
+ if (pps_info.assert_sequence ==
+ up->pps_info.assert_sequence)
+ return (0);
+ ts = up->pps_info.assert_timestamp;
+ } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
+ if (pps_info.clear_sequence ==
+ up->pps_info.clear_sequence)
+ return (0);
+ ts = up->pps_info.clear_timestamp;
+ } else {
+ return (0);
+ }
+ if ((up->ts.tv_sec == ts.tv_sec) && (up->ts.tv_nsec == ts.tv_nsec))
+ return (0);
+ up->ts = ts;
+
+ tstmp.l_ui = ts.tv_sec + JAN_1970;
+ dtemp = ts.tv_nsec * FRAC / 1e9;
+ tstmp.l_uf = (u_int32)dtemp;
+
+#ifdef DEBUG_PPS
+ msyslog(LOG_INFO,"ts.tv_sec: %d\n",(int)ts.tv_sec);
+ msyslog(LOG_INFO,"ts.tv_nsec: %ld\n",ts.tv_nsec);
+#endif /* DEBUG_PPS */
+
+ *tsptr = tstmp;
+ return (1);
+}
+
+/*
+ * ripencc_shutdown - shut down a GPS clock
+ */
+static void
+ripencc_shutdown(int unit, struct peer *peer)
+{
+ register struct ripencc_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+
+ if (up->handle != 0)
+ time_pps_destroy(up->handle);
+
+ io_closeclock(&pp->io);
+
+ free(up);
+}
+
+/*
+ * ripencc_poll - called by the transmit procedure
+ */
+static void
+ripencc_poll(int unit, struct peer *peer)
+{
+ register struct ripencc_unit *up;
+ struct refclockproc *pp;
+ TSIPPKT spt;
+
+#ifdef DEBUG_NCC
+ if (debug)
+ fprintf(stderr, "ripencc_poll(%d)\n", unit);
+#endif /* DEBUG_NCC */
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+
+ pp->polls++;
+ up->polled = 1;
+
+ /* poll for UTC superpacket */
+ cmd_0x8EADq (&spt);
+ ripencc_send(peer,spt);
+}
+
+/*
+ * ripencc_send - send message to clock
+ * use the structures being created by the trimble functions!
+ * makes the code more readable/clean
+ */
+static void
+ripencc_send(struct peer *peer, TSIPPKT spt)
+{
+ unsigned char *ip, *op;
+ unsigned char obuf[512];
+
+#ifdef DEBUG_RAW
+ {
+ register struct ripencc_unit *up;
+ register struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+ if (debug)
+ printf("ripencc_send(%d, %02X)\n", up->unit, cmd);
+ }
+#endif /* DEBUG_RAW */
+
+ ip = spt.buf;
+ op = obuf;
+
+ *op++ = 0x10;
+ *op++ = spt.code;
+
+ while (spt.len--) {
+ if (op-obuf > sizeof(obuf)-5) {
+ msyslog(LOG_ERR, "ripencc_send obuf overflow!");
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+
+ if (*ip == 0x10) /* byte stuffing */
+ *op++ = 0x10;
+ *op++ = *ip++;
+ }
+
+ *op++ = 0x10;
+ *op++ = 0x03;
+
+#ifdef DEBUG_RAW
+ if (debug) { /* print raw packet */
+ unsigned char *cp;
+ int i;
+
+ printf("ripencc_send: len %d\n", op-obuf);
+ for (i=1, cp=obuf; cp<op; i++, cp++) {
+ printf(" %02X", *cp);
+ if (i%10 == 0)
+ printf("\n");
+ }
+ printf("\n");
+ }
+#endif /* DEBUG_RAW */
+
+ if (write(peer->procptr->io.fd, obuf, op-obuf) == -1) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+/*
+ * ripencc_receive()
+ *
+ * called when a packet is received on the serial port
+ * takes care of further processing
+ *
+ */
+static void
+ripencc_receive(struct recvbuf *rbufp)
+{
+ register struct ripencc_unit *up;
+ register struct refclockproc *pp;
+ struct peer *peer;
+ static TSIPPKT rpt; /* structure for current incoming TSIP report */
+ TSIPPKT spt; /* send packet */
+ int ns_since_pps;
+ int i;
+ char *cp;
+ /* Use these variables to hold data until we decide its worth keeping */
+ char rd_lastcode[BMAX];
+ l_fp rd_tmp;
+ u_short rd_lencode;
+
+ /* msyslog(LOG_INFO, "%s",__FUNCTION__); */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct ripencc_unit *)pp->unitptr;
+ rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
+
+#ifdef DEBUG_RAW
+ if (debug)
+ fprintf(stderr, "ripencc_receive(%d)\n", up->unit);
+#endif /* DEBUG_RAW */
+
+#ifdef DEBUG_RAW
+ if (debug) { /* print raw packet */
+ int i;
+ unsigned char *cp;
+
+ printf("ripencc_receive: len %d\n", rbufp->recv_length);
+ for (i=1, cp=(char*)&rbufp->recv_space; i <= rbufp->recv_length; i++, cp++) {
+ printf(" %02X", *cp);
+ if (i%10 == 0)
+ printf("\n");
+ }
+ printf("\n");
+ }
+#endif /* DEBUG_RAW */
+
+ cp = (char*) &rbufp->recv_space;
+ i=rbufp->recv_length;
+
+ while (i--) { /* loop over received chars */
+
+ tsip_input_proc(&rpt, (unsigned char) *cp++);
+
+ if (rpt.status != TSIP_PARSED_FULL)
+ continue;
+
+ switch (rpt.code) {
+
+ case 0x8F: /* superpacket */
+
+ switch (rpt.buf[0]) {
+
+ case 0xAD: /* UTC Time */
+ /*
+ * When polling on port B the timecode
+ * is the time of the previous PPS.
+ * If we completed receiving the packet
+ * less than 150ms after the turn of the second,
+ * it may have the code of the previous second.
+ * We do not trust that and simply poll again
+ * without even parsing it.
+ *
+ * More elegant would be to re-schedule the poll,
+ * but I do not know (yet) how to do that cleanly.
+ *
+ */
+ /* BLA ns_since_pps = ncc_tstmp(rbufp, &trtmp); */
+/* if (up->polled && ns_since_pps > -1 && ns_since_pps < 150) { */
+
+ ns_since_pps=200;
+ if (up->polled && ns_since_pps < 150) {
+ msyslog(LOG_INFO, "%s(): up->polled",__FUNCTION__);
+ ripencc_poll(up->unit, peer);
+ break;
+ }
+
+ /*
+ * Parse primary utc time packet
+ * and fill refclock structure
+ * from results.
+ */
+ if (parse0x8FAD(&rpt, peer) < 0) {
+ msyslog(LOG_INFO, "%s(): parse0x8FAD < 0",__FUNCTION__);
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ /*
+ * If the PPSAPI is working, rather use its
+ * timestamps.
+ * assume that the PPS occurs on the second
+ * so blow any msec
+ */
+ if (ripencc_get_pps_ts(up, &rd_tmp) == 1) {
+ pp->lastrec = up->tstamp = rd_tmp;
+ pp->nsec = 0;
+ }
+ else
+ msyslog(LOG_INFO, "%s(): ripencc_get_pps_ts returns failure\n",__FUNCTION__);
+
+
+ if (!up->polled) {
+ msyslog(LOG_INFO, "%s(): unrequested packet\n",__FUNCTION__);
+ /* unrequested packet */
+ break;
+ }
+
+ /* we have been polled ! */
+ up->polled = 0;
+ up->pollcnt = 2;
+
+ /* poll for next packet */
+ cmd_0x8E0Bq(&spt);
+ ripencc_send(peer,spt);
+
+ if (ns_since_pps < 0) { /* no PPS */
+ msyslog(LOG_INFO, "%s(): ns_since_pps < 0",__FUNCTION__);
+ refclock_report(peer, CEVNT_BADTIME);
+ break;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion.
+ */
+ if (!refclock_process(pp)) {
+ msyslog(LOG_INFO, "%s(): !refclock_process",__FUNCTION__);
+ refclock_report(peer, CEVNT_BADTIME);
+ break;
+ }
+
+ refclock_receive(peer);
+ break;
+
+ case 0x0B: /* comprehensive time packet */
+ parse0x8F0B(&rpt, peer);
+ break;
+
+ default: /* other superpackets */
+#ifdef DEBUG_NCC
+ msyslog(LOG_INFO, "%s(): calling parseany",__FUNCTION__);
+#endif /* DEBUG_NCC */
+#ifdef TRIMBLE_OUTPUT_FUNC
+ parseany(&rpt, peer);
+#endif /* TRIMBLE_OUTPUT_FUNC */
+ break;
+ }
+ break;
+
+ case 0x4F: /* UTC parameters, for leap info */
+ parse0x4F(&rpt, peer);
+ break;
+
+ case 0x5C: /* sat tracking data */
+ parse0x5C(&rpt, peer);
+ break;
+
+ default: /* other packets */
+#ifdef TRIMBLE_OUTPUT_FUNC
+ parseany(&rpt, peer);
+#endif /* TRIMBLE_OUTPUT_FUNC */
+ break;
+ }
+ rpt.status = TSIP_PARSED_EMPTY;
+ }
+}
+
+/*
+ * All trimble functions that are directly referenced from driver code
+ * (so not from parseany)
+ */
+
+void cmd_0x1F (TSIPPKT *cmd)
+/* request software versions */
+{
+ cmd->len = 0;
+ cmd->code = 0x1F;
+}
+
+void cmd_0x26 (TSIPPKT *cmd)
+/* request receiver health */
+{
+ cmd->len = 0;
+ cmd->code = 0x26;
+}
+
+
+
+
+void cmd_0x2F (TSIPPKT *cmd)
+/* request UTC params */
+{
+ cmd->len = 0;
+ cmd->code = 0x2F;
+}
+
+void cmd_0x35s (TSIPPKT *cmd, unsigned char pos_code, unsigned char vel_code,
+ unsigned char time_code, unsigned char opts_code)
+/* set serial I/O options */
+{
+ cmd->buf[0] = pos_code;
+ cmd->buf[1] = vel_code;
+ cmd->buf[2] = time_code;
+ cmd->buf[3] = opts_code;
+ cmd->len = 4;
+ cmd->code = 0x35;
+}
+void cmd_0x3C (TSIPPKT *cmd, unsigned char sv_prn)
+/* request tracking status */
+{
+ cmd->buf[0] = sv_prn;
+ cmd->len = 1;
+ cmd->code = 0x3C;
+}
+
+
+void cmd_0x3Ds (TSIPPKT *cmd,
+ unsigned char baud_out, unsigned char baud_inp,
+ unsigned char char_code, unsigned char stopbitcode,
+ unsigned char output_mode, unsigned char input_mode)
+/* set Channel A configuration for dual-port operation */
+{
+ cmd->buf[0] = baud_out; /* XMT baud rate */
+ cmd->buf[1] = baud_inp; /* RCV baud rate */
+ cmd->buf[2] = char_code; /* parity and #bits per byte */
+ cmd->buf[3] = stopbitcode; /* number of stop bits code */
+ cmd->buf[4] = output_mode; /* Ch. A transmission mode */
+ cmd->buf[5] = input_mode; /* Ch. A reception mode */
+ cmd->len = 6;
+ cmd->code = 0x3D;
+}
+
+
+/* query primary configuration */
+void cmd_0xBBq (TSIPPKT *cmd,
+ unsigned char subcode)
+{
+
+ cmd->len = 1;
+ cmd->code = 0xBB;
+ cmd->buf[0] = subcode;
+}
+
+
+/**** Superpackets ****/
+void cmd_0x8E0Bq (TSIPPKT *cmd)
+/* 8E-0B to query 8F-0B controls */
+{
+
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x0B;
+}
+
+
+void cmd_0x8E41q (TSIPPKT *cmd)
+/* 8F-41 to query board serial number */
+{
+
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x41;
+}
+
+
+void cmd_0x8E42q (TSIPPKT *cmd)
+/* 8F-42 to query product serial number */
+{
+
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x42;
+}
+void cmd_0x8E4Aq (TSIPPKT *cmd)
+/* 8F-4A to query PPS parameters */
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4A;
+}
+
+
+/* set i/o options */
+void cmd_0x8E4As (TSIPPKT *cmd,
+ unsigned char PPSOnOff,
+ unsigned char TimeBase,
+ unsigned char Polarity,
+ double PPSOffset,
+ float Uncertainty)
+{
+ cmd->len = 16;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4A;
+ cmd->buf[1] = PPSOnOff;
+ cmd->buf[2] = TimeBase;
+ cmd->buf[3] = Polarity;
+ bPutDouble (&PPSOffset, &cmd->buf[4]);
+ bPutFloat (&Uncertainty, &cmd->buf[12]);
+}
+void cmd_0x8E4Bq (TSIPPKT *cmd)
+/* 8F-4B query survey limit */
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4B;
+}
+
+
+/* poll for UTC superpacket */
+void cmd_0x8EADq (TSIPPKT *cmd)
+/* 8E-AD to query 8F-AD controls */
+{
+ cmd->len = 1;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0xAD;
+}
+
+/* all outomatic packet output off */
+void cmd_0x8E4Ds (TSIPPKT *cmd,
+ unsigned long AutoOutputMask)
+{
+ cmd->len = 5;
+ cmd->code = 0x8E;
+ cmd->buf[0] = 0x4D;
+ bPutULong (&AutoOutputMask, &cmd->buf[1]);
+}
+
+
+
+
+/* for DOS machines, reverse order of bytes as they come through the
+ * serial port. */
+#ifdef BYTESWAP
+static short bGetShort (unsigned char *bp)
+{
+ short outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 1;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+static unsigned short bGetUShort (unsigned char *bp)
+{
+ unsigned short outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 1;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+static long bGetLong (unsigned char *bp)
+{
+ long outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 3;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+static unsigned long bGetULong (unsigned char *bp)
+{
+ unsigned long outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 3;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+static float bGetSingle (unsigned char *bp)
+{
+ float outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 3;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+static double bGetDouble (unsigned char *bp)
+{
+ double outval;
+ unsigned char *optr;
+
+ optr = (unsigned char*)&outval + 7;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr-- = *bp++;
+ *optr = *bp;
+ return outval;
+}
+
+#else /* not BYTESWAP */
+
+#define bGetShort(bp) (*(short*)(bp))
+#define bGetLong(bp) (*(long*)(bp))
+#define bGetULong(bp) (*(unsigned long*)(bp))
+#define bGetSingle(bp) (*(float*)(bp))
+#define bGetDouble(bp) (*(double*)(bp))
+
+#endif /* BYTESWAP */
+/*
+ * Byte-reversal is necessary for little-endian (Intel-based) machines.
+ * TSIP streams are Big-endian (Motorola-based).
+ */
+#ifdef BYTESWAP
+
+void
+bPutFloat (float *in, unsigned char *out)
+{
+ unsigned char *inptr;
+
+ inptr = (unsigned char*)in + 3;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out = *inptr;
+}
+
+static void
+bPutULong (unsigned long *in, unsigned char *out)
+{
+ unsigned char *inptr;
+
+ inptr = (unsigned char*)in + 3;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out = *inptr;
+}
+
+static void
+bPutDouble (double *in, unsigned char *out)
+{
+ unsigned char *inptr;
+
+ inptr = (unsigned char*)in + 7;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out++ = *inptr--;
+ *out = *inptr;
+}
+
+#else /* not BYTESWAP */
+
+void bPutShort (short a, unsigned char *cmdbuf) {*(short*) cmdbuf = a;}
+void bPutULong (long a, unsigned char *cmdbuf) {*(long*) cmdbuf = a;}
+void bPutFloat (float a, unsigned char *cmdbuf) {*(float*) cmdbuf = a;}
+void bPutDouble (double a, unsigned char *cmdbuf){*(double*) cmdbuf = a;}
+
+#endif /* BYTESWAP */
+
+/*
+ * Parse primary utc time packet
+ * and fill refclock structure
+ * from results.
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+static int
+parse0x8FAD(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ register struct refclockproc *pp;
+ register struct ripencc_unit *up;
+
+ unsigned day, month, year; /* data derived from received timecode */
+ unsigned hour, minute, second;
+ unsigned char trackstat, utcflags;
+
+ static char logbuf[1024]; /* logging string buffer */
+ int i;
+ unsigned char *buf;
+
+ buf = rpt->buf;
+ pp = peer->procptr;
+
+ if (rpt->len != 22)
+ return (-1);
+
+ if (bGetShort(&buf[1]) != 0) {
+#ifdef DEBUG_NCC
+ if (debug)
+ printf("parse0x8FAD: event count != 0\n");
+#endif /* DEBUG_NCC */
+ return(-1);
+ }
+
+
+ if (bGetDouble(&buf[3]) != 0.0) {
+#ifdef DEBUG_NCC
+ if (debug)
+ printf("parse0x8FAD: fracsecs != 0\n");
+#endif /* DEBUG_NCC */
+ return(-1);
+ }
+
+ hour = (unsigned int) buf[11];
+ minute = (unsigned int) buf[12];
+ second = (unsigned int) buf[13];
+ day = (unsigned int) buf[14];
+ month = (unsigned int) buf[15];
+ year = bGetShort(&buf[16]);
+ trackstat = buf[18];
+ utcflags = buf[19];
+
+
+ sprintf(logbuf, "U1 %d.%d.%d %02d:%02d:%02d %d %02x",
+ day, month, year, hour, minute, second, trackstat, utcflags);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ if (!utcflags & UTCF_UTC_AVAIL)
+ return(-1);
+
+ /* poll for UTC parameters once and then if UTC flag changed */
+ up = (struct ripencc_unit *) pp->unitptr;
+ if (utcflags != up->utcflags) {
+ TSIPPKT spt; /* local structure for send packet */
+ cmd_0x2F (&spt); /* request UTC params */
+ ripencc_send(peer,spt);
+ up->utcflags = utcflags;
+ }
+
+ /*
+ * If we hit the leap second, we choose to skip this sample
+ * rather than rely on other code to be perfectly correct.
+ * No offense, just defense ;-).
+ */
+ if (second == 60)
+ return(-1);
+
+ /* now check and convert the time we received */
+
+ pp->year = year;
+ if (month < 1 || month > 12 || day < 1 || day > 31)
+ return(-1);
+
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1])
+ return(-1);
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1])
+ return(-1);
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+ pp->hour = hour;
+ pp->minute = minute;
+ pp-> second = second;
+ pp->nsec = 0;
+
+ if ((utcflags&UTCF_LEAP_PNDG) && up->leapdelta != 0)
+ pp-> leap = (up->leapdelta > 0 ? LEAP_ADDSECOND : LEAP_DELSECOND);
+ else
+ pp-> leap = LEAP_NOWARNING;
+
+ return (0);
+}
+
+/*
+ * Parse comprehensive time packet
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+int parse0x8F0B(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ register struct refclockproc *pp;
+
+ unsigned day, month, year; /* data derived from received timecode */
+ unsigned hour, minute, second;
+ unsigned utcoff;
+ unsigned char mode;
+ double bias, rate;
+ float biasunc, rateunc;
+ double lat, lon, alt;
+ short lat_deg, lon_deg;
+ float lat_min, lon_min;
+ unsigned char north_south, east_west;
+ char sv[9];
+
+ static char logbuf[1024]; /* logging string buffer */
+ unsigned char b;
+ int i;
+ unsigned char *buf;
+ double tow;
+
+ buf = rpt->buf;
+ pp = peer->procptr;
+
+ if (rpt->len != 74)
+ return (-1);
+
+ if (bGetShort(&buf[1]) != 0)
+ return(-1);;
+
+ tow = bGetDouble(&buf[3]);
+
+ if (tow == -1.0) {
+ return(-1);
+ }
+ else if ((tow >= 604800.0) || (tow < 0.0)) {
+ return(-1);
+ }
+ else
+ {
+ if (tow < 604799.9) tow = tow + .00000001;
+ second = (unsigned int) fmod(tow, 60.);
+ minute = (unsigned int) fmod(tow/60., 60.);
+ hour = (unsigned int )fmod(tow / 3600., 24.);
+ }
+
+
+ day = (unsigned int) buf[11];
+ month = (unsigned int) buf[12];
+ year = bGetShort(&buf[13]);
+ mode = buf[15];
+ utcoff = bGetShort(&buf[16]);
+ bias = bGetDouble(&buf[18]) / GPS_C * 1e9; /* ns */
+ rate = bGetDouble(&buf[26]) / GPS_C * 1e9; /* ppb */
+ biasunc = bGetSingle(&buf[34]) / GPS_C * 1e9; /* ns */
+ rateunc = bGetSingle(&buf[38]) / GPS_C * 1e9; /* ppb */
+ lat = bGetDouble(&buf[42]) * R2D;
+ lon = bGetDouble(&buf[50]) * R2D;
+ alt = bGetDouble(&buf[58]);
+
+ if (lat < 0.0) {
+ north_south = 'S';
+ lat = -lat;
+ }
+ else {
+ north_south = 'N';
+ }
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+
+ if (lon < 0.0) {
+ east_west = 'W';
+ lon = -lon;
+ }
+ else {
+ east_west = 'E';
+ }
+
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+
+ for (i=0; i<8; i++) {
+ sv[i] = buf[i + 66];
+ if (sv[i]) {
+ TSIPPKT spt; /* local structure for sendpacket */
+ b = (unsigned char) (sv[i]<0 ? -sv[i] : sv[i]);
+ /* request tracking status */
+ cmd_0x3C (&spt, b);
+ ripencc_send(peer,spt);
+ }
+ }
+
+
+ sprintf(logbuf, "C1 %02d%02d%04d %02d%02d%02d %d %7.0f %.1f %.0f %.1f %d %02d%09.6f %c %02d%09.6f %c %.0f %d %d %d %d %d %d %d %d",
+ day, month, year, hour, minute, second, mode, bias, biasunc, rate, rateunc, utcoff,
+ lat_deg, lat_min, north_south, lon_deg, lon_min, east_west, alt,
+ sv[0], sv[1], sv[2], sv[3], sv[4], sv[5], sv[6], sv[7]);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ return (0);
+}
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+/*
+ * Parse any packet using Trimble machinery
+ */
+int parseany(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ static char logbuf[1024]; /* logging string buffer */
+
+ TranslateTSIPReportToText (rpt, logbuf); /* anything else */
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(&logbuf[1]);
+#endif /* DEBUG_NCC */
+ record_clock_stats(&peer->srcadr, &logbuf[1]);
+ return(0);
+}
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+
+/*
+ * Parse UTC Parameter Packet
+ *
+ * See the IDE for documentation!
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+int parse0x4F(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ register struct ripencc_unit *up;
+
+ double a0;
+ float a1, tot;
+ int dt_ls, wn_t, wn_lsf, dn, dt_lsf;
+
+ static char logbuf[1024]; /* logging string buffer */
+ unsigned char *buf;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 26)
+ return (-1);
+ a0 = bGetDouble (buf);
+ a1 = bGetSingle (&buf[8]);
+ dt_ls = bGetShort (&buf[12]);
+ tot = bGetSingle (&buf[14]);
+ wn_t = bGetShort (&buf[18]);
+ wn_lsf = bGetShort (&buf[20]);
+ dn = bGetShort (&buf[22]);
+ dt_lsf = bGetShort (&buf[24]);
+
+ sprintf(logbuf, "L1 %d %d %d %g %g %g %d %d %d",
+ dt_lsf - dt_ls, dt_ls, dt_lsf, a0, a1, tot, wn_t, wn_lsf, dn);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ up = (struct ripencc_unit *) peer->procptr->unitptr;
+ up->leapdelta = dt_lsf - dt_ls;
+
+ return (0);
+}
+
+/*
+ * Parse Tracking Status packet
+ *
+ * 0 = success
+ * -1 = errors
+ */
+
+int parse0x5C(rpt, peer)
+ TSIPPKT *rpt;
+ struct peer *peer;
+{
+ unsigned char prn, channel, aqflag, ephstat;
+ float snr, azinuth, elevation;
+
+ static char logbuf[1024]; /* logging string buffer */
+ unsigned char *buf;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 24)
+ return(-1);
+
+ prn = buf[0];
+ channel = (unsigned char)(buf[1] >> 3);
+ if (channel == 0x10)
+ channel = 2;
+ else
+ channel++;
+ aqflag = buf[2];
+ ephstat = buf[3];
+ snr = bGetSingle(&buf[4]);
+ elevation = bGetSingle(&buf[12]) * R2D;
+ azinuth = bGetSingle(&buf[16]) * R2D;
+
+ sprintf(logbuf, "S1 %02d %d %d %02x %4.1f %5.1f %4.1f",
+ prn, channel, aqflag, ephstat, snr, azinuth, elevation);
+
+#ifdef DEBUG_NCC
+ if (debug)
+ puts(logbuf);
+#endif /* DEBUG_NCC */
+
+ record_clock_stats(&peer->srcadr, logbuf);
+
+ return (0);
+}
+
+/******* Code below is from Trimble Tsipchat *************/
+
+/*
+ * *************************************************************************
+ *
+ * Trimble Navigation, Ltd.
+ * OEM Products Development Group
+ * P.O. Box 3642
+ * 645 North Mary Avenue
+ * Sunnyvale, California 94088-3642
+ *
+ * Corporate Headquarter:
+ * Telephone: (408) 481-8000
+ * Fax: (408) 481-6005
+ *
+ * Technical Support Center:
+ * Telephone: (800) 767-4822 (U.S. and Canada)
+ * (408) 481-6940 (outside U.S. and Canada)
+ * Fax: (408) 481-6020
+ * BBS: (408) 481-7800
+ * e-mail: trimble_support@trimble.com
+ * ftp://ftp.trimble.com/pub/sct/embedded/bin
+ *
+ * *************************************************************************
+ *
+ * ------- BYTE-SWAPPING -------
+ * TSIP is big-endian (Motorola) protocol. To use on little-endian (Intel)
+ * systems, the bytes of all multi-byte types (shorts, floats, doubles, etc.)
+ * must be reversed. This is controlled by the MACRO BYTESWAP; if defined, it
+ * assumes little-endian protocol.
+ * --------------------------------
+ *
+ * T_PARSER.C and T_PARSER.H contains primitive functions that interpret
+ * reports received from the receiver. A second source file pair,
+ * T_FORMAT.C and T_FORMAT.H, contin the matching TSIP command formatters.
+ *
+ * The module is in very portable, basic C language. It can be used as is, or
+ * with minimal changes if a TSIP communications application is needed separate
+ * from TSIPCHAT. The construction of most argument lists avoid the use of
+ * structures, but the developer is encouraged to reconstruct them using such
+ * definitions to meet project requirements. Declarations of T_PARSER.C
+ * functions are included in T_PARSER.H to provide prototyping definitions.
+ *
+ * There are two types of functions: a serial input processing routine,
+ * tsip_input_proc()
+ * which assembles incoming bytes into a TSIPPKT structure, and the
+ * report parsers, rpt_0x??().
+ *
+ * 1) The function tsip_input_proc() accumulates bytes from the receiver,
+ * strips control bytes (DLE), and checks if the report end sequence (DLE ETX)
+ * has been received. rpt.status is defined as TSIP_PARSED_FULL (== 1)
+ * if a complete packet is available.
+ *
+ * 2) The functions rpt_0x??() are report string interpreters patterned after
+ * the document called "Trimble Standard Interface Protocol". It should be
+ * noted that if the report buffer is sent into the receiver with the wrong
+ * length (byte count), the rpt_0x??() returns the Boolean equivalence for
+ * TRUE.
+ *
+ * *************************************************************************
+ *
+ */
+
+
+/**/
+static void tsip_input_proc (
+ TSIPPKT *rpt,
+ int inbyte)
+/* reads bytes until serial buffer is empty or a complete report
+ * has been received; end of report is signified by DLE ETX.
+ */
+{
+ unsigned char newbyte;
+
+ if (inbyte < 0 || inbyte > 0xFF) return;
+
+ newbyte = (unsigned char)(inbyte);
+ switch (rpt->status)
+ {
+ case TSIP_PARSED_DLE_1:
+ switch (newbyte)
+ {
+ case 0:
+ case ETX:
+ /* illegal TSIP IDs */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_EMPTY;
+ break;
+ case DLE:
+ /* try normal message start again */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DLE_1;
+ break;
+ default:
+ /* legal TSIP ID; start message */
+ rpt->code = newbyte;
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DATA;
+ break;
+ }
+ break;
+ case TSIP_PARSED_DATA:
+ switch (newbyte) {
+ case DLE:
+ /* expect DLE or ETX next */
+ rpt->status = TSIP_PARSED_DLE_2;
+ break;
+ default:
+ /* normal data byte */
+ rpt->buf[rpt->len] = newbyte;
+ rpt->len++;
+ /* no change in rpt->status */
+ break;
+ }
+ break;
+ case TSIP_PARSED_DLE_2:
+ switch (newbyte) {
+ case DLE:
+ /* normal data byte */
+ rpt->buf[rpt->len] = newbyte;
+ rpt->len++;
+ rpt->status = TSIP_PARSED_DATA;
+ break;
+ case ETX:
+ /* end of message; return TRUE here. */
+ rpt->status = TSIP_PARSED_FULL;
+ break;
+ default:
+ /* error: treat as TSIP_PARSED_DLE_1; start new report packet */
+ rpt->code = newbyte;
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DATA;
+ }
+ break;
+ case TSIP_PARSED_FULL:
+ case TSIP_PARSED_EMPTY:
+ default:
+ switch (newbyte) {
+ case DLE:
+ /* normal message start */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_DLE_1;
+ break;
+ default:
+ /* error: ignore newbyte */
+ rpt->len = 0;
+ rpt->status = TSIP_PARSED_EMPTY;
+ }
+ break;
+ }
+ if (rpt->len > MAX_RPTBUF) {
+ /* error: start new report packet */
+ rpt->status = TSIP_PARSED_EMPTY;
+ rpt->len = 0;
+ }
+}
+
+#ifdef TRIMBLE_OUTPUT_FUNC
+
+/**/
+short rpt_0x3D (TSIPPKT *rpt,
+ unsigned char *tx_baud_index,
+ unsigned char *rx_baud_index,
+ unsigned char *char_format_index,
+ unsigned char *stop_bits,
+ unsigned char *tx_mode_index,
+ unsigned char *rx_mode_index)
+/* Channel A configuration for dual port operation */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 6) return TRUE;
+ *tx_baud_index = buf[0];
+ *rx_baud_index = buf[1];
+ *char_format_index = buf[2];
+ *stop_bits = (unsigned char)((buf[3] == 0x07) ? 1 : 2);
+ *tx_mode_index = buf[4];
+ *rx_mode_index = buf[5];
+ return FALSE;
+}
+
+/**/
+short rpt_0x40 (TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ short *week_num,
+ float *t_zc,
+ float *eccentricity,
+ float *t_oa,
+ float *i_0,
+ float *OMEGA_dot,
+ float *sqrt_A,
+ float *OMEGA_0,
+ float *omega,
+ float *M_0)
+/* almanac data for specified satellite */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 39) return TRUE;
+ *sv_prn = buf[0];
+ *t_zc = bGetSingle (&buf[1]);
+ *week_num = bGetShort (&buf[5]);
+ *eccentricity = bGetSingle (&buf[7]);
+ *t_oa = bGetSingle (&buf[11]);
+ *i_0 = bGetSingle (&buf[15]);
+ *OMEGA_dot = bGetSingle (&buf[19]);
+ *sqrt_A = bGetSingle (&buf[23]);
+ *OMEGA_0 = bGetSingle (&buf[27]);
+ *omega = bGetSingle (&buf[31]);
+ *M_0 = bGetSingle (&buf[35]);
+ return FALSE;
+}
+
+short rpt_0x41 (TSIPPKT *rpt,
+ float *time_of_week,
+ float *UTC_offset,
+ short *week_num)
+/* GPS time */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 10) return TRUE;
+ *time_of_week = bGetSingle (buf);
+ *week_num = bGetShort (&buf[4]);
+ *UTC_offset = bGetSingle (&buf[6]);
+ return FALSE;
+}
+
+short rpt_0x42 (TSIPPKT *rpt,
+ float pos_ECEF[3],
+ float *time_of_fix)
+/* position in ECEF, single precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 16) return TRUE;
+ pos_ECEF[0] = bGetSingle (buf);
+ pos_ECEF[1]= bGetSingle (&buf[4]);
+ pos_ECEF[2]= bGetSingle (&buf[8]);
+ *time_of_fix = bGetSingle (&buf[12]);
+ return FALSE;
+}
+
+short rpt_0x43 (TSIPPKT *rpt,
+ float ECEF_vel[3],
+ float *freq_offset,
+ float *time_of_fix)
+/* velocity in ECEF, single precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 20) return TRUE;
+ ECEF_vel[0] = bGetSingle (buf);
+ ECEF_vel[1] = bGetSingle (&buf[4]);
+ ECEF_vel[2] = bGetSingle (&buf[8]);
+ *freq_offset = bGetSingle (&buf[12]);
+ *time_of_fix = bGetSingle (&buf[16]);
+ return FALSE;
+}
+
+short rpt_0x45 (TSIPPKT *rpt,
+ unsigned char *major_nav_version,
+ unsigned char *minor_nav_version,
+ unsigned char *nav_day,
+ unsigned char *nav_month,
+ unsigned char *nav_year,
+ unsigned char *major_dsp_version,
+ unsigned char *minor_dsp_version,
+ unsigned char *dsp_day,
+ unsigned char *dsp_month,
+ unsigned char *dsp_year)
+/* software versions */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 10) return TRUE;
+ *major_nav_version = buf[0];
+ *minor_nav_version = buf[1];
+ *nav_day = buf[2];
+ *nav_month = buf[3];
+ *nav_year = buf[4];
+ *major_dsp_version = buf[5];
+ *minor_dsp_version = buf[6];
+ *dsp_day = buf[7];
+ *dsp_month = buf[8];
+ *dsp_year = buf[9];
+ return FALSE;
+}
+
+short rpt_0x46 (TSIPPKT *rpt,
+ unsigned char *status1,
+ unsigned char *status2)
+/* receiver health and status */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 2) return TRUE;
+ *status1 = buf[0];
+ *status2 = buf[1];
+ return FALSE;
+}
+
+short rpt_0x47 (TSIPPKT *rpt,
+ unsigned char *nsvs, unsigned char *sv_prn,
+ float *snr)
+/* signal levels for all satellites tracked */
+{
+ short isv;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 1 + 5*buf[0]) return TRUE;
+ *nsvs = buf[0];
+ for (isv = 0; isv < (*nsvs); isv++) {
+ sv_prn[isv] = buf[5*isv + 1];
+ snr[isv] = bGetSingle (&buf[5*isv + 2]);
+ }
+ return FALSE;
+}
+
+short rpt_0x48 (TSIPPKT *rpt,
+ unsigned char *message)
+/* GPS system message */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 22) return TRUE;
+ memcpy (message, buf, 22);
+ message[22] = 0;
+ return FALSE;
+}
+
+short rpt_0x49 (TSIPPKT *rpt,
+ unsigned char *sv_health)
+/* health for all satellites from almanac health page */
+{
+ short i;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 32) return TRUE;
+ for (i = 0; i < 32; i++) sv_health [i]= buf[i];
+ return FALSE;
+}
+
+short rpt_0x4A (TSIPPKT *rpt,
+ float *lat,
+ float *lon,
+ float *alt,
+ float *clock_bias,
+ float *time_of_fix)
+/* position in lat-lon-alt, single precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 20) return TRUE;
+ *lat = bGetSingle (buf);
+ *lon = bGetSingle (&buf[4]);
+ *alt = bGetSingle (&buf[8]);
+ *clock_bias = bGetSingle (&buf[12]);
+ *time_of_fix = bGetSingle (&buf[16]);
+ return FALSE;
+}
+
+short rpt_0x4A_2 (TSIPPKT *rpt,
+ float *alt, float *dummy , unsigned char *alt_flag)
+/* reference altitude parameters */
+{
+ unsigned char *buf;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 9) return TRUE;
+ *alt = bGetSingle (buf);
+ *dummy = bGetSingle (&buf[4]);
+ *alt_flag = buf[8];
+ return FALSE;
+}
+
+short rpt_0x4B (TSIPPKT *rpt,
+ unsigned char *machine_id,
+ unsigned char *status3,
+ unsigned char *status4)
+/* machine ID code, status */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 3) return TRUE;
+ *machine_id = buf[0];
+ *status3 = buf[1];
+ *status4 = buf[2];
+ return FALSE;
+}
+
+short rpt_0x4C (TSIPPKT *rpt,
+ unsigned char *dyn_code,
+ float *el_mask,
+ float *snr_mask,
+ float *dop_mask,
+ float *dop_switch)
+/* operating parameters and masks */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 17) return TRUE;
+ *dyn_code = buf[0];
+ *el_mask = bGetSingle (&buf[1]);
+ *snr_mask = bGetSingle (&buf[5]);
+ *dop_mask = bGetSingle (&buf[9]);
+ *dop_switch = bGetSingle (&buf[13]);
+ return FALSE;
+}
+
+short rpt_0x4D (TSIPPKT *rpt,
+ float *osc_offset)
+/* oscillator offset */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 4) return TRUE;
+ *osc_offset = bGetSingle (buf);
+ return FALSE;
+}
+
+short rpt_0x4E (TSIPPKT *rpt,
+ unsigned char *response)
+/* yes/no response to command to set GPS time */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 1) return TRUE;
+ *response = buf[0];
+ return FALSE;
+}
+
+short rpt_0x4F (TSIPPKT *rpt,
+ double *a0,
+ float *a1,
+ float *time_of_data,
+ short *dt_ls,
+ short *wn_t,
+ short *wn_lsf,
+ short *dn,
+ short *dt_lsf)
+/* UTC data */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 26) return TRUE;
+ *a0 = bGetDouble (buf);
+ *a1 = bGetSingle (&buf[8]);
+ *dt_ls = bGetShort (&buf[12]);
+ *time_of_data = bGetSingle (&buf[14]);
+ *wn_t = bGetShort (&buf[18]);
+ *wn_lsf = bGetShort (&buf[20]);
+ *dn = bGetShort (&buf[22]);
+ *dt_lsf = bGetShort (&buf[24]);
+ return FALSE;
+}
+
+/**/
+short rpt_0x54 (TSIPPKT *rpt,
+ float *clock_bias,
+ float *freq_offset,
+ float *time_of_fix)
+/* clock offset and frequency offset in 1-SV (0-D) mode */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 12) return TRUE;
+ *clock_bias = bGetSingle (buf);
+ *freq_offset = bGetSingle (&buf[4]);
+ *time_of_fix = bGetSingle (&buf[8]);
+ return FALSE;
+}
+
+short rpt_0x55 (TSIPPKT *rpt,
+ unsigned char *pos_code,
+ unsigned char *vel_code,
+ unsigned char *time_code,
+ unsigned char *aux_code)
+/* I/O serial options */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 4) return TRUE;
+ *pos_code = buf[0];
+ *vel_code = buf[1];
+ *time_code = buf[2];
+ *aux_code = buf[3];
+ return FALSE;
+}
+
+short rpt_0x56 (TSIPPKT *rpt,
+ float vel_ENU[3], float *freq_offset, float *time_of_fix)
+/* velocity in east-north-up coordinates */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 20) return TRUE;
+ /* east */
+ vel_ENU[0] = bGetSingle (buf);
+ /* north */
+ vel_ENU[1] = bGetSingle (&buf[4]);
+ /* up */
+ vel_ENU[2] = bGetSingle (&buf[8]);
+ *freq_offset = bGetSingle (&buf[12]);
+ *time_of_fix = bGetSingle (&buf[16]);
+ return FALSE;
+}
+
+short rpt_0x57 (TSIPPKT *rpt,
+ unsigned char *source_code, unsigned char *diag_code,
+ short *week_num,
+ float *time_of_fix)
+/* info about last computed fix */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 8) return TRUE;
+ *source_code = buf[0];
+ *diag_code = buf[1];
+ *time_of_fix = bGetSingle (&buf[2]);
+ *week_num = bGetShort (&buf[6]);
+ return FALSE;
+}
+
+short rpt_0x58 (TSIPPKT *rpt,
+ unsigned char *op_code, unsigned char *data_type, unsigned char *sv_prn,
+ unsigned char *data_length, unsigned char *data_packet)
+/* GPS system data or acknowledgment of GPS system data load */
+{
+ unsigned char *buf, *buf4;
+ short dl;
+ ALM_INFO* alminfo;
+ ION_INFO* ioninfo;
+ UTC_INFO* utcinfo;
+ NAV_INFO* navinfo;
+
+ buf = rpt->buf;
+
+ if (buf[0] == 2) {
+ if (rpt->len < 4) return TRUE;
+ if (rpt->len != 4+buf[3]) return TRUE;
+ }
+ else if (rpt->len != 3) {
+ return TRUE;
+ }
+ *op_code = buf[0];
+ *data_type = buf[1];
+ *sv_prn = buf[2];
+ if (*op_code == 2) {
+ dl = buf[3];
+ *data_length = (unsigned char)dl;
+ buf4 = &buf[4];
+ switch (*data_type) {
+ case 2:
+ /* Almanac */
+ if (*data_length != sizeof (ALM_INFO)) return TRUE;
+ alminfo = (ALM_INFO*)data_packet;
+ alminfo->t_oa_raw = buf4[0];
+ alminfo->SV_health = buf4[1];
+ alminfo->e = bGetSingle(&buf4[2]);
+ alminfo->t_oa = bGetSingle(&buf4[6]);
+ alminfo->i_0 = bGetSingle(&buf4[10]);
+ alminfo->OMEGADOT = bGetSingle(&buf4[14]);
+ alminfo->sqrt_A = bGetSingle(&buf4[18]);
+ alminfo->OMEGA_0 = bGetSingle(&buf4[22]);
+ alminfo->omega = bGetSingle(&buf4[26]);
+ alminfo->M_0 = bGetSingle(&buf4[30]);
+ alminfo->a_f0 = bGetSingle(&buf4[34]);
+ alminfo->a_f1 = bGetSingle(&buf4[38]);
+ alminfo->Axis = bGetSingle(&buf4[42]);
+ alminfo->n = bGetSingle(&buf4[46]);
+ alminfo->OMEGA_n = bGetSingle(&buf4[50]);
+ alminfo->ODOT_n = bGetSingle(&buf4[54]);
+ alminfo->t_zc = bGetSingle(&buf4[58]);
+ alminfo->weeknum = bGetShort(&buf4[62]);
+ alminfo->wn_oa = bGetShort(&buf4[64]);
+ break;
+
+ case 3:
+ /* Almanac health page */
+ if (*data_length != sizeof (ALH_PARMS) + 3) return TRUE;
+
+ /* this record is returned raw */
+ memcpy (data_packet, buf4, dl);
+ break;
+
+ case 4:
+ /* Ionosphere */
+ if (*data_length != sizeof (ION_INFO) + 8) return TRUE;
+ ioninfo = (ION_INFO*)data_packet;
+ ioninfo->alpha_0 = bGetSingle (&buf4[8]);
+ ioninfo->alpha_1 = bGetSingle (&buf4[12]);
+ ioninfo->alpha_2 = bGetSingle (&buf4[16]);
+ ioninfo->alpha_3 = bGetSingle (&buf4[20]);
+ ioninfo->beta_0 = bGetSingle (&buf4[24]);
+ ioninfo->beta_1 = bGetSingle (&buf4[28]);
+ ioninfo->beta_2 = bGetSingle (&buf4[32]);
+ ioninfo->beta_3 = bGetSingle (&buf4[36]);
+ break;
+
+ case 5:
+ /* UTC */
+ if (*data_length != sizeof (UTC_INFO) + 13) return TRUE;
+ utcinfo = (UTC_INFO*)data_packet;
+ utcinfo->A_0 = bGetDouble (&buf4[13]);
+ utcinfo->A_1 = bGetSingle (&buf4[21]);
+ utcinfo->delta_t_LS = bGetShort (&buf4[25]);
+ utcinfo->t_ot = bGetSingle(&buf4[27]);
+ utcinfo->WN_t = bGetShort (&buf4[31]);
+ utcinfo->WN_LSF = bGetShort (&buf4[33]);
+ utcinfo->DN = bGetShort (&buf4[35]);
+ utcinfo->delta_t_LSF = bGetShort (&buf4[37]);
+ break;
+
+ case 6:
+ /* Ephemeris */
+ if (*data_length != sizeof (NAV_INFO) - 1) return TRUE;
+
+ navinfo = (NAV_INFO*)data_packet;
+
+ navinfo->sv_number = buf4[0];
+ navinfo->t_ephem = bGetSingle (&buf4[1]);
+ navinfo->ephclk.weeknum = bGetShort (&buf4[5]);
+
+ navinfo->ephclk.codeL2 = buf4[7];
+ navinfo->ephclk.L2Pdata = buf4[8];
+ navinfo->ephclk.SVacc_raw = buf4[9];
+ navinfo->ephclk.SV_health = buf4[10];
+ navinfo->ephclk.IODC = bGetShort (&buf4[11]);
+ navinfo->ephclk.T_GD = bGetSingle (&buf4[13]);
+ navinfo->ephclk.t_oc = bGetSingle (&buf4[17]);
+ navinfo->ephclk.a_f2 = bGetSingle (&buf4[21]);
+ navinfo->ephclk.a_f1 = bGetSingle (&buf4[25]);
+ navinfo->ephclk.a_f0 = bGetSingle (&buf4[29]);
+ navinfo->ephclk.SVacc = bGetSingle (&buf4[33]);
+
+ navinfo->ephorb.IODE = buf4[37];
+ navinfo->ephorb.fit_interval = buf4[38];
+ navinfo->ephorb.C_rs = bGetSingle (&buf4[39]);
+ navinfo->ephorb.delta_n = bGetSingle (&buf4[43]);
+ navinfo->ephorb.M_0 = bGetDouble (&buf4[47]);
+ navinfo->ephorb.C_uc = bGetSingle (&buf4[55]);
+ navinfo->ephorb.e = bGetDouble (&buf4[59]);
+ navinfo->ephorb.C_us = bGetSingle (&buf4[67]);
+ navinfo->ephorb.sqrt_A = bGetDouble (&buf4[71]);
+ navinfo->ephorb.t_oe = bGetSingle (&buf4[79]);
+ navinfo->ephorb.C_ic = bGetSingle (&buf4[83]);
+ navinfo->ephorb.OMEGA_0 = bGetDouble (&buf4[87]);
+ navinfo->ephorb.C_is = bGetSingle (&buf4[95]);
+ navinfo->ephorb.i_0 = bGetDouble (&buf4[99]);
+ navinfo->ephorb.C_rc = bGetSingle (&buf4[107]);
+ navinfo->ephorb.omega = bGetDouble (&buf4[111]);
+ navinfo->ephorb.OMEGADOT=bGetSingle (&buf4[119]);
+ navinfo->ephorb.IDOT = bGetSingle (&buf4[123]);
+ navinfo->ephorb.Axis = bGetDouble (&buf4[127]);
+ navinfo->ephorb.n = bGetDouble (&buf4[135]);
+ navinfo->ephorb.r1me2 = bGetDouble (&buf4[143]);
+ navinfo->ephorb.OMEGA_n=bGetDouble (&buf4[151]);
+ navinfo->ephorb.ODOT_n = bGetDouble (&buf4[159]);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+short rpt_0x59 (TSIPPKT *rpt,
+ unsigned char *code_type,
+ unsigned char status_code[32])
+/* satellite enable/disable or health heed/ignore list */
+{
+ short iprn;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 33) return TRUE;
+ *code_type = buf[0];
+ for (iprn = 0; iprn < 32; iprn++)
+ status_code[iprn] = buf[iprn + 1];
+ return FALSE;
+}
+
+short rpt_0x5A (TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ float *sample_length,
+ float *signal_level,
+ float *code_phase,
+ float *Doppler,
+ double *time_of_fix)
+/* raw measurement data - code phase/Doppler */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 25) return TRUE;
+ *sv_prn = buf[0];
+ *sample_length = bGetSingle (&buf[1]);
+ *signal_level = bGetSingle (&buf[5]);
+ *code_phase = bGetSingle (&buf[9]);
+ *Doppler = bGetSingle (&buf[13]);
+ *time_of_fix = bGetDouble (&buf[17]);
+ return FALSE;
+}
+
+short rpt_0x5B (TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ unsigned char *sv_health,
+ unsigned char *sv_iode,
+ unsigned char *fit_interval_flag,
+ float *time_of_collection,
+ float *time_of_eph,
+ float *sv_accy)
+/* satellite ephorb status */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 16) return TRUE;
+ *sv_prn = buf[0];
+ *time_of_collection = bGetSingle (&buf[1]);
+ *sv_health = buf[5];
+ *sv_iode = buf[6];
+ *time_of_eph = bGetSingle (&buf[7]);
+ *fit_interval_flag = buf[11];
+ *sv_accy = bGetSingle (&buf[12]);
+ return FALSE;
+}
+
+short rpt_0x5C (TSIPPKT *rpt,
+ unsigned char *sv_prn,
+ unsigned char *slot,
+ unsigned char *chan,
+ unsigned char *acq_flag,
+ unsigned char *eph_flag,
+ float *signal_level,
+ float *time_of_last_msmt,
+ float *elev,
+ float *azim,
+ unsigned char *old_msmt_flag,
+ unsigned char *integer_msec_flag,
+ unsigned char *bad_data_flag,
+ unsigned char *data_collect_flag)
+/* satellite tracking status */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 24) return TRUE;
+ *sv_prn = buf[0];
+ *slot = (unsigned char)((buf[1] & 0x07) + 1);
+ *chan = (unsigned char)(buf[1] >> 3);
+ if (*chan == 0x10) *chan = 2;
+ else (*chan)++;
+ *acq_flag = buf[2];
+ *eph_flag = buf[3];
+ *signal_level = bGetSingle (&buf[4]);
+ *time_of_last_msmt = bGetSingle (&buf[8]);
+ *elev = bGetSingle (&buf[12]);
+ *azim = bGetSingle (&buf[16]);
+ *old_msmt_flag = buf[20];
+ *integer_msec_flag = buf[21];
+ *bad_data_flag = buf[22];
+ *data_collect_flag = buf[23];
+ return FALSE;
+}
+
+/**/
+short rpt_0x6D (TSIPPKT *rpt,
+ unsigned char *manual_mode,
+ unsigned char *nsvs,
+ unsigned char *ndim,
+ unsigned char sv_prn[],
+ float *pdop,
+ float *hdop,
+ float *vdop,
+ float *tdop)
+/* over-determined satellite selection for position fixes, PDOP, fix mode */
+{
+ short islot;
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ *nsvs = (unsigned char)((buf[0] & 0xF0) >> 4);
+ if ((*nsvs)>8) return TRUE;
+ if (rpt->len != 17 + (*nsvs) ) return TRUE;
+
+ *manual_mode = (unsigned char)(buf[0] & 0x08);
+ *ndim = (unsigned char)((buf[0] & 0x07));
+ *pdop = bGetSingle (&buf[1]);
+ *hdop = bGetSingle (&buf[5]);
+ *vdop = bGetSingle (&buf[9]);
+ *tdop = bGetSingle (&buf[13]);
+ for (islot = 0; islot < (*nsvs); islot++)
+ sv_prn[islot] = buf[islot + 17];
+ return FALSE;
+}
+
+/**/
+short rpt_0x82 (TSIPPKT *rpt,
+ unsigned char *diff_mode)
+/* differential fix mode */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 1) return TRUE;
+ *diff_mode = buf[0];
+ return FALSE;
+}
+
+short rpt_0x83 (TSIPPKT *rpt,
+ double ECEF_pos[3],
+ double *clock_bias,
+ float *time_of_fix)
+/* position, ECEF double precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 36) return TRUE;
+ ECEF_pos[0] = bGetDouble (buf);
+ ECEF_pos[1] = bGetDouble (&buf[8]);
+ ECEF_pos[2] = bGetDouble (&buf[16]);
+ *clock_bias = bGetDouble (&buf[24]);
+ *time_of_fix = bGetSingle (&buf[32]);
+ return FALSE;
+}
+
+short rpt_0x84 (TSIPPKT *rpt,
+ double *lat,
+ double *lon,
+ double *alt,
+ double *clock_bias,
+ float *time_of_fix)
+/* position, lat-lon-alt double precision */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 36) return TRUE;
+ *lat = bGetDouble (buf);
+ *lon = bGetDouble (&buf[8]);
+ *alt = bGetDouble (&buf[16]);
+ *clock_bias = bGetDouble (&buf[24]);
+ *time_of_fix = bGetSingle (&buf[32]);
+ return FALSE;
+}
+
+short rpt_Paly0xBB(TSIPPKT *rpt,
+ TSIP_RCVR_CFG *TsipxBB)
+{
+
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ /* Palisade is inconsistent with other TSIP, which has a kength of 40 */
+ /* if (rpt->len != 40) return TRUE; */
+ if (rpt->len != 43) return TRUE;
+
+ TsipxBB->bSubcode = buf[0];
+ TsipxBB->operating_mode = buf[1] ;
+ TsipxBB->dyn_code = buf[3] ;
+ TsipxBB->elev_mask = bGetSingle (&buf[5]);
+ TsipxBB->cno_mask = bGetSingle (&buf[9]);
+ TsipxBB->dop_mask = bGetSingle (&buf[13]);
+ TsipxBB->dop_switch = bGetSingle (&buf[17]);
+ return FALSE;
+}
+
+short rpt_0xBC (TSIPPKT *rpt,
+ unsigned char *port_num,
+ unsigned char *in_baud,
+ unsigned char *out_baud,
+ unsigned char *data_bits,
+ unsigned char *parity,
+ unsigned char *stop_bits,
+ unsigned char *flow_control,
+ unsigned char *protocols_in,
+ unsigned char *protocols_out,
+ unsigned char *reserved)
+/* Receiver serial port configuration */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 10) return TRUE;
+ *port_num = buf[0];
+ *in_baud = buf[1];
+ *out_baud = buf[2];
+ *data_bits = buf[3];
+ *parity = buf[4];
+ *stop_bits = buf[5];
+ *flow_control = buf[6];
+ *protocols_in = buf[7];
+ *protocols_out = buf[8];
+ *reserved = buf[9];
+
+ return FALSE;
+}
+
+/**** Superpackets ****/
+
+short rpt_0x8F0B(TSIPPKT *rpt,
+ unsigned short *event,
+ double *tow,
+ unsigned char *date,
+ unsigned char *month,
+ short *year,
+ unsigned char *dim_mode,
+ short *utc_offset,
+ double *bias,
+ double *drift,
+ float *bias_unc,
+ float *dr_unc,
+ double *lat,
+ double *lon,
+ double *alt,
+ char sv_id[8])
+{
+ short local_index;
+ unsigned char *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 74) return TRUE;
+ *event = bGetShort(&buf[1]);
+ *tow = bGetDouble(&buf[3]);
+ *date = buf[11];
+ *month = buf[12];
+ *year = bGetShort(&buf[13]);
+ *dim_mode = buf[15];
+ *utc_offset = bGetShort(&buf[16]);
+ *bias = bGetDouble(&buf[18]);
+ *drift = bGetDouble(&buf[26]);
+ *bias_unc = bGetSingle(&buf[34]);
+ *dr_unc = bGetSingle(&buf[38]);
+ *lat = bGetDouble(&buf[42]);
+ *lon = bGetDouble(&buf[50]);
+ *alt = bGetDouble(&buf[58]);
+
+ for (local_index=0; local_index<8; local_index++) sv_id[local_index] = buf[local_index + 66];
+ return FALSE;
+}
+
+short rpt_0x8F14 (TSIPPKT *rpt,
+ short *datum_idx,
+ double datum_coeffs[5])
+/* datum index and coefficients */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 43) return TRUE;
+ *datum_idx = bGetShort(&buf[1]);
+ datum_coeffs[0] = bGetDouble (&buf[3]);
+ datum_coeffs[1] = bGetDouble (&buf[11]);
+ datum_coeffs[2] = bGetDouble (&buf[19]);
+ datum_coeffs[3] = bGetDouble (&buf[27]);
+ datum_coeffs[4] = bGetDouble (&buf[35]);
+ return FALSE;
+}
+
+
+short rpt_0x8F15 (TSIPPKT *rpt,
+ short *datum_idx,
+ double datum_coeffs[5])
+/* datum index and coefficients */
+{
+ unsigned char *buf;
+ buf = rpt->buf;
+
+ if (rpt->len != 43) return TRUE;
+ *datum_idx = bGetShort(&buf[1]);
+ datum_coeffs[0] = bGetDouble (&buf[3]);
+ datum_coeffs[1] = bGetDouble (&buf[11]);
+ datum_coeffs[2] = bGetDouble (&buf[19]);
+ datum_coeffs[3] = bGetDouble (&buf[27]);
+ datum_coeffs[4] = bGetDouble (&buf[35]);
+ return FALSE;
+}
+
+
+#define MAX_LONG (2147483648.) /* 2**31 */
+
+short rpt_0x8F20 (TSIPPKT *rpt,
+ unsigned char *info,
+ double *lat,
+ double *lon,
+ double *alt,
+ double vel_enu[],
+ double *time_of_fix,
+ short *week_num,
+ unsigned char *nsvs,
+ unsigned char sv_prn[],
+ short sv_IODC[],
+ short *datum_index)
+{
+ short
+ isv;
+ unsigned char
+ *buf, prnx, iode;
+ unsigned long
+ ulongtemp;
+ long
+ longtemp;
+ double
+ vel_scale;
+
+ buf = rpt->buf;
+
+ if (rpt->len != 56) return TRUE;
+
+ vel_scale = (buf[24]&1)? 0.020 : 0.005;
+ vel_enu[0] = bGetShort (buf+2)*vel_scale;
+ vel_enu[1] = bGetShort (buf+4)*vel_scale;
+ vel_enu[2] = bGetShort (buf+6)*vel_scale;
+
+ *time_of_fix = bGetULong (buf+8)*.001;
+
+ longtemp = bGetLong (buf+12);
+ *lat = longtemp*(GPS_PI/MAX_LONG);
+
+ ulongtemp = bGetULong (buf+16);
+ *lon = ulongtemp*(GPS_PI/MAX_LONG);
+ if (*lon > GPS_PI) *lon -= 2.0*GPS_PI;
+
+ *alt = bGetLong (buf+20)*.001;
+ /* 25 blank; 29 = UTC */
+ (*datum_index) = (short)((short)buf[26]-1);
+ *info = buf[27];
+ *nsvs = buf[28];
+ *week_num = bGetShort (&buf[30]);
+ for (isv = 0; isv < 8; isv++) {
+ prnx = buf[32+2*isv];
+ sv_prn[isv] = (unsigned char)(prnx&0x3F);
+ iode = buf[33+2*isv];
+ sv_IODC[isv] = (short)(iode | ((prnx>>6)<<8));
+ }
+ return FALSE;
+}
+
+short rpt_0x8F41 (TSIPPKT *rpt,
+ unsigned char *bSearchRange,
+ unsigned char *bBoardOptions,
+ unsigned long *iiSerialNumber,
+ unsigned char *bBuildYear,
+ unsigned char *bBuildMonth,
+ unsigned char *bBuildDay,
+ unsigned char *bBuildHour,
+ float *fOscOffset,
+ unsigned short *iTestCodeId)
+{
+ if(rpt->len != 17) return FALSE;
+ *bSearchRange = rpt->buf[1];
+ *bBoardOptions = rpt->buf[2];
+ *iiSerialNumber = bGetLong(&rpt->buf[3]);
+ *bBuildYear = rpt->buf[7];
+ *bBuildMonth = rpt->buf[8];
+ *bBuildDay = rpt->buf[9];
+ *bBuildHour = rpt->buf[10];
+ *fOscOffset = bGetSingle(&rpt->buf[11]);
+ *iTestCodeId = bGetShort(&rpt->buf[15]);
+/* Tsipx8E41Data = *Tsipx8E41; */
+ return TRUE;
+}
+
+short rpt_0x8F42 (TSIPPKT *rpt,
+ unsigned char *bProdOptionsPre,
+ unsigned char *bProdNumberExt,
+ unsigned short *iCaseSerialNumberPre,
+ unsigned long *iiCaseSerialNumber,
+ unsigned long *iiProdNumber,
+ unsigned short *iPremiumOptions,
+ unsigned short *iMachineID,
+ unsigned short *iKey)
+{
+ if(rpt->len != 19) return FALSE;
+ *bProdOptionsPre = rpt->buf[1];
+ *bProdNumberExt = rpt->buf[2];
+ *iCaseSerialNumberPre = bGetShort(&rpt->buf[3]);
+ *iiCaseSerialNumber = bGetLong(&rpt->buf[5]);
+ *iiProdNumber = bGetLong(&rpt->buf[9]);
+ *iPremiumOptions = bGetShort(&rpt->buf[13]);
+ *iMachineID = bGetShort(&rpt->buf[15]);
+ *iKey = bGetShort(&rpt->buf[17]);
+ return TRUE;
+}
+
+short rpt_0x8F45(TSIPPKT *rpt,
+ unsigned char *bSegMask)
+{
+ if(rpt->len != 2) return FALSE;
+ *bSegMask = rpt->buf[1];
+ return TRUE;
+}
+
+short rpt_0x8F4A_16(TSIPPKT *rpt,
+ unsigned char *pps_enabled,
+ unsigned char *pps_timebase,
+ unsigned char *pos_polarity,
+ double *pps_offset,
+ float *bias_unc_threshold)
+/* Stinger PPS definition */
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 16) return TRUE;
+ *pps_enabled = buf[1];
+ *pps_timebase = buf[2];
+ *pos_polarity = buf[3];
+ *pps_offset = bGetDouble(&buf[4]);
+ *bias_unc_threshold = bGetSingle(&buf[12]);
+ return FALSE;
+}
+
+short rpt_0x8F4B(TSIPPKT *rpt,
+ unsigned long *decorr_max)
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 5) return TRUE;
+ *decorr_max = bGetLong(&buf[1]);
+ return FALSE;
+}
+
+short rpt_0x8F4D(TSIPPKT *rpt,
+ unsigned long *event_mask)
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 5) return TRUE;
+ *event_mask = bGetULong (&buf[1]);
+ return FALSE;
+}
+
+short rpt_0x8FA5(TSIPPKT *rpt,
+ unsigned char *spktmask)
+{
+ unsigned char
+ *buf;
+
+ buf = rpt->buf;
+ if (rpt->len != 5) return TRUE;
+ spktmask[0] = buf[1];
+ spktmask[1] = buf[2];
+ spktmask[2] = buf[3];
+ spktmask[3] = buf[4];
+ return FALSE;
+}
+
+short rpt_0x8FAD (TSIPPKT *rpt,
+ unsigned short *COUNT,
+ double *FracSec,
+ unsigned char *Hour,
+ unsigned char *Minute,
+ unsigned char *Second,
+ unsigned char *Day,
+ unsigned char *Month,
+ unsigned short *Year,
+ unsigned char *Status,
+ unsigned char *Flags)
+{
+
+ if (rpt->len != 22) return TRUE;
+
+ *COUNT = bGetUShort(&rpt->buf[1]);
+ *FracSec = bGetDouble(&rpt->buf[3]);
+ *Hour = rpt->buf[11];
+ *Minute = rpt->buf[12];
+ *Second = rpt->buf[13];
+ *Day = rpt->buf[14];
+ *Month = rpt->buf[15];
+ *Year = bGetUShort(&rpt->buf[16]);
+ *Status = rpt->buf[18];
+ *Flags = rpt->buf[19];
+ return FALSE;
+}
+
+
+/*
+ * *************************************************************************
+ *
+ * Trimble Navigation, Ltd.
+ * OEM Products Development Group
+ * P.O. Box 3642
+ * 645 North Mary Avenue
+ * Sunnyvale, California 94088-3642
+ *
+ * Corporate Headquarter:
+ * Telephone: (408) 481-8000
+ * Fax: (408) 481-6005
+ *
+ * Technical Support Center:
+ * Telephone: (800) 767-4822 (U.S. and Canada)
+ * (408) 481-6940 (outside U.S. and Canada)
+ * Fax: (408) 481-6020
+ * BBS: (408) 481-7800
+ * e-mail: trimble_support@trimble.com
+ * ftp://ftp.trimble.com/pub/sct/embedded/bin
+ *
+ * *************************************************************************
+ *
+ * T_REPORT.C consists of a primary function TranslateTSIPReportToText()
+ * called by main().
+ *
+ * This function takes a character buffer that has been received as a report
+ * from a TSIP device and interprets it. The character buffer has been
+ * assembled using tsip_input_proc() in T_PARSER.C.
+ *
+ * A large case statement directs processing to one of many mid-level
+ * functions. The mid-level functions specific to the current report
+ * code passes the report buffer to the appropriate report decoder
+ * rpt_0x?? () in T_PARSER.C, which converts the byte stream in rpt.buf
+ * to data values approporaite for use.
+ *
+ * *************************************************************************
+ *
+ */
+
+
+#define GOOD_PARSE 0
+#define BADID_PARSE 1
+#define BADLEN_PARSE 2
+#define BADDATA_PARSE 3
+
+#define B_TSIP 0x02
+#define B_NMEA 0x04
+
+
+/* pbuf is the pointer to the current location of the text output */
+static char
+ *pbuf;
+
+/* keep track of whether the message has been successfully parsed */
+static short
+ parsed;
+
+
+/* convert time of week into day-hour-minute-second and print */
+char* show_time (float time_of_week)
+{
+ short days, hours, minutes;
+ float seconds;
+ double tow = 0;
+ static char timestring [80];
+
+ if (time_of_week == -1.0)
+ {
+ sprintf(timestring, " <No time yet> ");
+ }
+ else if ((time_of_week >= 604800.0) || (time_of_week < 0.0))
+ {
+ sprintf(timestring, " <Bad time> ");
+ }
+ else
+ {
+ if (time_of_week < 604799.9)
+ tow = time_of_week + .00000001;
+ seconds = (float)fmod(tow, 60.);
+ minutes = (short) fmod(tow/60., 60.);
+ hours = (short)fmod(tow / 3600., 24.);
+ days = (short)(tow / 86400.0);
+ sprintf(timestring, " %s %02d:%02d:%05.2f ",
+ dayname[days], hours, minutes, seconds);
+ }
+ return timestring;
+}
+
+/**/
+/* 0x3D */
+static void rpt_chan_A_config (TSIPPKT *rpt)
+{
+ unsigned char
+ tx_baud_index, rx_baud_index,
+ char_format_index, stop_bits,
+ tx_mode_index, rx_mode_index,
+ databits, parity;
+ int
+ i, nbaud;
+
+ /* unload rptbuf */
+ if (rpt_0x3D (rpt,
+ &tx_baud_index, &rx_baud_index, &char_format_index,
+ &stop_bits, &tx_mode_index, &rx_mode_index)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nChannel A Configuration");
+
+ nbaud = sizeof(old_baudnum);
+
+ for (i = 0; i < nbaud; ++i) if (tx_baud_index == old_baudnum[i]) break;
+ pbuf += sprintf(pbuf, "\n Transmit speed: %s at %s",
+ old_output_ch[tx_mode_index], st_baud_text_app[i]);
+
+ for (i = 0; i < nbaud; ++i) if (rx_baud_index == old_baudnum[i]) break;
+ pbuf += sprintf(pbuf, "\n Receive speed: %s at %s",
+ old_input_ch[rx_mode_index], st_baud_text_app[i]);
+
+ databits = (unsigned char)((char_format_index & 0x03) + 5);
+
+ parity = (unsigned char)(char_format_index >> 2);
+ if (parity > 4) parity = 2;
+
+ pbuf += sprintf(pbuf, "\n Character format (bits/char, parity, stop bits): %d-%s-%d",
+ databits, old_parity_text[parity], stop_bits);
+}
+
+/**/
+/* 0x40 */
+static void rpt_almanac_data_page (TSIPPKT *rpt)
+{
+ unsigned char
+ sv_prn;
+ short
+ week_num;
+ float
+ t_zc,
+ eccentricity,
+ t_oa,
+ i_0,
+ OMEGA_dot,
+ sqrt_A,
+ OMEGA_0,
+ omega,
+ M_0;
+
+ /* unload rptbuf */
+ if (rpt_0x40 (rpt,
+ &sv_prn, &week_num, &t_zc, &eccentricity, &t_oa,
+ &i_0, &OMEGA_dot, &sqrt_A, &OMEGA_0, &omega, &M_0)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nAlmanac for SV %02d", sv_prn);
+ pbuf += sprintf(pbuf, "\n Captured:%15.0f %s",
+ t_zc, show_time (t_zc));
+ pbuf += sprintf(pbuf, "\n week:%15d", week_num);
+ pbuf += sprintf(pbuf, "\n Eccentricity:%15g", eccentricity);
+ pbuf += sprintf(pbuf, "\n T_oa:%15.0f %s",
+ t_oa, show_time (t_oa));
+ pbuf += sprintf(pbuf, "\n i 0:%15g", i_0);
+ pbuf += sprintf(pbuf, "\n OMEGA dot:%15g", OMEGA_dot);
+ pbuf += sprintf(pbuf, "\n sqrt A:%15g", sqrt_A);
+ pbuf += sprintf(pbuf, "\n OMEGA 0:%15g", OMEGA_0);
+ pbuf += sprintf(pbuf, "\n omega:%15g", omega);
+ pbuf += sprintf(pbuf, "\n M 0:%15g", M_0);
+}
+
+/* 0x41 */
+static void rpt_GPS_time (TSIPPKT *rpt)
+{
+ float
+ time_of_week, UTC_offset;
+ short
+ week_num;
+
+ /* unload rptbuf */
+ if (rpt_0x41 (rpt, &time_of_week, &UTC_offset, &week_num)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nGPS time:%s GPS week: %d UTC offset %.1f",
+ show_time(time_of_week), week_num, UTC_offset);
+
+}
+
+/* 0x42 */
+static void rpt_single_ECEF_position (TSIPPKT *rpt)
+{
+ float
+ ECEF_pos[3], time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x42 (rpt, ECEF_pos, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSXYZ: %15.0f %15.0f %15.0f %s",
+ ECEF_pos[0], ECEF_pos[1], ECEF_pos[2],
+ show_time(time_of_fix));
+}
+
+/* 0x43 */
+static void rpt_single_ECEF_velocity (TSIPPKT *rpt)
+{
+
+ float
+ ECEF_vel[3], freq_offset, time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x43 (rpt, ECEF_vel, &freq_offset, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nVelECEF: %11.3f %11.3f %11.3f %12.3f%s",
+ ECEF_vel[0], ECEF_vel[1], ECEF_vel[2], freq_offset,
+ show_time(time_of_fix));
+}
+
+/* 0x45 */
+static void rpt_SW_version (TSIPPKT *rpt) {
+ unsigned char
+ major_nav_version, minor_nav_version,
+ nav_day, nav_month, nav_year,
+ major_dsp_version, minor_dsp_version,
+ dsp_day, dsp_month, dsp_year;
+
+ /* unload rptbuf */
+ if (rpt_0x45 (rpt,
+ &major_nav_version, &minor_nav_version,
+ &nav_day, &nav_month, &nav_year,
+ &major_dsp_version, &minor_dsp_version,
+ &dsp_day, &dsp_month, &dsp_year)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf,
+"\nFW Versions: Nav Proc %2d.%02d %2d/%2d/%2d Sig Proc %2d.%02d %2d/%2d/%2d",
+ major_nav_version, minor_nav_version, nav_day, nav_month, nav_year,
+ major_dsp_version, minor_dsp_version, dsp_day, dsp_month, dsp_year);
+}
+
+/* 0x46 */
+static void rpt_rcvr_health (TSIPPKT *rpt)
+{
+ unsigned char
+ status1, status2;
+ static char
+ *sc_text[] = {
+ "Doing position fixes",
+ "Don't have GPS time yet",
+ "Waiting for almanac collection",
+ "DOP too high ",
+ "No satellites available",
+ "Only 1 satellite available",
+ "Only 2 satellites available",
+ "Only 3 satellites available",
+ "No satellites usable ",
+ "Only 1 satellite usable",
+ "Only 2 satellites usable",
+ "Only 3 satellites usable",
+ "Chosen satellite unusable"};
+
+
+ /* unload rptbuf */
+ if (rpt_0x46 (rpt, &status1, &status2))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nRcvr status1: %s (%02Xh); ",
+ sc_text[rpt->buf[0]], status1);
+
+ pbuf += sprintf(pbuf, "status2: %s, %s (%02Xh)",
+ (status2 & 0x01)?"No BBRAM":"BBRAM OK",
+ (status2 & 0x10)?"No Ant":"Ant OK",
+ status2);
+}
+
+/* 0x47 */
+static void rpt_SNR_all_SVs (TSIPPKT *rpt)
+{
+ unsigned char
+ nsvs, sv_prn[12];
+ short
+ isv;
+ float
+ snr[12];
+
+ /* unload rptbuf */
+ if (rpt_0x47 (rpt, &nsvs, sv_prn, snr))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSNR for satellites: %d", nsvs);
+ for (isv = 0; isv < nsvs; isv++)
+ {
+ pbuf += sprintf(pbuf, "\n SV %02d %6.2f",
+ sv_prn[isv], snr[isv]);
+ }
+}
+
+/* 0x48 */
+static void rpt_GPS_system_message (TSIPPKT *rpt)
+{
+ unsigned char
+ message[23];
+
+ /* unload rptbuf */
+ if (rpt_0x48 (rpt, message))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nGPS message: %s", message);
+}
+
+/* 0x49 */
+static void rpt_almanac_health_page (TSIPPKT *rpt)
+{
+ short
+ iprn;
+ unsigned char
+ sv_health [32];
+
+ /* unload rptbuf */
+ if (rpt_0x49 (rpt, sv_health))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nAlmanac health page:");
+ for (iprn = 0; iprn < 32; iprn++)
+ {
+ if (!(iprn%5)) *pbuf++ = '\n';
+ pbuf += sprintf(pbuf, " SV%02d %2X",
+ (iprn+1) , sv_health[iprn]);
+ }
+}
+
+/* 0x4A */
+static void rpt_single_lla_position (TSIPPKT *rpt) {
+ short
+ lat_deg, lon_deg;
+ float
+ lat, lon,
+ alt, clock_bias, time_of_fix;
+ double lat_min, lon_min;
+ unsigned char
+ north_south, east_west;
+
+ if (rpt_0x4A (rpt,
+ &lat, &lon, &alt, &clock_bias, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ /* convert from radians to degrees */
+ lat *= (float)R2D;
+ north_south = 'N';
+ if (lat < 0.0)
+ {
+ north_south = 'S';
+ lat = -lat;
+ }
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+
+ lon *= (float)R2D;
+ east_west = 'E';
+ if (lon < 0.0)
+ {
+ east_west = 'W';
+ lon = -lon;
+ }
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+
+ pbuf += sprintf(pbuf, "\nSLLA: %4d: %06.3f %c%5d:%06.3f %c%10.2f %12.2f%s",
+ lat_deg, lat_min, north_south,
+ lon_deg, lon_min, east_west,
+ alt, clock_bias,
+ show_time(time_of_fix));
+}
+
+/* 0x4A */
+static void rpt_ref_alt (TSIPPKT *rpt) {
+
+ float
+ alt, dummy;
+ unsigned char
+ alt_flag;
+
+ if (rpt_0x4A_2 (rpt,
+ &alt, &dummy, &alt_flag))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nReference Alt: %.1f m; %s",
+ alt, alt_flag?"ON":"OFF");
+}
+
+/* 0x4B */
+static void rpt_rcvr_id_and_status (TSIPPKT *rpt)
+{
+
+ unsigned char
+ machine_id, status3, status4;
+
+ /* unload rptbuf */
+ if (rpt_0x4B (rpt, &machine_id, &status3, &status4))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nRcvr Machine ID: %d; Status3 = %s, %s (%02Xh)",
+ machine_id,
+ (status3 & 0x02)?"No RTC":"RTC OK",
+ (status3 & 0x08)?"No Alm":"Alm OK",
+ status3);
+}
+
+/* 0x4C */
+static void rpt_operating_parameters (TSIPPKT *rpt)
+{
+ unsigned char
+ dyn_code;
+ float
+ el_mask, snr_mask, dop_mask, dop_switch;
+
+ /* unload rptbuf */
+ if (rpt_0x4C (rpt, &dyn_code, &el_mask,
+ &snr_mask, &dop_mask, &dop_switch))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nOperating Parameters:");
+ pbuf += sprintf(pbuf, "\n Dynamics code = %d %s",
+ dyn_code, dyn_text[dyn_code]);
+ pbuf += sprintf(pbuf, "\n Elevation mask = %.2fø", el_mask * R2D);
+ pbuf += sprintf(pbuf, "\n SNR mask = %.2f", snr_mask);
+ pbuf += sprintf(pbuf, "\n DOP mask = %.2f", dop_mask);
+ pbuf += sprintf(pbuf, "\n DOP switch = %.2f", dop_switch);
+}
+
+/* 0x4D */
+static void rpt_oscillator_offset (TSIPPKT *rpt)
+{
+ float
+ osc_offset;
+
+ /* unload rptbuf */
+ if (rpt_0x4D (rpt, &osc_offset))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nOscillator offset: %.2f Hz = %.3f PPM",
+ osc_offset, osc_offset/1575.42);
+}
+
+/* 0x4E */
+static void rpt_GPS_time_set_response (TSIPPKT *rpt)
+{
+
+ unsigned char
+ response;
+
+ /* unload rptbuf */
+ if (rpt_0x4E (rpt, &response))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ switch (response)
+ {
+ case 'Y':
+ pbuf += sprintf(pbuf, "\nTime set accepted");
+ break;
+
+ case 'N':
+ pbuf += sprintf(pbuf, "\nTime set rejected or not required");
+ break;
+
+ default:
+ parsed = BADDATA_PARSE;
+ }
+}
+
+/* 0x4F */
+static void rpt_UTC_offset (TSIPPKT *rpt)
+{
+ double
+ a0;
+ float
+ a1, time_of_data;
+ short
+ dt_ls, wn_t, wn_lsf, dn, dt_lsf;
+
+ /* unload rptbuf */
+ if (rpt_0x4F (rpt, &a0, &a1, &time_of_data,
+ &dt_ls, &wn_t, &wn_lsf, &dn, &dt_lsf)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nUTC Correction Data");
+ pbuf += sprintf(pbuf, "\n A_0 = %g ", a0);
+ pbuf += sprintf(pbuf, "\n A_1 = %g ", a1);
+ pbuf += sprintf(pbuf, "\n delta_t_LS = %d ", dt_ls);
+ pbuf += sprintf(pbuf, "\n t_ot = %.0f ", time_of_data);
+ pbuf += sprintf(pbuf, "\n WN_t = %d ", wn_t );
+ pbuf += sprintf(pbuf, "\n WN_LSF = %d ", wn_lsf );
+ pbuf += sprintf(pbuf, "\n DN = %d ", dn );
+ pbuf += sprintf(pbuf, "\n delta_t_LSF = %d ", dt_lsf );
+}
+
+/**/
+/* 0x54 */
+static void rpt_1SV_bias (TSIPPKT *rpt)
+{
+ float
+ clock_bias, freq_offset, time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x54 (rpt, &clock_bias, &freq_offset, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf (pbuf, "\nTime Fix Clock Bias: %6.2f m Freq Bias: %6.2f m/s%s",
+ clock_bias, freq_offset, show_time (time_of_fix));
+}
+
+/* 0x55 */
+static void rpt_io_opt (TSIPPKT *rpt)
+{
+ unsigned char
+ pos_code, vel_code, time_code, aux_code;
+
+ /* unload rptbuf */
+ if (rpt_0x55 (rpt,
+ &pos_code, &vel_code, &time_code, &aux_code)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ /* rptbuf unloaded */
+
+ pbuf += sprintf(pbuf, "\nI/O Options: %2X %2X %2X %2X",
+ pos_code, vel_code, time_code, aux_code);
+
+ if (pos_code & 0x01) {
+ pbuf += sprintf(pbuf, "\n ECEF XYZ position output");
+ }
+
+ if (pos_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n LLA position output");
+ }
+
+ pbuf += sprintf(pbuf, (pos_code & 0x04)?
+ "\n MSL altitude output (Geoid height) ":
+ "\n WGS-84 altitude output");
+
+ pbuf += sprintf(pbuf, (pos_code & 0x08)?
+ "\n MSL altitude input":
+ "\n WGS-84 altitude input");
+
+ pbuf += sprintf(pbuf, (pos_code & 0x10)?
+ "\n Double precision":
+ "\n Single precision");
+
+ if (pos_code & 0x20) {
+ pbuf += sprintf(pbuf, "\n All Enabled Superpackets");
+ }
+
+ if (vel_code & 0x01) {
+ pbuf += sprintf(pbuf, "\n ECEF XYZ velocity output");
+ }
+
+ if (vel_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n ENU velocity output");
+ }
+
+ pbuf += sprintf(pbuf, (time_code & 0x01)?
+ "\n Time tags in UTC":
+ "\n Time tags in GPS time");
+
+ if (time_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n Fixes delayed to integer seconds");
+ }
+
+ if (time_code & 0x04) {
+ pbuf += sprintf(pbuf, "\n Fixes sent only on request");
+ }
+
+ if (time_code & 0x08) {
+ pbuf += sprintf(pbuf, "\n Synchronized measurements");
+ }
+
+ if (time_code & 0x10) {
+ pbuf += sprintf(pbuf, "\n Minimize measurement propagation");
+ }
+
+ pbuf += sprintf(pbuf, (time_code & 0x20) ?
+ "\n PPS output at all times" :
+ "\n PPS output during fixes");
+
+ if (aux_code & 0x01) {
+ pbuf += sprintf(pbuf, "\n Raw measurement output");
+ }
+
+ if (aux_code & 0x02) {
+ pbuf += sprintf(pbuf, "\n Code-phase smoothed before output");
+ }
+
+ if (aux_code & 0x04) {
+ pbuf += sprintf(pbuf, "\n Additional fix status");
+ }
+
+ pbuf += sprintf(pbuf, (aux_code & 0x08)?
+ "\n Signal Strength Output as dBHz" :
+ "\n Signal Strength Output as AMU");
+}
+
+/* 0x56 */
+static void rpt_ENU_velocity (TSIPPKT *rpt)
+{
+ float
+ vel_ENU[3], freq_offset, time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x56 (rpt, vel_ENU, &freq_offset, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nVel ENU: %11.3f %11.3f %11.3f %12.3f%s",
+ vel_ENU[0], vel_ENU[1], vel_ENU[2], freq_offset,
+ show_time (time_of_fix));
+}
+
+/* 0x57 */
+static void rpt_last_fix_info (TSIPPKT *rpt)
+{
+ unsigned char
+ source_code, diag_code;
+ short
+ week_num;
+ float
+ time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x57 (rpt, &source_code, &diag_code, &week_num, &time_of_fix)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n source code %d; diag code: %2Xh",
+ source_code, diag_code);
+ pbuf += sprintf(pbuf, "\n Time of last fix:%s", show_time(time_of_fix));
+ pbuf += sprintf(pbuf, "\n Week of last fix: %d", week_num);
+}
+
+/* 0x58 */
+static void rpt_GPS_system_data (TSIPPKT *rpt)
+{
+ unsigned char
+ iprn,
+ op_code, data_type, sv_prn,
+ data_length, data_packet[250];
+ ALM_INFO
+ *almanac;
+ ALH_PARMS
+ *almh;
+ UTC_INFO
+ *utc;
+ ION_INFO
+ *ionosphere;
+ EPHEM_CLOCK
+ *cdata;
+ EPHEM_ORBIT
+ *edata;
+ NAV_INFO
+ *nav_data;
+ unsigned char
+ curr_t_oa;
+ unsigned short
+ curr_wn_oa;
+ static char
+ *datname[] =
+ {"", "", "Almanac Orbit",
+ "Health Page & Ref Time", "Ionosphere", "UTC ",
+ "Ephemeris"};
+
+ /* unload rptbuf */
+ if (rpt_0x58 (rpt, &op_code, &data_type, &sv_prn,
+ &data_length, data_packet))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSystem data [%d]: %s SV%02d",
+ data_type, datname[data_type], sv_prn);
+ switch (op_code)
+ {
+ case 1:
+ pbuf += sprintf(pbuf, " Acknowledgment");
+ break;
+ case 2:
+ pbuf += sprintf(pbuf, " length = %d bytes", data_length);
+ switch (data_type) {
+ case 2:
+ /* Almanac */
+ if (sv_prn == 0 || sv_prn > 32) {
+ pbuf += sprintf(pbuf, " Binary PRN invalid");
+ return;
+ }
+ almanac = (ALM_INFO*)data_packet;
+ pbuf += sprintf(pbuf, "\n t_oa_raw = % -12d SV_hlth = % -12d ",
+ almanac->t_oa_raw , almanac->SV_health );
+ pbuf += sprintf(pbuf, "\n e = % -12g t_oa = % -12g ",
+ almanac->e , almanac->t_oa );
+ pbuf += sprintf(pbuf, "\n i_0 = % -12g OMEGADOT = % -12g ",
+ almanac->i_0 , almanac->OMEGADOT );
+ pbuf += sprintf(pbuf, "\n sqrt_A = % -12g OMEGA_0 = % -12g ",
+ almanac->sqrt_A , almanac->OMEGA_0 );
+ pbuf += sprintf(pbuf, "\n omega = % -12g M_0 = % -12g ",
+ almanac->omega , almanac->M_0 );
+ pbuf += sprintf(pbuf, "\n a_f0 = % -12g a_f1 = % -12g ",
+ almanac->a_f0 , almanac->a_f1 );
+ pbuf += sprintf(pbuf, "\n Axis = % -12g n = % -12g ",
+ almanac->Axis , almanac->n );
+ pbuf += sprintf(pbuf, "\n OMEGA_n = % -12g ODOT_n = % -12g ",
+ almanac->OMEGA_n , almanac->ODOT_n );
+ pbuf += sprintf(pbuf, "\n t_zc = % -12g weeknum = % -12d ",
+ almanac->t_zc , almanac->weeknum );
+ pbuf += sprintf(pbuf, "\n wn_oa = % -12d", almanac->wn_oa );
+ break;
+
+ case 3:
+ /* Almanac health page */
+ almh = (ALH_PARMS*)data_packet;
+ pbuf += sprintf(pbuf, "\n t_oa = %d, wn_oa&0xFF = %d ",
+ almh->t_oa, almh->WN_a);
+ pbuf += sprintf(pbuf, "\nAlmanac health page:");
+ for (iprn = 0; iprn < 32; iprn++) {
+ if (!(iprn%5)) *pbuf++ = '\n';
+ pbuf += sprintf(pbuf, " SV%02d %2X",
+ (iprn+1) , almh->SV_health[iprn]);
+ }
+ curr_t_oa = data_packet[34];
+ curr_wn_oa = (unsigned short)((data_packet[35]<<8) + data_packet[36]);
+ pbuf += sprintf(pbuf, "\n current t_oa = %d, wn_oa = %d ",
+ curr_t_oa, curr_wn_oa);
+ break;
+
+ case 4:
+ /* Ionosphere */
+ ionosphere = (ION_INFO*)data_packet;
+ pbuf += sprintf(pbuf, "\n alpha_0 = % -12g alpha_1 = % -12g ",
+ ionosphere->alpha_0, ionosphere->alpha_1);
+ pbuf += sprintf(pbuf, "\n alpha_2 = % -12g alpha_3 = % -12g ",
+ ionosphere->alpha_2, ionosphere->alpha_3);
+ pbuf += sprintf(pbuf, "\n beta_0 = % -12g beta_1 = % -12g ",
+ ionosphere->beta_0, ionosphere->beta_1);
+ pbuf += sprintf(pbuf, "\n beta_2 = % -12g beta_3 = % -12g ",
+ ionosphere->beta_2, ionosphere->beta_3);
+ break;
+
+ case 5:
+ /* UTC */
+ utc = (UTC_INFO*)data_packet;
+ pbuf += sprintf(pbuf, "\n A_0 = %g ", utc->A_0);
+ pbuf += sprintf(pbuf, "\n A_1 = %g ", utc->A_1);
+ pbuf += sprintf(pbuf, "\n delta_t_LS = %d ", utc->delta_t_LS);
+ pbuf += sprintf(pbuf, "\n t_ot = %.0f ", utc->t_ot );
+ pbuf += sprintf(pbuf, "\n WN_t = %d ", utc->WN_t );
+ pbuf += sprintf(pbuf, "\n WN_LSF = %d ", utc->WN_LSF );
+ pbuf += sprintf(pbuf, "\n DN = %d ", utc->DN );
+ pbuf += sprintf(pbuf, "\n delta_t_LSF = %d ", utc->delta_t_LSF );
+ break;
+
+ case 6: /* Ephemeris */
+ if (sv_prn == 0 || sv_prn > 32) {
+ pbuf += sprintf(pbuf, " Binary PRN invalid");
+ return;
+ }
+ nav_data = (NAV_INFO*)data_packet;
+
+ pbuf += sprintf(pbuf, "\n SV_PRN = % -12d . t_ephem = % -12g . ",
+ nav_data->sv_number , nav_data->t_ephem );
+ cdata = &(nav_data->ephclk);
+ pbuf += sprintf(pbuf,
+ "\n weeknum = % -12d . codeL2 = % -12d . L2Pdata = % -12d",
+ cdata->weeknum , cdata->codeL2 , cdata->L2Pdata );
+ pbuf += sprintf(pbuf,
+ "\n SVacc_raw = % -12d .SV_health = % -12d . IODC = % -12d",
+ cdata->SVacc_raw, cdata->SV_health, cdata->IODC );
+ pbuf += sprintf(pbuf,
+ "\n T_GD = % -12g . t_oc = % -12g . a_f2 = % -12g",
+ cdata->T_GD, cdata->t_oc, cdata->a_f2 );
+ pbuf += sprintf(pbuf,
+ "\n a_f1 = % -12g . a_f0 = % -12g . SVacc = % -12g",
+ cdata->a_f1, cdata->a_f0, cdata->SVacc );
+ edata = &(nav_data->ephorb);
+ pbuf += sprintf(pbuf,
+ "\n IODE = % -12d .fit_intvl = % -12d . C_rs = % -12g",
+ edata->IODE, edata->fit_interval, edata->C_rs );
+ pbuf += sprintf(pbuf,
+ "\n delta_n = % -12g . M_0 = % -12g . C_uc = % -12g",
+ edata->delta_n, edata->M_0, edata->C_uc );
+ pbuf += sprintf(pbuf,
+ "\n ecc = % -12g . C_us = % -12g . sqrt_A = % -12g",
+ edata->e, edata->C_us, edata->sqrt_A );
+ pbuf += sprintf(pbuf,
+ "\n t_oe = % -12g . C_ic = % -12g . OMEGA_0 = % -12g",
+ edata->t_oe, edata->C_ic, edata->OMEGA_0 );
+ pbuf += sprintf(pbuf,
+ "\n C_is = % -12g . i_0 = % -12g . C_rc = % -12g",
+ edata->C_is, edata->i_0, edata->C_rc );
+ pbuf += sprintf(pbuf,
+ "\n omega = % -12g . OMEGADOT = % -12g . IDOT = % -12g",
+ edata->omega, edata->OMEGADOT, edata->IDOT );
+ pbuf += sprintf(pbuf,
+ "\n Axis = % -12g . n = % -12g . r1me2 = % -12g",
+ edata->Axis, edata->n, edata->r1me2 );
+ pbuf += sprintf(pbuf,
+ "\n OMEGA_n = % -12g . ODOT_n = % -12g",
+ edata->OMEGA_n, edata->ODOT_n );
+ break;
+ }
+ }
+}
+
+
+/* 0x59: */
+static void rpt_SVs_enabled (TSIPPKT *rpt)
+{
+ unsigned char
+ numsvs,
+ code_type,
+ status_code[32];
+ short
+ iprn;
+
+ /* unload rptbuf */
+ if (rpt_0x59 (rpt, &code_type, status_code))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ switch (code_type)
+ {
+ case 3: pbuf += sprintf(pbuf, "\nSVs Disabled:\n"); break;
+ case 6: pbuf += sprintf(pbuf, "\nSVs with Health Ignored:\n"); break;
+ default: return;
+ }
+ numsvs = 0;
+ for (iprn=0; iprn<32; iprn++)
+ {
+ if (status_code[iprn])
+ {
+ pbuf += sprintf(pbuf, " %02d", iprn+1);
+ numsvs++;
+ }
+ }
+ if (numsvs == 0) pbuf += sprintf(pbuf, "None");
+}
+
+
+/* 0x5A */
+static void rpt_raw_msmt (TSIPPKT *rpt)
+{
+ unsigned char
+ sv_prn;
+ float
+ sample_length, signal_level, code_phase, Doppler;
+ double
+ time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x5A (rpt, &sv_prn, &sample_length, &signal_level,
+ &code_phase, &Doppler, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n %02d %5.0f %7.1f %10.2f %10.2f %12.3f %s",
+ sv_prn, sample_length, signal_level, code_phase, Doppler, time_of_fix,
+ show_time ((float)time_of_fix));
+}
+
+/* 0x5B */
+static void rpt_SV_ephemeris_status (TSIPPKT *rpt)
+{
+ unsigned char
+ sv_prn, sv_health, sv_iode, fit_interval_flag;
+ float
+ time_of_collection, time_of_eph, sv_accy;
+
+ /* unload rptbuf */
+ if (rpt_0x5B (rpt, &sv_prn, &sv_health, &sv_iode, &fit_interval_flag,
+ &time_of_collection, &time_of_eph, &sv_accy))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n SV%02d %s %2Xh %2Xh ",
+ sv_prn, show_time (time_of_collection), sv_health, sv_iode);
+ /* note: cannot use show_time twice in same call */
+ pbuf += sprintf(pbuf, "%s %1d %4.1f",
+ show_time (time_of_eph), fit_interval_flag, sv_accy);
+}
+
+/* 0x5C */
+static void rpt_SV_tracking_status (TSIPPKT *rpt)
+{
+ unsigned char
+ sv_prn, chan, slot, acq_flag, eph_flag,
+ old_msmt_flag, integer_msec_flag, bad_data_flag,
+ data_collect_flag;
+ float
+ signal_level, time_of_last_msmt,
+ elev, azim;
+
+ /* unload rptbuf */
+ if (rpt_0x5C (rpt,
+ &sv_prn, &slot, &chan, &acq_flag, &eph_flag,
+ &signal_level, &time_of_last_msmt, &elev, &azim,
+ &old_msmt_flag, &integer_msec_flag, &bad_data_flag,
+ &data_collect_flag))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf,
+"\n SV%2d %1d %1d %1d %4.1f %s %5.1f %5.1f",
+ sv_prn, chan,
+ acq_flag, eph_flag, signal_level,
+ show_time(time_of_last_msmt),
+ elev*R2D, azim*R2D);
+}
+
+/**/
+/* 0x6D */
+static void rpt_allSV_selection (TSIPPKT *rpt)
+{
+ unsigned char
+ manual_mode, nsvs, sv_prn[8], ndim;
+ short
+ islot;
+ float
+ pdop, hdop, vdop, tdop;
+
+ /* unload rptbuf */
+ if (rpt_0x6D (rpt,
+ &manual_mode, &nsvs, &ndim, sv_prn,
+ &pdop, &hdop, &vdop, &tdop))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ switch (ndim)
+ {
+ case 0:
+ pbuf += sprintf(pbuf, "\nMode: Searching, %d-SV:", nsvs);
+ break;
+ case 1:
+ pbuf += sprintf(pbuf, "\nMode: One-SV Timing:");
+ break;
+ case 3: case 4:
+ pbuf += sprintf(pbuf, "\nMode: %c-%dD, %d-SV:",
+ manual_mode ? 'M' : 'A', ndim - 1, nsvs);
+ break;
+ case 5:
+ pbuf += sprintf(pbuf, "\nMode: Timing, %d-SV:", nsvs);
+ break;
+ default:
+ pbuf += sprintf(pbuf, "\nMode: Unknown = %d:", ndim);
+ break;
+ }
+
+ for (islot = 0; islot < nsvs; islot++)
+ {
+ if (sv_prn[islot]) pbuf += sprintf(pbuf, " %02d", sv_prn[islot]);
+ }
+ if (ndim == 3 || ndim == 4)
+ {
+ pbuf += sprintf(pbuf, "; DOPs: P %.1f H %.1f V %.1f T %.1f",
+ pdop, hdop, vdop, tdop);
+ }
+}
+
+/**/
+/* 0x82 */
+static void rpt_DGPS_position_mode (TSIPPKT *rpt)
+{
+ unsigned char
+ diff_mode;
+
+ /* unload rptbuf */
+ if (rpt_0x82 (rpt, &diff_mode)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nFix is%s DGPS-corrected (%s mode) (%d)",
+ (diff_mode&1) ? "" : " not",
+ (diff_mode&2) ? "auto" : "manual",
+ diff_mode);
+}
+
+/* 0x83 */
+static void rpt_double_ECEF_position (TSIPPKT *rpt)
+{
+
+ double
+ ECEF_pos[3], clock_bias;
+ float
+ time_of_fix;
+
+ /* unload rptbuf */
+ if (rpt_0x83 (rpt, ECEF_pos, &clock_bias, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nDXYZ:%12.2f %13.2f %13.2f %12.2f%s",
+ ECEF_pos[0], ECEF_pos[1], ECEF_pos[2], clock_bias,
+ show_time(time_of_fix));
+}
+
+/* 0x84 */
+static void rpt_double_lla_position (TSIPPKT *rpt)
+{
+ short
+ lat_deg, lon_deg;
+ double
+ lat, lon, lat_min, lon_min,
+ alt, clock_bias;
+ float
+ time_of_fix;
+ unsigned char
+ north_south, east_west;
+
+ /* unload rptbuf */
+ if (rpt_0x84 (rpt,
+ &lat, &lon, &alt, &clock_bias, &time_of_fix))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ lat *= R2D;
+ lon *= R2D;
+ if (lat < 0.0) {
+ north_south = 'S';
+ lat = -lat;
+ } else {
+ north_south = 'N';
+ }
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+
+ if (lon < 0.0) {
+ east_west = 'W';
+ lon = -lon;
+ } else {
+ east_west = 'E';
+ }
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+ pbuf += sprintf(pbuf, "\nDLLA: %2d:%08.5f %c; %3d:%08.5f %c; %10.2f %12.2f%s",
+ lat_deg, lat_min, north_south,
+ lon_deg, lon_min, east_west,
+ alt, clock_bias,
+ show_time(time_of_fix));
+}
+
+/* 0xBB */
+static void rpt_complete_rcvr_config (TSIPPKT *rpt)
+{
+ TSIP_RCVR_CFG TsipxBB ;
+ /* unload rptbuf */
+ if (rpt_Paly0xBB (rpt, &TsipxBB))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n operating mode: %s",
+ NavModeText0xBB[TsipxBB.operating_mode]);
+ pbuf += sprintf(pbuf, "\n dynamics: %s",
+ dyn_text[TsipxBB.dyn_code]);
+ pbuf += sprintf(pbuf, "\n elev angle mask: %g deg",
+ TsipxBB.elev_mask * R2D);
+ pbuf += sprintf(pbuf, "\n SNR mask: %g AMU",
+ TsipxBB.cno_mask);
+ pbuf += sprintf(pbuf, "\n DOP mask: %g",
+ TsipxBB.dop_mask);
+ pbuf += sprintf(pbuf, "\n DOP switch: %g",
+ TsipxBB.dop_switch);
+ return ;
+}
+
+/* 0xBC */
+static void rpt_rcvr_serial_port_config (TSIPPKT *rpt)
+{
+ unsigned char
+ port_num, in_baud, out_baud, data_bits, parity, stop_bits, flow_control,
+ protocols_in, protocols_out, reserved;
+ unsigned char known;
+
+ /* unload rptbuf */
+ if (rpt_0xBC (rpt, &port_num, &in_baud, &out_baud, &data_bits, &parity,
+ &stop_bits, &flow_control, &protocols_in, &protocols_out, &reserved)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ /* rptbuf unloaded */
+
+ pbuf += sprintf(pbuf, "\n RECEIVER serial port %s config:",
+ rcvr_port_text[port_num]);
+
+ pbuf += sprintf(pbuf, "\n I/O Baud %s/%s, %d - %s - %d",
+ st_baud_text_app[in_baud],
+ st_baud_text_app[out_baud],
+ data_bits+5,
+ parity_text[parity],
+ stop_bits=1);
+ pbuf += sprintf(pbuf, "\n Input protocols: ");
+ known = FALSE;
+ if (protocols_in&B_TSIP)
+ {
+ pbuf += sprintf(pbuf, "%s ", protocols_in_text[1]);
+ known = TRUE;
+ }
+ if (known == FALSE) pbuf += sprintf(pbuf, "No known");
+
+ pbuf += sprintf(pbuf, "\n Output protocols: ");
+ known = FALSE;
+ if (protocols_out&B_TSIP)
+ {
+ pbuf += sprintf(pbuf, "%s ", protocols_out_text[1]);
+ known = TRUE;
+ }
+ if (protocols_out&B_NMEA)
+ {
+ pbuf += sprintf(pbuf, "%s ", protocols_out_text[2]);
+ known = TRUE;
+ }
+ if (known == FALSE) pbuf += sprintf(pbuf, "No known");
+ reserved = reserved;
+
+ }
+
+/* 0x8F */
+/* 8F0B */
+static void rpt_8F0B(TSIPPKT *rpt)
+{
+ const char
+ *oprtng_dim[7] = {
+ "horizontal (2-D)",
+ "full position (3-D)",
+ "single satellite (0-D)",
+ "automatic",
+ "N/A",
+ "N/A",
+ "overdetermined clock"};
+ char
+ sv_id[8];
+ unsigned char
+ month,
+ date,
+ dim_mode,
+ north_south,
+ east_west;
+ unsigned short
+ event;
+ short
+ utc_offset,
+ year,
+ local_index;
+ short
+ lat_deg,
+ lon_deg;
+ float
+ bias_unc,
+ dr_unc;
+ double
+ tow,
+ bias,
+ drift,
+ lat,
+ lon,
+ alt,
+ lat_min,
+ lon_min;
+ int
+ numfix,
+ numnotfix;
+
+ if (rpt_0x8F0B(rpt,
+ &event,
+ &tow,
+ &date,
+ &month,
+ &year,
+ &dim_mode,
+ &utc_offset,
+ &bias,
+ &drift,
+ &bias_unc,
+ &dr_unc,
+ &lat,
+ &lon,
+ &alt,
+ sv_id))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ if (event == 0)
+ {
+ pbuf += sprintf(pbuf, "\nNew partial+full meas");
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, "\nEvent count: %5d", event);
+ }
+
+ pbuf += sprintf(pbuf, "\nGPS time : %s %2d/%2d/%2d (DMY)",
+ show_time(tow), date, month, year);
+ pbuf += sprintf(pbuf, "\nMode : %s", oprtng_dim[dim_mode]);
+ pbuf += sprintf(pbuf, "\nUTC offset: %2d", utc_offset);
+ pbuf += sprintf(pbuf, "\nClock Bias: %6.2f m", bias);
+ pbuf += sprintf(pbuf, "\nFreq bias : %6.2f m/s", drift);
+ pbuf += sprintf(pbuf, "\nBias unc : %6.2f m", bias_unc);
+ pbuf += sprintf(pbuf, "\nFreq unc : %6.2f m/s", dr_unc);
+
+ lat *= R2D; /* convert from radians to degrees */
+ lon *= R2D;
+ if (lat < 0.0)
+ {
+ north_south = 'S';
+ lat = -lat;
+ }
+ else
+ {
+ north_south = 'N';
+ }
+
+ lat_deg = (short)lat;
+ lat_min = (lat - lat_deg) * 60.0;
+ if (lon < 0.0)
+ {
+ east_west = 'W';
+ lon = -lon;
+ }
+ else
+ {
+ east_west = 'E';
+ }
+
+ lon_deg = (short)lon;
+ lon_min = (lon - lon_deg) * 60.0;
+ pbuf += sprintf(pbuf, "\nPosition :");
+ pbuf += sprintf(pbuf, " %4d %6.3f %c", lat_deg, lat_min, north_south);
+ pbuf += sprintf(pbuf, " %5d %6.3f %c", lon_deg, lon_min, east_west);
+ pbuf += sprintf(pbuf, " %10.2f", alt);
+
+ numfix = numnotfix = 0;
+ for (local_index=0; local_index<8; local_index++)
+ {
+ if (sv_id[local_index] < 0) numnotfix++;
+ if (sv_id[local_index] > 0) numfix++;
+ }
+ if (numfix > 0)
+ {
+ pbuf += sprintf(pbuf, "\nSVs used in fix : ");
+ for (local_index=0; local_index<8; local_index++)
+ {
+ if (sv_id[local_index] > 0)
+ {
+ pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]);
+ }
+ }
+ }
+ if (numnotfix > 0)
+ {
+ pbuf += sprintf(pbuf, "\nOther SVs tracked: ");
+ for (local_index=0; local_index<8; local_index++)
+ {
+ if (sv_id[local_index] < 0)
+ {
+ pbuf += sprintf(pbuf, "%2d ", sv_id[local_index]);
+ }
+ }
+ }
+}
+
+/* 0x8F14 */
+static void rpt_8F14 (TSIPPKT *rpt)
+/* Datum parameters */
+{
+ double
+ datum_coeffs[5];
+ short
+ datum_idx;
+
+ /* unload rptbuf */
+ if (rpt_0x8F14 (rpt, &datum_idx, datum_coeffs))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ if (datum_idx == -1)
+ {
+ pbuf += sprintf(pbuf, "\nUser-Entered Datum:");
+ pbuf += sprintf(pbuf, "\n dx = %6.1f", datum_coeffs[0]);
+ pbuf += sprintf(pbuf, "\n dy = %6.1f", datum_coeffs[1]);
+ pbuf += sprintf(pbuf, "\n dz = %6.1f", datum_coeffs[2]);
+ pbuf += sprintf(pbuf, "\n a-axis = %10.3f", datum_coeffs[3]);
+ pbuf += sprintf(pbuf, "\n e-squared = %16.14f", datum_coeffs[4]);
+ }
+ else if (datum_idx == 0)
+ {
+ pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 ");
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx);
+ }
+}
+
+/* 0x8F15 */
+static void rpt_8F15 (TSIPPKT *rpt)
+/* Datum parameters */
+{
+ double
+ datum_coeffs[5];
+ short
+ datum_idx;
+
+ /* unload rptbuf */
+ if (rpt_0x8F15 (rpt, &datum_idx, datum_coeffs)) {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ if (datum_idx == -1)
+ {
+ pbuf += sprintf(pbuf, "\nUser-Entered Datum:");
+ pbuf += sprintf(pbuf, "\n dx = %6.1f", datum_coeffs[0]);
+ pbuf += sprintf(pbuf, "\n dy = %6.1f", datum_coeffs[1]);
+ pbuf += sprintf(pbuf, "\n dz = %6.1f", datum_coeffs[2]);
+ pbuf += sprintf(pbuf, "\n a-axis = %10.3f", datum_coeffs[3]);
+ pbuf += sprintf(pbuf, "\n e-squared = %16.14f", datum_coeffs[4]);
+ }
+ else if (datum_idx == 0)
+ {
+ pbuf += sprintf(pbuf, "\nWGS-84 datum, Index 0 ");
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, "\nStandard Datum, Index %3d ", datum_idx);
+ }
+}
+
+/* 0x8F20 */
+#define INFO_DGPS 0x02
+#define INFO_2D 0x04
+#define INFO_ALTSET 0x08
+#define INFO_FILTERED 0x10
+static void rpt_8F20 (TSIPPKT *rpt)
+{
+ unsigned char
+ info, nsvs, sv_prn[32];
+ short
+ week_num, datum_index, sv_IODC[32];
+ double
+ lat, lon, alt, time_of_fix;
+ double
+ londeg, latdeg, vel[3];
+ short
+ isv;
+ char
+ datum_string[20];
+
+ /* unload rptbuf */
+ if (rpt_0x8F20 (rpt,
+ &info, &lat, &lon, &alt, vel,
+ &time_of_fix,
+ &week_num, &nsvs, sv_prn, sv_IODC, &datum_index))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ pbuf += sprintf(pbuf,
+ "\nFix at: %04d:%3s:%02d:%02d:%06.3f GPS (=UTC+%2ds) FixType: %s%s%s",
+ week_num,
+ dayname[(short)(time_of_fix/86400.0)],
+ (short)fmod(time_of_fix/3600., 24.),
+ (short)fmod(time_of_fix/60., 60.),
+ fmod(time_of_fix, 60.),
+ (char)rpt->buf[29], /* UTC offset */
+ (info & INFO_DGPS)?"Diff":"",
+ (info & INFO_2D)?"2D":"3D",
+ (info & INFO_FILTERED)?"-Filtrd":"");
+
+ if (datum_index > 0)
+ {
+ sprintf(datum_string, "Datum%3d", datum_index);
+ }
+ else if (datum_index)
+ {
+ sprintf(datum_string, "Unknown ");
+ }
+ else
+ {
+ sprintf(datum_string, "WGS-84");
+ }
+
+ /* convert from radians to degrees */
+ latdeg = R2D * fabs(lat);
+ londeg = R2D * fabs(lon);
+ pbuf += sprintf(pbuf,
+ "\n Pos: %4d:%09.6f %c %5d:%09.6f %c %10.2f m HAE (%s)",
+ (short)latdeg, fmod (latdeg, 1.)*60.0,
+ (lat<0.0)?'S':'N',
+ (short)londeg, fmod (londeg, 1.)*60.0,
+ (lon<0.0)?'W':'E',
+ alt,
+ datum_string);
+ pbuf += sprintf(pbuf,
+ "\n Vel: %9.3f E %9.3f N %9.3f U (m/sec)",
+ vel[0], vel[1], vel[2]);
+
+ pbuf += sprintf(pbuf,
+ "\n SVs: ");
+ for (isv = 0; isv < nsvs; isv++) {
+ pbuf += sprintf(pbuf, " %02d", sv_prn[isv]);
+ }
+ pbuf += sprintf(pbuf, " (IODEs:");
+ for (isv = 0; isv < nsvs; isv++) {
+ pbuf += sprintf(pbuf, " %02X", sv_IODC[isv]&0xFF);
+ }
+ pbuf += sprintf(pbuf, ")");
+}
+
+/* 0x8F41 */
+static void rpt_8F41(TSIPPKT *rpt)
+{
+ unsigned char
+ bSearchRange,
+ bBoardOptions,
+ bBuildYear,
+ bBuildMonth,
+ bBuildDay,
+ bBuildHour;
+ float
+ fOscOffset;
+ unsigned short
+ iTestCodeId;
+ unsigned long
+ iiSerialNumber;
+
+ if (!rpt_0x8F41(rpt,
+ &bSearchRange,
+ &bBoardOptions,
+ &iiSerialNumber,
+ &bBuildYear,
+ &bBuildMonth,
+ &bBuildDay,
+ &bBuildHour,
+ &fOscOffset,
+ &iTestCodeId))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n search range: %d",
+ bSearchRange);
+ pbuf += sprintf(pbuf, "\n board options: %d",
+ bBoardOptions);
+ pbuf += sprintf(pbuf, "\n board serial #: %ld",
+ iiSerialNumber);
+ pbuf += sprintf(pbuf, "\n build date/hour: %02d/%02d/%02d %02d:00",
+ bBuildDay, bBuildMonth, bBuildYear, bBuildHour);
+ pbuf += sprintf(pbuf, "\n osc offset: %.3f PPM (%.0f Hz)",
+ fOscOffset/1575.42, fOscOffset);
+ pbuf += sprintf(pbuf, "\n test code: %d",
+ iTestCodeId);
+}
+
+/* 0x8F42 */
+static void rpt_8F42(TSIPPKT *rpt)
+{
+ unsigned char
+ bProdOptionsPre,
+ bProdNumberExt;
+ unsigned short
+ iCaseSerialNumberPre,
+ iPremiumOptions,
+ iMachineID,
+ iKey;
+ unsigned long
+ iiCaseSerialNumber,
+ iiProdNumber;
+
+ if (!rpt_0x8F42(rpt,
+ &bProdOptionsPre,
+ &bProdNumberExt,
+ &iCaseSerialNumberPre,
+ &iiCaseSerialNumber,
+ &iiProdNumber,
+ &iPremiumOptions,
+ &iMachineID,
+ &iKey))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nProduct ID 8F42");
+ pbuf += sprintf(pbuf, "\n extension: %d", bProdNumberExt);
+ pbuf += sprintf(pbuf, "\n case serial # prefix: %d", iCaseSerialNumberPre);
+ pbuf += sprintf(pbuf, "\n case serial #: %ld", iiCaseSerialNumber);
+ pbuf += sprintf(pbuf, "\n prod. #: %ld", iiProdNumber);
+ pbuf += sprintf(pbuf, "\n premium options: %Xh", iPremiumOptions);
+ pbuf += sprintf(pbuf, "\n machine ID: %d", iMachineID);
+ pbuf += sprintf(pbuf, "\n key: %Xh", iKey);
+}
+
+/* 0x8F45 */
+static void rpt_8F45(TSIPPKT *rpt)
+{
+ unsigned char bSegMask;
+
+ if (!rpt_0x8F45(rpt,
+ &bSegMask))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+ pbuf += sprintf(pbuf, "\nCleared Segment Mask: %Xh", bSegMask);
+}
+
+static void rpt_8F4A(TSIPPKT *rpt)
+/* Stinger PPS def */
+{
+ unsigned char
+ pps_enabled,
+ pps_timebase,
+ pps_polarity;
+ float
+ bias_unc_threshold;
+ double
+ pps_offset;
+
+ if (rpt_0x8F4A_16 (rpt,
+ &pps_enabled,
+ &pps_timebase,
+ &pps_polarity,
+ &pps_offset,
+ &bias_unc_threshold))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nPPS is %s", pps_enabled?"enabled":"disabled");
+ pbuf += sprintf(pbuf, "\n timebase: %s", PPSTimeBaseText[pps_timebase]);
+ pbuf += sprintf(pbuf, "\n polarity: %s", PPSPolarityText[pps_polarity]);
+ pbuf += sprintf(pbuf, "\n offset: %.1f ns, ", pps_offset*1.e9);
+ pbuf += sprintf(pbuf, "\n biasunc: %.1f ns", bias_unc_threshold/GPS_C*1.e9);
+}
+
+static void rpt_8F4B(TSIPPKT *rpt)
+/* fast-SA decorrolation time for self-survey */
+{
+ unsigned long
+ decorr_max;
+
+ if (rpt_0x8F4B(rpt, &decorr_max))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf,
+ "\nMax # of position fixes for self-survey : %ld",
+ decorr_max);
+}
+
+static void rpt_8F4D(TSIPPKT *rpt)
+{
+ static char
+ *linestart;
+ unsigned long
+ OutputMask;
+ static unsigned long
+ MaskBit[] = {
+ 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020,
+ 0x00000100L, 0x00000800L, 0x00001000L,
+ 0x40000000L, 0x80000000L};
+ int
+ ichoice,
+ numchoices;
+
+ if (rpt_0x8F4D(rpt, &OutputMask))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nAuto-Report Mask: %02X %02X %02X %02X",
+ (unsigned char)(OutputMask>>24),
+ (unsigned char)(OutputMask>>16),
+ (unsigned char)(OutputMask>>8),
+ (unsigned char)OutputMask);
+
+ numchoices = sizeof(MaskText)/sizeof(char*);
+ pbuf += sprintf(pbuf, "\nAuto-Reports scheduled for Output:");
+ linestart = pbuf;
+ for (ichoice=0; ichoice<numchoices; ichoice++)
+ {
+ if (OutputMask&MaskBit[ichoice])
+ {
+ pbuf += sprintf(pbuf, "%s %s",
+ (pbuf==linestart)?"\n ":",",
+ MaskText[ichoice]);
+ if (pbuf-linestart > 60) linestart = pbuf;
+ }
+ }
+
+ pbuf += sprintf(pbuf, "\nAuto-Reports NOT scheduled for Output:");
+ linestart = pbuf;
+ for (ichoice=0; ichoice<numchoices; ichoice++)
+ {
+ if (OutputMask&MaskBit[ichoice]) continue;
+ pbuf += sprintf(pbuf, "%s %s",
+ (pbuf==linestart)?"\n ":",",
+ MaskText[ichoice]);
+ if (pbuf-linestart > 60) linestart = pbuf;
+ }
+}
+
+static void rpt_8FA5(TSIPPKT *rpt)
+{
+ unsigned char
+ spktmask[4];
+
+ if (rpt_0x8FA5(rpt, spktmask))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\nSuperpacket auto-output mask: %02X %02X %02X %02X",
+ spktmask[0], spktmask[1], spktmask[2], spktmask[3]);
+
+ if (spktmask[0]&0x01) pbuf+= sprintf (pbuf, "\n PPS 8F-0B");
+ if (spktmask[0]&0x02) pbuf+= sprintf (pbuf, "\n Event 8F-0B");
+ if (spktmask[0]&0x10) pbuf+= sprintf (pbuf, "\n PPS 8F-AD");
+ if (spktmask[0]&0x20) pbuf+= sprintf (pbuf, "\n Event 8F-AD");
+ if (spktmask[2]&0x01) pbuf+= sprintf (pbuf, "\n ppos Fix 8F-20");
+}
+
+static void rpt_8FAD (TSIPPKT *rpt)
+{
+ unsigned short
+ Count,
+ Year;
+ double
+ FracSec;
+ unsigned char
+ Hour,
+ Minute,
+ Second,
+ Day,
+ Month,
+ Status,
+ Flags;
+ static char* Status8FADText[] = {
+ "CODE_DOING_FIXES",
+ "CODE_GOOD_1_SV",
+ "CODE_APPX_1SV",
+ "CODE_NEED_TIME",
+ "CODE_NEED_INITIALIZATION",
+ "CODE_PDOP_HIGH",
+ "CODE_BAD_1SV",
+ "CODE_0SVS",
+ "CODE_1SV",
+ "CODE_2SVS",
+ "CODE_3SVS",
+ "CODE_NO_INTEGRITY",
+ "CODE_DCORR_GEN",
+ "CODE_OVERDET_CLK",
+ "Invalid Status"},
+ *LeapStatusText[] = {
+ " UTC Avail", " ", " ", " ",
+ " Scheduled", " Pending", " Warning", " In Progress"};
+ int i;
+
+ if (rpt_0x8FAD (rpt,
+ &Count,
+ &FracSec,
+ &Hour,
+ &Minute,
+ &Second,
+ &Day,
+ &Month,
+ &Year,
+ &Status,
+ &Flags))
+ {
+ parsed = BADLEN_PARSE;
+ return;
+ }
+
+ pbuf += sprintf(pbuf, "\n8FAD Count: %d Status: %s",
+ Count, Status8FADText[Status]);
+
+ pbuf += sprintf(pbuf, "\n Leap Flags:");
+ if (Flags)
+ {
+ for (i=0; i<8; i++)
+ {
+ if (Flags&(1<<i)) pbuf += sprintf(pbuf, LeapStatusText[i]);
+ }
+ }
+ else
+ {
+ pbuf += sprintf(pbuf, " UTC info not available");
+ }
+
+ pbuf += sprintf(pbuf, "\n %02d/%02d/%04d (DMY) %02d:%02d:%02d.%09ld UTC",
+ Day, Month, Year, Hour, Minute, Second, (long)(FracSec*1.e9));
+}
+
+
+int print_msg_table_header (int rptcode, char *HdrStr, int force)
+{
+ /* force header is to help auto-output function */
+ /* last_rptcode is to determine whether to print a header */
+ /* for the first occurrence of a series of reports */
+ static int
+ last_rptcode = 0;
+ int
+ numchars;
+
+ numchars = 0;
+ if (force || rptcode!=last_rptcode)
+ {
+ /* supply a header in console output */
+ switch (rptcode)
+ {
+ case 0x5A:
+ numchars = sprintf(HdrStr, "\nRaw Measurement Data");
+ numchars += sprintf(HdrStr+numchars,
+ "\n SV Sample SNR Code Phase Doppler Seconds Time of Meas");
+ break;
+
+ case 0x5B:
+ numchars = sprintf(HdrStr, "\nEphemeris Status");
+ numchars += sprintf(HdrStr+numchars,
+ "\n SV Time collected Health IODE t oe Fit URA");
+ break;
+
+ case 0x5C:
+ numchars = sprintf(HdrStr, "\nTracking Info");
+ numchars += sprintf(HdrStr+numchars,
+ "\n SV C Acq Eph SNR Time of Meas Elev Azim ");
+ break;
+
+ }
+ }
+ last_rptcode = rptcode;
+ return (short)numchars;
+}
+
+static void unknown_rpt (TSIPPKT *rpt)
+{
+ int i;
+
+ /* app-specific rpt packets */
+ if (parsed == BADLEN_PARSE)
+ {
+ pbuf += sprintf(pbuf, "\nTSIP report packet ID %2Xh, length %d: Bad length",
+ rpt->code, rpt->len);
+ }
+ if (parsed == BADID_PARSE)
+ {
+ pbuf += sprintf(pbuf,
+ "\nTSIP report packet ID %2Xh, length %d: translation not supported",
+ rpt->code, rpt->len);
+ }
+
+ if (parsed == BADDATA_PARSE)
+ {
+ pbuf += sprintf(pbuf,
+ "\nTSIP report packet ID %2Xh, length %d: data content incorrect",
+ rpt->code, rpt->len);
+ }
+
+ for (i = 0; i < rpt->len; i++) {
+ if ((i % 20) == 0) *pbuf++ = '\n';
+ pbuf += sprintf(pbuf, " %02X", rpt->buf[i]);
+ }
+}
+/**/
+/*
+** main subroutine, called from ProcessInputBytesWhileWaitingForKBHit()
+*/
+void TranslateTSIPReportToText (TSIPPKT *rpt, char *TextOutputBuffer)
+{
+
+ /* pbuf is the pointer to the current location of the text output */
+ pbuf = TextOutputBuffer;
+
+ /* keep track of whether the message has been successfully parsed */
+ parsed = GOOD_PARSE;
+
+ /* print a header if this is the first of a series of messages */
+ pbuf += print_msg_table_header (rpt->code, pbuf, FALSE);
+
+ /* process incoming TSIP report according to code */
+ switch (rpt->code)
+ {
+ case 0x3D: rpt_chan_A_config (rpt); break;
+ case 0x40: rpt_almanac_data_page (rpt); break;
+ case 0x41: rpt_GPS_time (rpt); break;
+ case 0x42: rpt_single_ECEF_position (rpt); break;
+ case 0x43: rpt_single_ECEF_velocity (rpt); break;
+ case 0x45: rpt_SW_version (rpt); break;
+ case 0x46: rpt_rcvr_health (rpt); break;
+ case 0x47: rpt_SNR_all_SVs (rpt); break;
+ case 0x48: rpt_GPS_system_message (rpt); break;
+ case 0x49: rpt_almanac_health_page (rpt); break;
+ case 0x4A: switch (rpt->len) {
+ /*
+ ** special case (=slip-up) in the TSIP protocol;
+ ** parsing method depends on length
+ */
+ case 20: rpt_single_lla_position (rpt); break;
+ case 9: rpt_ref_alt (rpt); break;
+ } break;
+ case 0x4B: rpt_rcvr_id_and_status (rpt);break;
+ case 0x4C: rpt_operating_parameters (rpt); break;
+ case 0x4D: rpt_oscillator_offset (rpt); break;
+ case 0x4E: rpt_GPS_time_set_response (rpt); break;
+ case 0x4F: rpt_UTC_offset (rpt); break;
+ case 0x54: rpt_1SV_bias (rpt); break;
+ case 0x55: rpt_io_opt (rpt); break;
+ case 0x56: rpt_ENU_velocity (rpt); break;
+ case 0x57: rpt_last_fix_info (rpt); break;
+ case 0x58: rpt_GPS_system_data (rpt); break;
+ case 0x59: rpt_SVs_enabled (rpt); break;
+ case 0x5A: rpt_raw_msmt (rpt); break;
+ case 0x5B: rpt_SV_ephemeris_status (rpt); break;
+ case 0x5C: rpt_SV_tracking_status (rpt); break;
+ case 0x6D: rpt_allSV_selection (rpt); break;
+ case 0x82: rpt_DGPS_position_mode (rpt); break;
+ case 0x83: rpt_double_ECEF_position (rpt); break;
+ case 0x84: rpt_double_lla_position (rpt); break;
+ case 0xBB: rpt_complete_rcvr_config (rpt); break;
+ case 0xBC: rpt_rcvr_serial_port_config (rpt); break;
+
+ case 0x8F: switch (rpt->buf[0])
+ {
+ /* superpackets; parsed according to subcodes */
+ case 0x0B: rpt_8F0B(rpt); break;
+ case 0x14: rpt_8F14(rpt); break;
+ case 0x15: rpt_8F15(rpt); break;
+ case 0x20: rpt_8F20(rpt); break;
+ case 0x41: rpt_8F41(rpt); break;
+ case 0x42: rpt_8F42(rpt); break;
+ case 0x45: rpt_8F45(rpt); break;
+ case 0x4A: rpt_8F4A(rpt); break;
+ case 0x4B: rpt_8F4B(rpt); break;
+ case 0x4D: rpt_8F4D(rpt); break;
+ case 0xA5: rpt_8FA5(rpt); break;
+ case 0xAD: rpt_8FAD(rpt); break;
+ default: parsed = BADID_PARSE; break;
+ }
+ break;
+
+ default: parsed = BADID_PARSE; break;
+ }
+
+ if (parsed != GOOD_PARSE)
+ {
+ /*
+ **The message has TSIP structure (DLEs, etc.)
+ ** but could not be parsed by above routines
+ */
+ unknown_rpt (rpt);
+ }
+
+ /* close TextOutputBuffer */
+ pbuf = '\0';
+}
+
+#endif /* TRIMBLE_OUTPUT_FUNC */
+
+#else /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */
+int refclock_ripencc_bs;
+#endif /* defined(REFCLOCK) && defined(CLOCK_RIPENCC) */
+
diff --git a/ntpd/refclock_shm.c b/ntpd/refclock_shm.c
new file mode 100644
index 0000000..4ab0ede
--- /dev/null
+++ b/ntpd/refclock_shm.c
@@ -0,0 +1,305 @@
+/*
+ * refclock_shm - clock driver for utc via shared memory
+ * - under construction -
+ * To add new modes: Extend or union the shmTime-struct. Do not
+ * extend/shrink size, because otherwise existing implementations
+ * will specify wrong size of shared memory-segment
+ * PB 18.3.97
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_SHM)
+
+#include "ntpd.h"
+#undef fileno
+#include "ntp_io.h"
+#undef fileno
+#include "ntp_refclock.h"
+#undef fileno
+#include "ntp_unixtime.h"
+#undef fileno
+#include "ntp_stdlib.h"
+
+#undef fileno
+#include <ctype.h>
+#undef fileno
+
+#ifndef SYS_WINNT
+# include <sys/ipc.h>
+# include <sys/shm.h>
+# include <assert.h>
+# include <unistd.h>
+# include <stdio.h>
+#endif
+
+/*
+ * This driver supports a reference clock attached thru shared memory
+ */
+
+/*
+ * SHM interface definitions
+ */
+#define PRECISION (-1) /* precision assumed (0.5 s) */
+#define REFID "SHM" /* reference ID */
+#define DESCRIPTION "SHM/Shared memory interface"
+
+#define NSAMPLES 3 /* stages of median filter */
+
+/*
+ * Function prototypes
+ */
+static int shm_start (int, struct peer *);
+static void shm_shutdown (int, struct peer *);
+static void shm_poll (int unit, struct peer *);
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_shm = {
+ shm_start, /* start up driver */
+ shm_shutdown, /* shut down driver */
+ shm_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used */
+ NOFLAGS /* not used */
+};
+struct shmTime {
+ int mode; /* 0 - if valid set
+ * use values,
+ * clear valid
+ * 1 - if valid set
+ * if count before and after read of values is equal,
+ * use values
+ * clear valid
+ */
+ int count;
+ time_t clockTimeStampSec;
+ int clockTimeStampUSec;
+ time_t receiveTimeStampSec;
+ int receiveTimeStampUSec;
+ int leap;
+ int precision;
+ int nsamples;
+ int valid;
+ int dummy[10];
+};
+
+struct shmTime *getShmTime(int);
+
+struct shmTime *getShmTime (int unit) {
+#ifndef SYS_WINNT
+ int shmid=0;
+
+ assert (unit<10); /* MAXUNIT is 4, so should never happen */
+ shmid=shmget (0x4e545030+unit, sizeof (struct shmTime),
+ IPC_CREAT|(unit<2?0700:0777));
+ if (shmid==-1) { /*error */
+ msyslog(LOG_ERR,"SHM shmget (unit %d): %s",unit,strerror(errno));
+ return 0;
+ }
+ else { /* no error */
+ struct shmTime *p=(struct shmTime *)shmat (shmid, 0, 0);
+ if ((int)(long)p==-1) { /* error */
+ msyslog(LOG_ERR,"SHM shmat (unit %d): %s",unit,strerror(errno));
+ return 0;
+ }
+ return p;
+ }
+#else
+ char buf[10];
+ LPSECURITY_ATTRIBUTES psec=0;
+ HANDLE shmid=0;
+ SECURITY_DESCRIPTOR sd;
+ SECURITY_ATTRIBUTES sa;
+ sprintf (buf,"NTP%d",unit);
+ if (unit>=2) { /* world access */
+ if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
+ msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m",unit);
+ return 0;
+ }
+ if (!SetSecurityDescriptorDacl(&sd,1,0,0)) {
+ msyslog(LOG_ERR,"SHM SetSecurityDescriptorDacl (unit %d): %m",unit);
+ return 0;
+ }
+ sa.nLength=sizeof (SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor=&sd;
+ sa.bInheritHandle=0;
+ psec=&sa;
+ }
+ shmid=CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE,
+ 0, sizeof (struct shmTime),buf);
+ if (!shmid) { /*error*/
+ char buf[1000];
+ FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, GetLastError (), 0, buf, sizeof (buf), 0);
+ msyslog(LOG_ERR,"SHM CreateFileMapping (unit %d): %s",unit,buf);
+ return 0;
+ }
+ else {
+ struct shmTime *p=(struct shmTime *) MapViewOfFile (shmid,
+ FILE_MAP_WRITE, 0, 0, sizeof (struct shmTime));
+ if (p==0) { /*error*/
+ char buf[1000];
+ FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, GetLastError (), 0, buf, sizeof (buf), 0);
+ msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s",unit,buf);
+ return 0;
+ }
+ return p;
+ }
+#endif
+}
+/*
+ * shm_start - attach to shared memory
+ */
+static int
+shm_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = -1;
+ pp->unitptr = (caddr_t)getShmTime(unit);
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ memcpy((char *)&pp->refid, REFID, 4);
+ if (pp->unitptr!=0) {
+ ((struct shmTime*)pp->unitptr)->precision=PRECISION;
+ peer->precision = ((struct shmTime*)pp->unitptr)->precision;
+ ((struct shmTime*)pp->unitptr)->valid=0;
+ ((struct shmTime*)pp->unitptr)->nsamples=NSAMPLES;
+ pp->clockdesc = DESCRIPTION;
+ return (1);
+ }
+ else {
+ return 0;
+ }
+}
+
+
+/*
+ * shm_shutdown - shut down the clock
+ */
+static void
+shm_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct shmTime *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct shmTime *)pp->unitptr;
+#ifndef SYS_WINNT
+ /* HMS: shmdt()wants char* or const void * */
+ (void) shmdt (up);
+#else
+ UnmapViewOfFile (up);
+#endif
+}
+
+
+/*
+ * shm_poll - called by the transmit procedure
+ */
+static void
+shm_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct shmTime *up;
+ struct refclockproc *pp;
+
+ /*
+ * This is the main routine. It snatches the time from the shm
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct shmTime*)pp->unitptr;
+ if (up==0) { /* try to map again - this may succeed if meanwhile some-
+ body has ipcrm'ed the old (unaccessible) shared mem
+ segment */
+ pp->unitptr = (caddr_t)getShmTime(unit);
+ up = (struct shmTime*)pp->unitptr;
+ }
+ if (up==0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ if (up->valid) {
+ struct timeval tvr;
+ struct timeval tvt;
+ struct tm *t;
+ int ok=1;
+ switch (up->mode) {
+ case 0: {
+ tvr.tv_sec=up->receiveTimeStampSec;
+ tvr.tv_usec=up->receiveTimeStampUSec;
+ tvt.tv_sec=up->clockTimeStampSec;
+ tvt.tv_usec=up->clockTimeStampUSec;
+ }
+ break;
+ case 1: {
+ int cnt=up->count;
+ tvr.tv_sec=up->receiveTimeStampSec;
+ tvr.tv_usec=up->receiveTimeStampUSec;
+ tvt.tv_sec=up->clockTimeStampSec;
+ tvt.tv_usec=up->clockTimeStampUSec;
+ ok=(cnt==up->count);
+ }
+ break;
+ default:
+ msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d",up->mode);
+ }
+ up->valid=0;
+ if (ok) {
+ TVTOTS(&tvr,&pp->lastrec);
+ pp->lastrec.l_ui += JAN_1970;
+ /* pp->lasttime = current_time; */
+ pp->polls++;
+ t=gmtime (&tvt.tv_sec);
+ pp->day=t->tm_yday+1;
+ pp->hour=t->tm_hour;
+ pp->minute=t->tm_min;
+ pp->second=t->tm_sec;
+ pp->nsec=tvt.tv_usec * 1000;
+ peer->precision=up->precision;
+ pp->leap=up->leap;
+ }
+ else {
+ refclock_report(peer, CEVNT_FAULT);
+ msyslog (LOG_NOTICE, "SHM: access clash in shared memory");
+ return;
+ }
+ }
+ else {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ /*
+ msyslog (LOG_NOTICE, "SHM: no new value found in shared memory");
+ */
+ return;
+ }
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+
+#else
+int refclock_shm_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_tpro.c b/ntpd/refclock_tpro.c
new file mode 100644
index 0000000..3c42568
--- /dev/null
+++ b/ntpd/refclock_tpro.c
@@ -0,0 +1,216 @@
+/*
+ * refclock_tpro - clock driver for the KSI/Odetics TPRO-S IRIG-B reader
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TPRO)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "sys/tpro.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the KSI/Odetecs TPRO-S IRIG-B reader and TPRO-
+ * SAT GPS receiver for the Sun Microsystems SBus. It requires that the
+ * tpro.o device driver be installed and loaded.
+ */
+
+/*
+ * TPRO interface definitions
+ */
+#define DEVICE "/dev/tpro%d" /* device name and unit */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "KSI/Odetics TPRO/S IRIG Interface" /* WRU */
+
+/*
+ * Unit control structure
+ */
+struct tprounit {
+ struct tproval tprodata; /* data returned from tpro read */
+};
+
+/*
+ * Function prototypes
+ */
+static int tpro_start P((int, struct peer *));
+static void tpro_shutdown P((int, struct peer *));
+static void tpro_poll P((int unit, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_tpro = {
+ tpro_start, /* start up driver */
+ tpro_shutdown, /* shut down driver */
+ tpro_poll, /* transmit poll message */
+ noentry, /* not used (old tpro_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old tpro_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * tpro_start - open the TPRO device and initialize data for processing
+ */
+static int
+tpro_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ char device[20];
+ int fd;
+
+ /*
+ * Open TPRO device
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDONLY | O_NDELAY, 0777);
+ if (fd == -1) {
+ msyslog(LOG_ERR, "tpro_start: open of %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct tprounit *) emalloc(sizeof(struct tprounit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct tprounit));
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * tpro_shutdown - shut down the clock
+ */
+static void
+tpro_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct tprounit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * tpro_poll - called by the transmit procedure
+ */
+static void
+tpro_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ struct tproval *tp;
+
+ /*
+ * This is the main routine. It snatches the time from the TPRO
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct tprounit *)pp->unitptr;
+
+ tp = &up->tprodata;
+ if (read(pp->io.fd, (char *)tp, sizeof(struct tproval)) < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ get_systime(&pp->lastrec);
+ pp->polls++;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit. Note: we
+ * can't use the sec/usec conversion produced by the driver,
+ * since the year may be suspect. All format error checking is
+ * done by the sprintf() and sscanf() routines.
+ *
+ * Note that the refclockproc usec member has now become nsec.
+ * We could either multiply the read-in usec value by 1000 or
+ * we could pad the written string appropriately and read the
+ * resulting value in already scaled.
+ */
+ sprintf(pp->a_lastcode,
+ "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
+ tp->day100, tp->day10, tp->day1, tp->hour10, tp->hour1,
+ tp->min10, tp->min1, tp->sec10, tp->sec1, tp->ms100,
+ tp->ms10, tp->ms1, tp->usec100, tp->usec10, tp->usec1,
+ tp->status);
+ pp->lencode = strlen(pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("tpro: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->a_lastcode);
+#endif
+ if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld", &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->nsec)
+ != 5) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->nsec *= 1000; /* Convert usec to nsec */
+ if (!tp->status & 0x3)
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ pp->lastref = pp->lastrec;
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ peer->burst = NSTAGE;
+}
+
+#else
+int refclock_tpro_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_trak.c b/ntpd/refclock_trak.c
new file mode 100644
index 0000000..3a4a54e
--- /dev/null
+++ b/ntpd/refclock_trak.c
@@ -0,0 +1,359 @@
+/*
+ * refclock_trak - clock driver for the TRAK 8810 GPS Station Clock
+ *
+ * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp>
+ * original version Dec, 1993
+ * revised version Sep, 1994 for ntp3.4e or later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TRAK) && defined(PPS)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+/*
+ * This driver supports the TRAK 8810/8820 GPS Station Clock. The claimed
+ * accuracy at the 1-pps output is 200-300 ns relative to the broadcast
+ * signal; however, in most cases the actual accuracy is limited by the
+ * precision of the timecode and the latencies of the serial interface
+ * and operating system.
+ *
+ * For best accuracy, this radio requires the LDISC_ACTS line
+ * discipline, which captures a timestamp at the '*' on-time character
+ * of the timecode. Using this discipline the jitter is in the order of
+ * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer
+ * timestamp is used, which is captured at the \r ending the timecode
+ * message. This introduces a systematic error of 23 character times, or
+ * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun
+ * IPC-class machines.
+ *
+ * Using the memus, the radio should be set for 9600 bps, one stop bit
+ * and no parity. It should be set to operate in computer (no echo)
+ * mode. The timecode format includes neither the year nor leap-second
+ * warning. No provisions are included in this preliminary version of
+ * the driver to read and record detailed internal radio status.
+ *
+ * In operation, this driver sends a RQTS\r request to the radio at
+ * initialization in order to put it in continuous time output mode. The
+ * radio then sends the following message once each second:
+ *
+ * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
+ *
+ * on-time = '*' * ddd = day of year
+ * hh:mm:ss = hours, minutes, seconds
+ * q = quality indicator (phase error), 0-6:
+ * 0 > 20 us
+ * 6 > 10 us
+ * 5 > 1 us
+ * 4 > 100 ns
+ * 3 > 10 ns
+ * 2 < 10 ns
+ *
+ * The alarm condition is indicated by '0' at Q, which means the radio
+ * has a phase error than 20 usec relative to the broadcast time. The
+ * absence of year, DST and leap-second warning in this format is also
+ * alarming.
+ *
+ * The continuous time mode is disabled using the RQTX<cr> request,
+ * following which the radio sends a RQTX DONE<cr><lf> response. In the
+ * normal mode, other control and status requests are effective,
+ * including the leap-second status request RQLS<cr>. The radio responds
+ * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and
+ * day. Presumably, this gives the epoch of the next leap second,
+ * RQLS 00,00,00 if none is specified in the GPS message. Specified in
+ * this form, the information is generally useless and is ignored by
+ * the driver.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/trak%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */
+
+#define LENTRAK 24 /* timecode length */
+#define C_CTO "RQTS\r" /* start continuous time output */
+
+/*
+ * Unit control structure
+ */
+struct trakunit {
+ int polled; /* poll message flag */
+ l_fp tstamp; /* timestamp of last poll */
+};
+
+/*
+ * Function prototypes
+ */
+static int trak_start P((int, struct peer *));
+static void trak_shutdown P((int, struct peer *));
+static void trak_receive P((struct recvbuf *));
+static void trak_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_trak = {
+ trak_start, /* start up driver */
+ trak_shutdown, /* shut down driver */
+ trak_poll, /* transmit poll message */
+ noentry, /* not used (old trak_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old trak_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * trak_start - open the devices and initialize data for processing
+ */
+static int
+trak_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct trakunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. The LDISC_ACTS line discipline inserts a
+ * timestamp following the "*" on-time character of the
+ * timecode.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (
+#ifdef PPS
+ !(fd = refclock_open(device, SPEED232, LDISC_CLK))
+#else
+ !(fd = refclock_open(device, SPEED232, 0))
+#endif /* PPS */
+ )
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct trakunit *)
+ emalloc(sizeof(struct trakunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct trakunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = trak_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->polled = 0;
+
+ /*
+ * Start continuous time output. If something breaks, fold the
+ * tent and go home.
+ */
+ if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
+ refclock_report(peer, CEVNT_FAULT);
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ return (1);
+}
+
+
+/*
+ * trak_shutdown - shut down the clock
+ */
+static void
+trak_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct trakunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct trakunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * trak_receive - receive data from the serial interface
+ */
+static void
+trak_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct trakunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ char *dpt, *dpend;
+ char qchar;
+#ifdef PPS
+ struct ppsclockev ppsev;
+ int request;
+#ifdef HAVE_CIOGETEV
+ request = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ request = TIOCGPPSEV;
+#endif
+#endif /* PPS */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. We
+ * then chuck out everything, including runts, except one
+ * message each poll interval.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct trakunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &pp->lastrec);
+
+ /*
+ * We get a buffer and timestamp following the '*' on-time
+ * character. If a valid timestamp, we use that in place of the
+ * buffer timestamp and edit out the timestamp for prettyprint
+ * billboards.
+ */
+ dpt = pp->a_lastcode;
+ dpend = dpt + pp->lencode;
+ if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
+ if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
+ pp->lastrec.l_i + 1) {
+ pp->lastrec = trtmp;
+ dpt += 9;
+ while (dpt < dpend) {
+ *(dpt - 8) = *dpt;
+ ++dpt;
+ }
+ }
+ }
+ if (up->polled == 0) return;
+ up->polled = 0;
+#ifndef PPS
+ get_systime(&up->tstamp);
+#endif
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("trak: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENTRAK) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
+ */
+ if (sscanf(pp->a_lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c",
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode quality and leap characters. If unsynchronized, set
+ * the leap bits accordingly and exit.
+ */
+ if (qchar == '0') {
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+#ifdef PPS
+ if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
+ ppsev.tv.tv_sec += (u_int32) JAN_1970;
+ TVTOTS(&ppsev.tv,&up->tstamp);
+ }
+#endif /* PPS */
+ /* record the last ppsclock event time stamp */
+ pp->lastrec = up->tstamp;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+}
+
+
+/*
+ * trak_poll - called by the transmit procedure
+ */
+static void
+trak_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct trakunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * We don't really do anything here, except arm the receiving
+ * side to capture a sample and check for timeouts.
+ */
+ pp = peer->procptr;
+ up = (struct trakunit *)pp->unitptr;
+ if (up->polled)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ pp->polls++;
+ up->polled = 1;
+}
+
+#else
+int refclock_trak_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_true.c b/ntpd/refclock_true.c
new file mode 100644
index 0000000..dd355d9
--- /dev/null
+++ b/ntpd/refclock_true.c
@@ -0,0 +1,873 @@
+/*
+ * refclock_true - clock driver for the Kinemetrics Truetime receivers
+ * Receiver Version 3.0C - tested plain, with CLKLDISC
+ * Developement work being done:
+ * - Properly handle varying satellite positions (more acurately)
+ * - Integrate GPSTM and/or OMEGA and/or TRAK and/or ??? drivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TRUETIME)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* This should be an atom clock but those are very hard to build.
+ *
+ * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch
+ * of TTL input and output pins, all brought out to the back panel. If you
+ * wire a PPS signal (such as the TTL PPS coming out of a GOES or other
+ * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the
+ * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the
+ * number of uSecs since the last PPS upward swing, mediated by reading OUT0
+ * to find out if the counter has wrapped around (this happens if more than
+ * 65535us (65ms) elapses between the PPS event and our being called.)
+ */
+#ifdef CLOCK_PPS720
+# undef min /* XXX */
+# undef max /* XXX */
+# include <machine/inline.h>
+# include <sys/pcl720.h>
+# include <sys/i8253.h>
+# define PCL720_IOB 0x2a0 /* XXX */
+# define PCL720_CTR 0 /* XXX */
+#endif
+
+/*
+ * Support for Kinemetrics Truetime Receivers
+ * GOES
+ * GPS/TM-TMD
+ * XL-DC (a 151-602-210, reported by the driver as a GPS/TM-TMD)
+ * GPS-800 TCU (an 805-957 with the RS232 Talker/Listener module)
+ * OM-DC: getting stale ("OMEGA")
+ *
+ * Most of this code is originally from refclock_wwvb.c with thanks.
+ * It has been so mangled that wwvb is not a recognizable ancestor.
+ *
+ * Timcode format: ADDD:HH:MM:SSQCL
+ * A - control A (this is stripped before we see it)
+ * Q - Quality indication (see below)
+ * C - Carriage return
+ * L - Line feed
+ *
+ * Quality codes indicate possible error of
+ * 468-DC GOES Receiver:
+ * GPS-TM/TMD Receiver: (default quality codes for XL-DC)
+ * ? +/- 1 milliseconds # +/- 100 microseconds
+ * * +/- 10 microseconds . +/- 1 microsecond
+ * space less than 1 microsecond
+ * OM-DC OMEGA Receiver: (default quality codes for OMEGA)
+ * WARNING OMEGA navigation system is no longer existent
+ * > >+- 5 seconds
+ * ? >+/- 500 milliseconds # >+/- 50 milliseconds
+ * * >+/- 5 milliseconds . >+/- 1 millisecond
+ * A-H less than 1 millisecond. Character indicates which station
+ * is being received as follows:
+ * A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
+ * E = La Reunion, F = Argentina, G = Australia, H = Japan.
+ *
+ * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
+ *
+ * Notes on 468-DC and OMEGA receiver:
+ *
+ * Send the clock a 'R' or 'C' and once per second a timestamp will
+ * appear. Send a 'P' to get the satellite position once (GOES only.)
+ *
+ * Notes on the 468-DC receiver:
+ *
+ * Since the old east/west satellite locations are only historical, you can't
+ * set your clock propagation delay settings correctly and still use
+ * automatic mode. The manual says to use a compromise when setting the
+ * switches. This results in significant errors. The solution; use fudge
+ * time1 and time2 to incorporate corrections. If your clock is set for
+ * 50 and it should be 58 for using the west and 46 for using the east,
+ * use the line
+ *
+ * fudge 127.127.5.0 time1 +0.008 time2 -0.004
+ *
+ * This corrects the 4 milliseconds advance and 8 milliseconds retard
+ * needed. The software will ask the clock which satellite it sees.
+ *
+ * Ntp.conf parameters:
+ * time1 - offset applied to samples when reading WEST satellite (default = 0)
+ * time2 - offset applied to samples when reading EAST satellite (default = 0)
+ * val1 - stratum to assign to this clock (default = 0)
+ * val2 - refid assigned to this clock (default = "TRUE", see below)
+ * flag1 - will silence the clock side of ntpd, just reading the clock
+ * without trying to write to it. (default = 0)
+ * flag2 - generate a debug file /tmp/true%d.
+ * flag3 - enable ppsclock streams module
+ * flag4 - use the PCL-720 (BSD/OS only)
+ */
+
+
+/*
+ * Definitions
+ */
+#define DEVICE "/dev/true%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "TRUE" /* reference id */
+#define DESCRIPTION "Kinemetrics/TrueTime Receiver"
+
+/*
+ * Tags which station (satellite) we see
+ */
+#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */
+#define GOES_EAST 1 /* until you discover otherwise */
+
+/*
+ * used by the state machine
+ */
+enum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite,
+ e_Poll, e_Location, e_TS, e_Max};
+const char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite",
+ "Poll", "Location", "TS"};
+#define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?")
+
+enum true_state {s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES,
+ s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max};
+const char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES",
+ "Init", "F18", "F50", "Start", "Auto"};
+#define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?")
+
+enum true_type {t_unknown, t_goes, t_tm, t_tcu, t_omega, t_Max};
+const char *types[] = {"unknown", "goes", "tm", "tcu", "omega"};
+#define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?")
+
+/*
+ * unit control structure
+ */
+struct true_unit {
+ unsigned int pollcnt; /* poll message counter */
+ unsigned int station; /* which station we are on */
+ unsigned int polled; /* Hand in a time sample? */
+ enum true_state state; /* state machine */
+ enum true_type type; /* what kind of clock is it? */
+ int unit; /* save an extra copy of this */
+ FILE *debug; /* debug logging file */
+#ifdef CLOCK_PPS720
+ int pcl720init; /* init flag for PCL 720 */
+#endif
+};
+
+/*
+ * Function prototypes
+ */
+static int true_start P((int, struct peer *));
+static void true_shutdown P((int, struct peer *));
+static void true_receive P((struct recvbuf *));
+static void true_poll P((int, struct peer *));
+static void true_send P((struct peer *, const char *));
+static void true_doevent P((struct peer *, enum true_event));
+
+#ifdef CLOCK_PPS720
+static u_long true_sample720 P((void));
+#endif
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_true = {
+ true_start, /* start up driver */
+ true_shutdown, /* shut down driver */
+ true_poll, /* transmit poll message */
+ noentry, /* not used (old true_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old true_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+#if !defined(__STDC__)
+# define true_debug (void)
+#else
+static void
+true_debug(struct peer *peer, const char *fmt, ...)
+{
+ va_list ap;
+ int want_debugging, now_debugging;
+ struct refclockproc *pp;
+ struct true_unit *up;
+
+ va_start(ap, fmt);
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+
+ want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0;
+ now_debugging = (up->debug != NULL);
+ if (want_debugging != now_debugging)
+ {
+ if (want_debugging) {
+ char filename[40];
+ int fd;
+
+ snprintf(filename, sizeof(filename), "/tmp/true%d.debug", up->unit);
+ fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0600);
+ if (fd >= 0 && (up->debug = fdopen(fd, "r+"))) {
+#ifdef HAVE_SETVBUF
+ static char buf[BUFSIZ];
+ setvbuf(up->debug, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(up->debug);
+#endif
+ }
+ } else {
+ fclose(up->debug);
+ up->debug = NULL;
+ }
+ }
+
+ if (up->debug) {
+ fprintf(up->debug, "true%d: ", up->unit);
+ vfprintf(up->debug, fmt, ap);
+ }
+}
+#endif /*STDC*/
+
+/*
+ * true_start - open the devices and initialize data for processing
+ */
+static int
+true_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct true_unit *up;
+ struct refclockproc *pp;
+ char device[40];
+ int fd;
+
+ /*
+ * Open serial port
+ */
+ (void)snprintf(device, sizeof(device), DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct true_unit *)
+ emalloc(sizeof(struct true_unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct true_unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = true_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ up->type = t_unknown;
+ up->state = s_Base;
+ true_doevent(peer, e_Init);
+ return (1);
+}
+
+/*
+ * true_shutdown - shut down the clock
+ */
+static void
+true_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct true_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * true_receive - receive data from the serial interface on a clock
+ */
+static void
+true_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct true_unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ u_short new_station;
+ char synced;
+ int i;
+ int lat, lon, off; /* GOES Satellite position */
+ /* Use these variable to hold data until we decide its worth keeping */
+ char rd_lastcode[BMAX];
+ l_fp rd_tmp;
+ u_short rd_lencode;
+
+ /*
+ * Get the clock this applies to and pointers to the data.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+
+ /*
+ * Read clock output. Automatically handles STREAMS, CLKLDISC.
+ */
+ rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
+ rd_lastcode[rd_lencode] = '\0';
+
+ /*
+ * There is a case where <cr><lf> generates 2 timestamps.
+ */
+ if (rd_lencode == 0)
+ return;
+ pp->lencode = rd_lencode;
+ strcpy(pp->a_lastcode, rd_lastcode);
+ pp->lastrec = rd_tmp;
+ true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, pp->lencode);
+
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code decodes a multitude of different
+ * clock messages. Timecodes are processed if needed. All replies
+ * will be run through the state machine to tweak driver options
+ * and program the clock.
+ */
+
+ /*
+ * Clock misunderstood our last command?
+ */
+ if (pp->a_lastcode[0] == '?' ||
+ strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) {
+ true_doevent(peer, e_Huh);
+ return;
+ }
+
+ /*
+ * Timecode: "nnnnn+nnn-nnn"
+ * (from GOES clock when asked about satellite position)
+ */
+ if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') &&
+ (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') &&
+ sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3
+ ) {
+ const char *label = "Botch!";
+
+ /*
+ * This is less than perfect. Call the (satellite)
+ * either EAST or WEST and adjust slop accodingly
+ * Perfectionists would recalculate the exact delay
+ * and adjust accordingly...
+ */
+ if (lon > 7000 && lon < 14000) {
+ if (lon < 10000) {
+ new_station = GOES_EAST;
+ label = "EAST";
+ } else {
+ new_station = GOES_WEST;
+ label = "WEST";
+ }
+
+ if (new_station != up->station) {
+ double dtemp;
+
+ dtemp = pp->fudgetime1;
+ pp->fudgetime1 = pp->fudgetime2;
+ pp->fudgetime2 = dtemp;
+ up->station = new_station;
+ }
+ }
+ else {
+ /*refclock_report(peer, CEVNT_BADREPLY);*/
+ label = "UNKNOWN";
+ }
+ true_debug(peer, "GOES: station %s\n", label);
+ true_doevent(peer, e_Satellite);
+ return;
+ }
+
+ /*
+ * Timecode: "Fnn"
+ * (from TM/TMD clock when it wants to tell us what it's up to.)
+ */
+ if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) {
+ switch (i) {
+ case 50:
+ true_doevent(peer, e_F50);
+ break;
+ case 51:
+ true_doevent(peer, e_F51);
+ break;
+ default:
+ true_debug(peer, "got F%02d - ignoring\n", i);
+ break;
+ }
+ return;
+ }
+
+ /*
+ * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
+ * (from a TM/TMD/XL clock during initialization.)
+ */
+ if (strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 ||
+ strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
+ true_doevent(peer, e_F18);
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode);
+ }
+ return;
+ }
+
+ /*
+ * Timecode: "N03726428W12209421+000033"
+ * 1 2
+ * 0123456789012345678901234
+ * (from a TCU during initialization)
+ */
+ if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') &&
+ (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') &&
+ pp->a_lastcode[18] == '+') {
+ true_doevent(peer, e_Location);
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode);
+ }
+ return;
+ }
+ /*
+ * Timecode: "ddd:hh:mm:ssQ"
+ * (from all clocks supported by this driver.)
+ */
+ if (pp->a_lastcode[3] == ':' &&
+ pp->a_lastcode[6] == ':' &&
+ pp->a_lastcode[9] == ':' &&
+ sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c",
+ &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &synced) == 5) {
+
+ /*
+ * Adjust the synchronize indicator according to timecode
+ * say were OK, and then say not if we really are not OK
+ */
+ if (synced == '>' || synced == '#' || synced == '?')
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ true_doevent(peer, e_TS);
+
+#ifdef CLOCK_PPS720
+ /* If it's taken more than 65ms to get here, we'll lose. */
+ if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) {
+ l_fp off;
+
+#ifdef CLOCK_ATOM
+ /*
+ * find out what time it really is. Include
+ * the count from the PCL720
+ */
+ if (!clocktime(pp->day, pp->hour, pp->minute,
+ pp->second, GMT, pp->lastrec.l_ui,
+ &pp->yearstart, &off.l_ui)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ off.l_uf = 0;
+#endif
+
+ pp->usec = true_sample720();
+#ifdef CLOCK_ATOM
+ TVUTOTSF(pp->usec, off.l_uf);
+#endif
+
+ /*
+ * Stomp all over the timestamp that was pulled out
+ * of the input stream. It's irrelevant since we've
+ * adjusted the input time to reflect now (via pp->usec)
+ * rather than when the data was collected.
+ */
+ get_systime(&pp->lastrec);
+#ifdef CLOCK_ATOM
+ /*
+ * Create a true offset for feeding to pps_sample()
+ */
+ L_SUB(&off, &pp->lastrec);
+
+ pps_sample(peer, &off);
+#endif
+ true_debug(peer, "true_sample720: %luus\n", pp->usec);
+ }
+#endif
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!up->polled)
+ return;
+
+ true_doevent(peer, e_Poll);
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ /*
+ * If clock is good we send a NOMINAL message so that
+ * any previous BAD messages are nullified
+ */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ refclock_report(peer, CEVNT_NOMINAL);
+
+ /*
+ * We have succedded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+
+ return;
+ }
+
+ /*
+ * No match to known timecodes, report failure and return
+ */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+}
+
+
+/*
+ * true_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+true_send(
+ struct peer *peer,
+ const char *cmd
+ )
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ register int len = strlen(cmd);
+
+ true_debug(peer, "Send '%s'\n", cmd);
+ if (write(pp->io.fd, cmd, (unsigned)len) != len)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+ }
+}
+
+
+/*
+ * state machine for initializing and controlling a clock
+ */
+static void
+true_doevent(
+ struct peer *peer,
+ enum true_event event
+ )
+{
+ struct true_unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+ if (event != e_TS) {
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s",
+ typeStr(up->type),
+ stateStr(up->state),
+ eventStr(event));
+ }
+ }
+ true_debug(peer, "clock %s, state %s, event %s\n",
+ typeStr(up->type), stateStr(up->state), eventStr(event));
+ switch (up->type) {
+ case t_goes:
+ switch (event) {
+ case e_Init: /* FALLTHROUGH */
+ case e_Satellite:
+ /*
+ * Switch back to on-second time codes and return.
+ */
+ true_send(peer, "C");
+ up->state = s_Start;
+ break;
+ case e_Poll:
+ /*
+ * After each poll, check the station (satellite).
+ */
+ true_send(peer, "P");
+ /* No state change needed. */
+ break;
+ default:
+ break;
+ }
+ /* FALLTHROUGH */
+ case t_omega:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "C");
+ up->state = s_Start;
+ break;
+ case e_TS:
+ if (up->state != s_Start && up->state != s_Auto) {
+ true_send(peer, "\03\r");
+ break;
+ }
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_tm:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "F18\r");
+ up->state = s_Init;
+ break;
+ case e_F18:
+ true_send(peer, "F50\r");
+ up->state = s_F18;
+ break;
+ case e_F50:
+ true_send(peer, "F51\r");
+ up->state = s_F50;
+ break;
+ case e_F51:
+ true_send(peer, "F08\r");
+ up->state = s_Start;
+ break;
+ case e_TS:
+ if (up->state != s_Start && up->state != s_Auto) {
+ true_send(peer, "\03\r");
+ break;
+ }
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_tcu:
+ switch (event) {
+ case e_Init:
+ true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */
+ true_send(peer, "TSU\r"); /* UTC, not GPS. */
+ true_send(peer, "AU\r"); /* Auto Timestamps. */
+ up->state = s_Start;
+ break;
+ case e_TS:
+ if (up->state != s_Start && up->state != s_Auto) {
+ true_send(peer, "\03\r");
+ break;
+ }
+ up->state = s_Auto;
+ break;
+ default:
+ break;
+ }
+ break;
+ case t_unknown:
+ switch (up->state) {
+ case s_Base:
+ if (event != e_Init)
+ abort();
+ true_send(peer, "P\r");
+ up->state = s_InqGOES;
+ break;
+ case s_InqGOES:
+ switch (event) {
+ case e_Satellite:
+ up->type = t_goes;
+ true_doevent(peer, e_Init);
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh: /*FALLTHROUGH*/
+ case e_TS:
+ up->state = s_InqOmega;
+ true_send(peer, "C\r");
+ break;
+ default:
+ abort();
+ }
+ break;
+ case s_InqOmega:
+ switch (event) {
+ case e_TS:
+ up->type = t_omega;
+ up->state = s_Auto; /* Inq side-effect. */
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ up->state = s_InqTM;
+ true_send(peer, "F18\r");
+ break;
+ default:
+ abort();
+ }
+ break;
+ case s_InqTM:
+ switch (event) {
+ case e_F18:
+ up->type = t_tm;
+ true_doevent(peer, e_Init);
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ true_send(peer, "PO\r");
+ up->state = s_InqTCU;
+ break;
+ default:
+ abort();
+ }
+ break;
+ case s_InqTCU:
+ switch (event) {
+ case e_Location:
+ up->type = t_tcu;
+ true_doevent(peer, e_Init);
+ break;
+ case e_Init: /*FALLTHROUGH*/
+ case e_Huh:
+ up->state = s_Base;
+ sleep(1); /* XXX */
+ break;
+ default:
+ abort();
+ }
+ break;
+ /*
+ * An expedient hack to prevent lint complaints,
+ * these don't actually need to be used here...
+ */
+ case s_Init:
+ case s_F18:
+ case s_F50:
+ case s_Start:
+ case s_Auto:
+ case s_Max:
+ msyslog(LOG_INFO, "TRUE: state %s is unexpected!", stateStr(up->state));
+ }
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+
+#ifdef CLOCK_PPS720
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) {
+ /* Make counter trigger on gate0, count down from 65535. */
+ pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535);
+ /*
+ * (These constants are OK since
+ * they represent hardware maximums.)
+ */
+ NLOG(NLOG_CLOCKINFO) {
+ msyslog(LOG_NOTICE, "PCL-720 initialized");
+ }
+ up->pcl720init++;
+ }
+#endif
+
+
+}
+
+/*
+ * true_poll - called by the transmit procedure
+ */
+static void
+true_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct true_unit *up;
+ struct refclockproc *pp;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+ pp = peer->procptr;
+ up = (struct true_unit *)pp->unitptr;
+ if (up->pollcnt > 0)
+ up->pollcnt--;
+ else {
+ true_doevent(peer, e_Init);
+ refclock_report(peer, CEVNT_TIMEOUT);
+ }
+
+ /*
+ * polled every 64 seconds. Ask true_receive to hand in a
+ * timestamp.
+ */
+ up->polled = 1;
+ pp->polls++;
+}
+
+#ifdef CLOCK_PPS720
+/*
+ * true_sample720 - sample the PCL-720
+ */
+static u_long
+true_sample720(void)
+{
+ unsigned long f;
+
+ /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3.
+ * If it is not being held low now, we did not get called
+ * within 65535us.
+ */
+ if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) {
+ NLOG(NLOG_CLOCKINFO) {
+ msyslog(LOG_NOTICE, "PCL-720 out of synch");
+ }
+ return (0);
+ }
+ f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR));
+#ifdef PPS720_DEBUG
+ msyslog(LOG_DEBUG, "PCL-720: %luus", f);
+#endif
+ return (f);
+}
+#endif
+
+#else
+int refclock_true_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_tt560.c b/ntpd/refclock_tt560.c
new file mode 100644
index 0000000..f3d7bc1
--- /dev/null
+++ b/ntpd/refclock_tt560.c
@@ -0,0 +1,274 @@
+/*
+ * refclock_tt560 - clock driver for the TrueTime 560 IRIG-B decoder
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_TT560)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "sys/tt560_api.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the TrueTime 560 IRIG-B decoder for the PCI bus.
+ */
+
+/*
+ * TT560 interface definitions
+ */
+#define DEVICE "/dev/tt560%d" /* device name and unit */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "TrueTime 560 IRIG-B PCI Decoder"
+
+/*
+ * Unit control structure
+ */
+struct tt560unit {
+ tt_mem_space_t *tt_mem; /* mapped address of PCI board */
+ time_freeze_reg_t tt560rawt; /* data returned from PCI board */
+};
+
+typedef union byteswap_u
+{
+ unsigned int long_word;
+ unsigned char byte[4];
+} byteswap_t;
+
+/*
+ * Function prototypes
+ */
+static int tt560_start P((int, struct peer *));
+static void tt560_shutdown P((int, struct peer *));
+static void tt560_poll P((int unit, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_tt560 = {
+ tt560_start, /* clock_start */
+ tt560_shutdown, /* clock_shutdown */
+ tt560_poll, /* clock_poll */
+ noentry, /* clock_control (not used) */
+ noentry, /* clock_init (not used) */
+ noentry, /* clock_buginfo (not used) */
+ NOFLAGS /* clock_flags (not used) */
+};
+
+
+/*
+ * tt560_start - open the TT560 device and initialize data for processing
+ */
+static int
+tt560_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tt560unit *up;
+ struct refclockproc *pp;
+ char device[20];
+ int fd;
+ caddr_t membase;
+
+ /*
+ * Open TT560 device
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDWR);
+ if (fd == -1) {
+ msyslog(LOG_ERR, "tt560_start: open of %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Map the device registers into user space.
+ */
+ membase = mmap ((caddr_t) 0, TTIME_MEMORY_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, (off_t)0);
+
+ if (membase == (caddr_t) -1) {
+ msyslog(LOG_ERR, "tt560_start: mapping of %s: %m", device);
+ (void) close(fd);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct tt560unit *) emalloc(sizeof(struct tt560unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct tt560unit));
+ up->tt_mem = (tt_mem_space_t *)membase;
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * tt560_shutdown - shut down the clock
+ */
+static void
+tt560_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tt560unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct tt560unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * tt560_poll - called by the transmit procedure
+ */
+static void
+tt560_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct tt560unit *up;
+ struct refclockproc *pp;
+ time_freeze_reg_t *tp;
+ tt_mem_space_t *mp;
+
+ int i;
+ unsigned int *p_time_t, *tt_mem_t;
+
+ /*
+ * This is the main routine. It snatches the time from the TT560
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct tt560unit *)pp->unitptr;
+ mp = up->tt_mem;
+ tp = &up->tt560rawt;
+
+ p_time_t = (unsigned int *)tp;
+ tt_mem_t = (unsigned int *)&mp->time_freeze_reg;
+
+ *tt_mem_t = 0; /* update the time freeze register */
+ /* and copy time stamp to memory */
+ for (i=0; i < TIME_FREEZE_REG_LEN; i++) {
+ *p_time_t = byte_swap(*tt_mem_t);
+ p_time_t++;
+ tt_mem_t++;
+ }
+
+ get_systime(&pp->lastrec);
+ pp->polls++;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit. Note: we
+ * can't use the sec/usec conversion produced by the driver,
+ * since the year may be suspect. All format error checking is
+ * done by the sprintf() and sscanf() routines.
+ */
+ sprintf(pp->a_lastcode,
+ "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
+ tp->hun_day, tp->tens_day, tp->unit_day,
+ tp->tens_hour, tp->unit_hour,
+ tp->tens_min, tp->unit_min,
+ tp->tens_sec, tp->unit_sec,
+ tp->hun_ms, tp->tens_ms, tp->unit_ms,
+ tp->hun_us, tp->tens_us, tp->unit_us,
+ tp->status);
+ pp->lencode = strlen(pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("tt560: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->a_lastcode);
+#endif
+ if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld",
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->usec)
+ != 5) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if ((tp->status & 0x6) != 0x6)
+ pp->leap = LEAP_NOTINSYNC;
+ else
+ pp->leap = LEAP_NOWARNING;
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ peer->burst = NSTAGE;
+}
+
+/******************************************************************
+ *
+ * byte_swap
+ *
+ * Inputs: 32 bit integer
+ *
+ * Output: byte swapped 32 bit integer.
+ *
+ * This routine is used to compensate for the byte alignment
+ * differences between big-endian and little-endian integers.
+ *
+ ******************************************************************/
+static unsigned int
+byte_swap(unsigned int input_num)
+{
+ byteswap_t byte_swap;
+ unsigned char temp;
+
+ byte_swap.long_word = input_num;
+
+ temp = byte_swap.byte[3];
+ byte_swap.byte[3] = byte_swap.byte[0];
+ byte_swap.byte[0] = temp;
+
+ temp = byte_swap.byte[2];
+ byte_swap.byte[2] = byte_swap.byte[1];
+ byte_swap.byte[1] = temp;
+
+ return (byte_swap.long_word);
+}
+
+#else
+int refclock_tt560_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_ulink.c b/ntpd/refclock_ulink.c
new file mode 100644
index 0000000..1f5e78a
--- /dev/null
+++ b/ntpd/refclock_ulink.c
@@ -0,0 +1,497 @@
+/*
+ * refclock_ulink - clock driver for Ultralink WWVB receiver
+ *
+ */
+
+/***********************************************************************
+ * *
+ * Copyright (c) David L. Mills 1992-1998 *
+ * *
+ * Permission to use, copy, modify, and distribute this software and *
+ * its documentation for any purpose and without fee is hereby *
+ * granted, provided that the above copyright notice appears in all *
+ * copies and that both the copyright notice and this permission *
+ * notice appear in supporting documentation, and that the name *
+ * University of Delaware not be used in advertising or publicity *
+ * pertaining to distribution of the software without specific, *
+ * written prior permission. The University of Delaware makes no *
+ * representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied *
+ * warranty. *
+ **********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ULINK)
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports ultralink Model 320,330,331,332 WWVB radios
+ *
+ * this driver was based on the refclock_wwvb.c driver
+ * in the ntp distribution.
+ *
+ * Fudge Factors
+ *
+ * fudge flag1 0 don't poll clock
+ * 1 send poll character
+ *
+ * revision history:
+ * 99/9/09 j.c.lang original edit's
+ * 99/9/11 j.c.lang changed timecode parse to
+ * match what the radio actually
+ * sends.
+ * 99/10/11 j.c.lang added support for continous
+ * time code mode (dipsw2)
+ * 99/11/26 j.c.lang added support for 320 decoder
+ * (taken from Dave Strout's
+ * Model 320 driver)
+ * 99/11/29 j.c.lang added fudge flag 1 to control
+ * clock polling
+ * 99/12/15 j.c.lang fixed 320 quality flag
+ * 01/02/21 s.l.smith fixed 33x quality flag
+ * added more debugging stuff
+ * updated 33x time code explanation
+ *
+ * Questions, bugs, ideas send to:
+ * Joseph C. Lang
+ * tcnojl1@earthlink.net
+ *
+ * Dave Strout
+ * dstrout@linuxfoundry.com
+ *
+ *
+ * on the Ultralink model 33X decoder Dip switch 2 controls
+ * polled or continous timecode
+ * set fudge flag1 if using polled (needed for model 320)
+ * dont set fudge flag1 if dip switch 2 is set on model 33x decoder
+*/
+
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwvb%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 10 ms) */
+#define REFID "WWVB" /* reference ID */
+#define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */
+
+#define LEN33X 32 /* timecode length Model 325 & 33X */
+#define LEN320 24 /* timecode length Model 320 */
+
+/*
+ * unit control structure
+ */
+struct ulinkunit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+};
+
+/*
+ * Function prototypes
+ */
+static int ulink_start P((int, struct peer *));
+static void ulink_shutdown P((int, struct peer *));
+static void ulink_receive P((struct recvbuf *));
+static void ulink_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_ulink = {
+ ulink_start, /* start up driver */
+ ulink_shutdown, /* shut down driver */
+ ulink_poll, /* transmit poll message */
+ noentry, /* not used */
+ noentry, /* not used */
+ noentry, /* not used */
+ NOFLAGS
+};
+
+
+/*
+ * ulink_start - open the devices and initialize data for processing
+ */
+static int
+ulink_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct ulinkunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct ulinkunit *)
+ emalloc(sizeof(struct ulinkunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct ulinkunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = ulink_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * ulink_shutdown - shut down the clock
+ */
+static void
+ulink_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct ulinkunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct ulinkunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * ulink_receive - receive data from the serial interface
+ */
+static void
+ulink_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct ulinkunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int quality; /* quality indicator */
+ int temp; /* int temp */
+ char syncchar; /* synchronization indicator */
+ char leapchar; /* leap indicator */
+ char modechar; /* model 320 mode flag */
+ char char_quality[2]; /* temp quality flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct ulinkunit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained.
+ */
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+#ifdef DEBUG
+ if (debug)
+ printf("ulink: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ syncchar = leapchar = modechar = ' ';
+ switch (pp->lencode ) {
+ case LEN33X:
+ /*
+ * Model 33X decoder:
+ * Timecode format from January 29, 2001 datasheet is:
+ * <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5
+ * S WWVB decoder sync indicator. S for in-sync(?)
+ * or N for noisy signal.
+ * 9+ RF signal level in S-units, 0-9 followed by
+ * a space (0x20). The space turns to '+' if the
+ * level is over 9.
+ * D Data bit 0, 1, 2 (position mark), or
+ * 3 (unknown).
+ * space Space character (0x20)
+ * 00 Hours since last good WWVB frame sync. Will
+ * be 00-23 hrs, or '1d' to '7d'. Will be 'Lk'
+ * if currently in sync.
+ * space Space character (0x20)
+ * YYYY Current year, 1990-2089
+ * + Leap year indicator. '+' if a leap year,
+ * a space (0x20) if not.
+ * DDD Day of year, 001 - 366.
+ * UTC Timezone (always 'UTC').
+ * S Daylight savings indicator
+ * S - standard time (STD) in effect
+ * O - during STD to DST day 0000-2400
+ * D - daylight savings time (DST) in effect
+ * I - during DST to STD day 0000-2400
+ * space Space character (0x20)
+ * HH Hours 00-23
+ * : This is the REAL in sync indicator (: = insync)
+ * MM Minutes 00-59
+ * : : = in sync ? = NOT in sync
+ * SS Seconds 00-59
+ * L Leap second flag. Changes from space (0x20)
+ * to '+' or '-' during month preceding leap
+ * second adjustment.
+ * +5 UT1 correction (sign + digit ))
+ */
+
+ if (sscanf(pp->a_lastcode,
+ "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
+ char_quality, &pp->year, &pp->day,
+ &pp->hour, &syncchar, &pp->minute, &pp->second,
+ &leapchar) == 8) {
+
+ if (char_quality[0] == 'L') {
+ quality = 0;
+ } else if (char_quality[0] == '0') {
+ quality = (char_quality[1] & 0x0f);
+ } else {
+ quality = 99;
+ }
+
+/*
+#ifdef DEBUG
+ if (debug) {
+ printf("ulink: char_quality %c %c\n",
+ char_quality[0], char_quality[1]);
+ printf("ulink: quality %d\n", quality);
+ printf("ulink: syncchar %x\n", syncchar);
+ printf("ulink: leapchar %x\n", leapchar);
+ }
+#endif
+*/
+
+ break;
+ }
+
+ case LEN320:
+ /*
+ * Model 320 Decoder
+ * The timecode format is:
+ *
+ * <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
+ *
+ * where:
+ *
+ * S = 'S' -- sync'd in last hour,
+ * '0'-'9' - hours x 10 since last update,
+ * '?' -- not in sync
+ * Q = Number of correlating time-frames, from 0 to 5
+ * R = 'R' -- reception in progress,
+ * 'N' -- Noisy reception,
+ * ' ' -- standby mode
+ * YYYY = year from 1990 to 2089
+ * DDD = current day from 1 to 366
+ * + = '+' if current year is a leap year, else ' '
+ * HH = UTC hour 0 to 23
+ * MM = Minutes of current hour from 0 to 59
+ * SS = Seconds of current minute from 0 to 59
+ * mm = 10's milliseconds of the current second from 00 to 99
+ * L = Leap second pending at end of month
+ * 'I' = insert, 'D'= delete
+ * T = DST <-> STD transition indicators
+ *
+ */
+ if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c",
+ &syncchar, &quality, &modechar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second,
+ &pp->nsec, &leapchar) == 10) {
+ pp->nsec *= 10000000; /* M320 returns 10's of msecs */
+ if (leapchar == 'I' ) leapchar = '+';
+ if (leapchar == 'D' ) leapchar = '-';
+ if (syncchar != '?' ) syncchar = ':';
+
+ break;
+ }
+
+ default:
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+
+ /*
+ * Decode quality indicator
+ * For the 325 & 33x series, the lower the number the "better"
+ * the time is. I used the dispersion as the measure of time
+ * quality. The quality indicator in the 320 is the number of
+ * correlating time frames (the more the better)
+ */
+
+ /*
+ * The spec sheet for the 325 & 33x series states the clock will
+ * maintain +/-0.002 seconds accuracy when locked to WWVB. This
+ * is indicated by 'Lk' in the quality portion of the incoming
+ * string. When not in lock, a drift of +/-0.015 seconds should
+ * be allowed for.
+ * With the quality indicator decoding scheme above, the 'Lk'
+ * condition will produce a quality value of 0. If the quality
+ * indicator starts with '0' then the second character is the
+ * number of hours since we were last locked. If the first
+ * character is anything other than 'L' or '0' then we have been
+ * out of lock for more than 9 hours so we assume the worst and
+ * force a quality value that selects the 'default' maximum
+ * dispersion. The dispersion values below are what came with the
+ * driver. They're not unreasonable so they've not been changed.
+ */
+
+ if (pp->lencode == LEN33X) {
+ switch (quality) {
+ case 0 :
+ pp->disp=.002;
+ break;
+ case 1 :
+ pp->disp=.02;
+ break;
+ case 2 :
+ pp->disp=.04;
+ break;
+ case 3 :
+ pp->disp=.08;
+ break;
+ default:
+ pp->disp=MAXDISPERSE;
+ break;
+ }
+ } else {
+ switch (quality) {
+ case 5 :
+ pp->disp=.002;
+ break;
+ case 4 :
+ pp->disp=.02;
+ break;
+ case 3 :
+ pp->disp=.04;
+ break;
+ case 2 :
+ pp->disp=.08;
+ break;
+ case 1 :
+ pp->disp=.16;
+ break;
+ default:
+ pp->disp=MAXDISPERSE;
+ break;
+ }
+
+ }
+
+ /*
+ * Decode synchronization, and leap characters. If
+ * unsynchronized, set the leap bits accordingly and exit.
+ * Otherwise, set the leap bits according to the leap character.
+ */
+
+ if (syncchar != ':')
+ pp->leap = LEAP_NOTINSYNC;
+ else if (leapchar == '+')
+ pp->leap = LEAP_ADDSECOND;
+ else if (leapchar == '-')
+ pp->leap = LEAP_DELSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ }
+
+}
+
+
+/*
+ * ulink_poll - called by the transmit procedure
+ */
+static void
+ulink_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ char pollchar;
+
+ pp = peer->procptr;
+ pollchar = 'T';
+ if (pp->sloppyclockflag & CLK_FLAG1) {
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+ }
+ else
+ pp->polls++;
+
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ peer->burst = NSTAGE;
+
+}
+
+#else
+int refclock_ulink_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_usno.c b/ntpd/refclock_usno.c
new file mode 100644
index 0000000..057eef9
--- /dev/null
+++ b/ntpd/refclock_usno.c
@@ -0,0 +1,674 @@
+/*
+ * refclock_usno - clock driver for the Naval Observatory dialup
+ * Michael Shields <shields@tembel.org> 1995/02/25
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_USNO)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_control.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+/*
+ * This driver supports the Naval Observatory dialup at +1 202 653 0351.
+ * It is a hacked-up version of the ACTS driver.
+ *
+ * This driver does not support the `phone' configuration because that
+ * is needlessly global; it would clash with the ACTS driver.
+ *
+ * The Naval Observatory does not support the echo-delay measurement scheme.
+ *
+ * However, this driver *does* support UUCP port locking, allowing the
+ * line to be shared with other processes when not actually dialing
+ * for time.
+ */
+
+/*
+ * Interface definitions
+ */
+
+#define DEVICE "/dev/cua%d" /* device name and unit */
+#define LOCKFILE "/var/lock/LCK..cua%d"
+/* #define LOCKFILE "/usr/spool/uucp/LCK..cua%d" */
+
+#define PHONE "atdt 202 653 0351"
+/* #define PHONE "atdt 1 202 653 0351" */
+
+#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "USNO" /* reference ID */
+#define DESCRIPTION "Naval Observatory dialup"
+
+#define MODE_AUTO 0 /* automatic mode */
+#define MODE_BACKUP 1 /* backup mode */
+#define MODE_MANUAL 2 /* manual mode */
+
+#define MSGCNT 10 /* we need this many time messages */
+#define SMAX 80 /* max token string length */
+#define LENCODE 20 /* length of valid timecode string */
+#define USNO_MINPOLL 10 /* log2 min poll interval (1024 s) */
+#define USNO_MAXPOLL 14 /* log2 max poll interval (16384 s) */
+#define MAXOUTAGE 3600 /* max before USNO kicks in (s) */
+
+/*
+ * Modem control strings. These may have to be changed for some modems.
+ *
+ * AT command prefix
+ * B1 initiate call negotiation using Bell 212A
+ * &C1 enable carrier detect
+ * &D2 hang up and return to command mode on DTR transition
+ * E0 modem command echo disabled
+ * l1 set modem speaker volume to low level
+ * M1 speaker enabled untill carrier detect
+ * Q0 return result codes
+ * V1 return result codes as English words
+ */
+#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
+#define MODEM_HANGUP "ATH" /* modem disconnect */
+
+/*
+ * Timeouts
+ */
+#define IDLE 60 /* idle timeout (s) */
+#define WAIT 2 /* wait timeout (s) */
+#define ANSWER 30 /* answer timeout (s) */
+#define CONNECT 10 /* connect timeout (s) */
+#define TIMECODE (MSGCNT+16) /* timecode timeout (s) */
+
+/*
+ * Unit control structure
+ */
+struct usnounit {
+ int pollcnt; /* poll message counter */
+
+ int state; /* the first one was Delaware */
+ int run; /* call program run switch */
+ int msgcnt; /* count of time messages received */
+ long redial; /* interval to next automatic call */
+ int unit; /* unit number (= port) */
+};
+
+/*
+ * Function prototypes
+ */
+static int usno_start P((int, struct peer *));
+static void usno_shutdown P((int, struct peer *));
+static void usno_poll P((int, struct peer *));
+static void usno_disc P((struct peer *));
+#if 0
+static void usno_timeout P((struct peer *));
+static void usno_receive P((struct recvbuf *));
+static int usno_write P((struct peer *, const char *));
+#endif /* 0 */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_usno = {
+ usno_start, /* start up driver */
+ usno_shutdown, /* shut down driver */
+ usno_poll, /* transmit poll message */
+ noentry, /* not used (usno_control) */
+ noentry, /* not used (usno_init) */
+ noentry, /* not used (usno_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * usno_start - open the devices and initialize data for processing
+ */
+static int
+usno_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ pp = peer->procptr;
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->minpoll = USNO_MINPOLL;
+ peer->maxpoll = USNO_MAXPOLL;
+ peer->sstclktype = CTL_SST_TS_TELEPHONE;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct usnounit *)
+ emalloc(sizeof(struct usnounit))))
+ return (0);
+ memset((char *)up, 0, sizeof(struct usnounit));
+ up->unit = unit;
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Set up the driver timeout
+ */
+ peer->nextdate = current_time + WAIT;
+ return (1);
+}
+
+
+/*
+ * usno_shutdown - shut down the clock
+ */
+static void
+usno_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+
+#ifdef DEBUG
+ if (debug)
+ printf("usno: clock %s shutting down\n", ntoa(&peer->srcadr));
+#endif
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ usno_disc(peer);
+ free(up);
+}
+
+
+#if 0
+/*
+ * usno_receive - receive data from the serial interface
+ */
+static void
+usno_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char str[SMAX];
+ u_long mjd; /* Modified Julian Day */
+ static int day, hour, minute, second;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. If
+ * the OK modem status code, leave it where folks can find it.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &pp->lastrec);
+ if (pp->lencode == 0) {
+ if (strcmp(pp->a_lastcode, "OK") == 0)
+ pp->lencode = 2;
+ return;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("usno: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+
+ switch (up->state) {
+
+ case 0:
+
+ /*
+ * State 0. We are not expecting anything. Probably
+ * modem disconnect noise. Go back to sleep.
+ */
+ return;
+
+ case 1:
+
+ /*
+ * State 1. We are about to dial. Just drop it.
+ */
+ return;
+
+ case 2:
+
+ /*
+ * State 2. We are waiting for the call to be answered.
+ * All we care about here is CONNECT as the first token
+ * in the string. If the modem signals BUSY, ERROR, NO
+ * ANSWER, NO CARRIER or NO DIALTONE, we immediately
+ * hang up the phone. If CONNECT doesn't happen after
+ * ANSWER seconds, hang up the phone. If everything is
+ * okay, start the connect timeout and slide into state
+ * 3.
+ */
+ (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX);
+ if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
+ 0 || strcmp(str, "NO") == 0) {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s USNO modem status %s",
+ ntoa(&peer->srcadr), pp->a_lastcode);
+ usno_disc(peer);
+ } else if (strcmp(str, "CONNECT") == 0) {
+ peer->nextdate = current_time + CONNECT;
+ up->msgcnt = 0;
+ up->state++;
+ } else {
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_WARNING,
+ "clock %s USNO unknown modem status %s",
+ ntoa(&peer->srcadr), pp->a_lastcode);
+ }
+ return;
+
+ case 3:
+
+ /*
+ * State 3. The call has been answered and we are
+ * waiting for the first message. If this doesn't
+ * happen within the timecode timeout, hang up the
+ * phone. We probably got a wrong number or they are
+ * down.
+ */
+ peer->nextdate = current_time + TIMECODE;
+ up->state++;
+ return;
+
+ case 4:
+
+ /*
+ * State 4. We are reading a timecode. It's an actual
+ * timecode, or it's the `*' OTM.
+ *
+ * jjjjj nnn hhmmss UTC
+ */
+ if (pp->lencode == LENCODE) {
+ if (sscanf(pp->a_lastcode, "%5ld %3d %2d%2d%2d UTC",
+ &mjd, &day, &hour, &minute, &second) != 5) {
+#ifdef DEBUG
+ if (debug)
+ printf("usno: bad timecode format\n");
+#endif
+ refclock_report(peer, CEVNT_BADREPLY);
+ } else
+ up->msgcnt++;
+ return;
+ } else if (pp->lencode != 1 || !up->msgcnt)
+ return;
+ /* else, OTM; drop out of switch */
+ }
+
+ pp->leap = LEAP_NOWARNING;
+ pp->day = day;
+ pp->hour = hour;
+ pp->minute = minute;
+ pp->second = second;
+
+ /*
+ * Colossal hack here. We process each sample in a trimmed-mean
+ * filter and determine the reference clock offset and
+ * dispersion. The fudge time1 value is added to each sample as
+ * received.
+ */
+ if (!refclock_process(pp)) {
+#ifdef DEBUG
+ if (debug)
+ printf("usno: time rejected\n");
+#endif
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } else if (up->msgcnt < MSGCNT)
+ return;
+
+ /*
+ * We have a filtered sample offset ready for peer processing.
+ * We use lastrec as both the reference time and receive time in
+ * order to avoid being cute, like setting the reference time
+ * later than the receive time, which may cause a paranoid
+ * protocol module to chuck out the data. Finaly, we unhook the
+ * timeout, arm for the next call, fold the tent and go home.
+ */
+ pp->lastref = pp->lastrec;
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ up->pollcnt = 0;
+ up->state = 0;
+ usno_disc(peer);
+}
+#endif /* 0 */
+
+
+/*
+ * usno_poll - called by the transmit routine
+ */
+static void
+usno_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+
+ /*
+ * If the driver is running, we set the enable flag (fudge
+ * flag1), which causes the driver timeout routine to initiate a
+ * call. If not, the enable flag can be set using
+ * ntpdc. If this is the sustem peer, then follow the system
+ * poll interval.
+ */
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ if (up->run) {
+ pp->sloppyclockflag |= CLK_FLAG1;
+ if (peer == sys_peer)
+ peer->hpoll = sys_poll;
+ else
+ peer->hpoll = peer->minpoll;
+ }
+}
+
+
+#if 0
+/*
+ * usno_timeout - called by the timer interrupt
+ */
+static void
+usno_timeout(
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ char lockfile[128], pidbuf[8];
+ int dtr = TIOCM_DTR;
+
+ /*
+ * If a timeout occurs in other than state 0, the call has
+ * failed. If in state 0, we just see if there is other work to
+ * do.
+ */
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ if (up->state) {
+ if (up->state != 1) {
+ usno_disc(peer);
+ return;
+ }
+ /*
+ * Call, and start the answer timeout. We think it
+ * strange if the OK status has not been received from
+ * the modem, but plow ahead anyway.
+ *
+ * This code is *here* because we had to stick in a brief
+ * delay to let the modem settle down after raising DTR,
+ * and for the OK to be received. State machines are
+ * contorted.
+ */
+ if (strcmp(pp->a_lastcode, "OK") != 0)
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "clock %s USNO no modem status",
+ ntoa(&peer->srcadr));
+ (void)usno_write(peer, PHONE);
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE, "clock %s USNO calling %s\n",
+ ntoa(&peer->srcadr), PHONE);
+ up->state = 2;
+ up->pollcnt++;
+ pp->polls++;
+ peer->nextdate = current_time + ANSWER;
+ return;
+ }
+ switch (peer->ttl) {
+
+ /*
+ * In manual mode the calling program is activated
+ * by the ntpdc program using the enable flag (fudge
+ * flag1), either manually or by a cron job.
+ */
+ case MODE_MANUAL:
+ up->run = 0;
+ break;
+
+ /*
+ * In automatic mode the calling program runs
+ * continuously at intervals determined by the sys_poll
+ * variable.
+ */
+ case MODE_AUTO:
+ if (!up->run)
+ pp->sloppyclockflag |= CLK_FLAG1;
+ up->run = 1;
+ break;
+
+ /*
+ * In backup mode the calling program is disabled,
+ * unless no system peer has been selected for MAXOUTAGE
+ * (3600 s). Once enabled, it runs until some other NTP
+ * peer shows up.
+ */
+ case MODE_BACKUP:
+ if (!up->run && sys_peer == 0) {
+ if (current_time - last_time > MAXOUTAGE) {
+ up->run = 1;
+ peer->hpoll = peer->minpoll;
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s USNO backup started ",
+ ntoa(&peer->srcadr));
+ }
+ } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) {
+ peer->hpoll = peer->minpoll;
+ up->run = 0;
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s USNO backup stopped",
+ ntoa(&peer->srcadr));
+ }
+ break;
+
+ default:
+ msyslog(LOG_ERR,
+ "clock %s USNO invalid mode", ntoa(&peer->srcadr));
+
+ }
+
+ /*
+ * The fudge flag1 is used as an enable/disable; if set either
+ * by the code or via ntpdc, the calling program is
+ * started; if reset, the phones stop ringing.
+ */
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ up->pollcnt = 0;
+ peer->nextdate = current_time + IDLE;
+ return;
+ }
+
+ /*
+ * Lock the port.
+ */
+ (void)sprintf(lockfile, LOCKFILE, up->unit);
+ fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd < 0) {
+ msyslog(LOG_ERR, "clock %s USNO port busy",
+ ntoa(&peer->srcadr));
+ return;
+ }
+ sprintf(pidbuf, "%d\n", (int) getpid());
+ write(fd, pidbuf, strlen(pidbuf));
+ close(fd);
+
+ /*
+ * Open serial port. Use ACTS line discipline, if available. It
+ * pumps a timestamp into the data stream at every on-time
+ * character '*' found. Note: the port must have modem control
+ * or deep pockets for the phone bill. HP-UX 9.03 users should
+ * have very deep pockets.
+ */
+ (void)sprintf(device, DEVICE, up->unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) {
+ unlink(lockfile);
+ return;
+ }
+ if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0)
+ msyslog(LOG_WARNING, "usno_timeout: clock %s: couldn't clear DTR: %m",
+ ntoa(&peer->srcadr));
+
+ pp->io.clock_recv = usno_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ unlink(lockfile);
+ free(up);
+ return;
+ }
+
+ /*
+ * Initialize modem and kill DTR. We skedaddle if this comes
+ * bum.
+ */
+ if (!usno_write(peer, MODEM_SETUP)) {
+ msyslog(LOG_ERR, "clock %s USNO couldn't write",
+ ntoa(&peer->srcadr));
+ io_closeclock(&pp->io);
+ unlink(lockfile);
+ free(up);
+ return;
+ }
+
+ /*
+ * Initiate a call to the Observatory. If we wind up here in
+ * other than state 0, a successful call could not be completed
+ * within minpoll seconds.
+ */
+ if (up->pollcnt) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_NOTICE,
+ "clock %s USNO calling program terminated",
+ ntoa(&peer->srcadr));
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ up->pollcnt = 0;
+#ifdef DEBUG
+ if (debug)
+ printf("usno: calling program terminated\n");
+#endif
+ usno_disc(peer);
+ return;
+ }
+
+ /*
+ * Raise DTR, and let the modem settle. Then we'll dial.
+ */
+ if (ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr) < -1)
+ msyslog(LOG_INFO, "usno_timeout: clock %s: couldn't set DTR: %m",
+ ntoa(&peer->srcadr));
+ up->state = 1;
+ peer->nextdate = current_time + WAIT;
+}
+#endif /* 0 */
+
+
+/*
+ * usno_disc - disconnect the call and wait for the ruckus to cool
+ */
+static void
+usno_disc(
+ struct peer *peer
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+ char lockfile[128];
+
+ /*
+ * We should never get here other than in state 0, unless a call
+ * has timed out. We drop DTR, which will reliably get the modem
+ * off the air, even while the modem is hammering away full tilt.
+ */
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+
+ if (ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr) < 0)
+ msyslog(LOG_INFO, "usno_disc: clock %s: couldn't clear DTR: %m",
+ ntoa(&peer->srcadr));
+
+ if (up->state > 0) {
+ up->state = 0;
+ msyslog(LOG_NOTICE, "clock %s USNO call failed %d",
+ ntoa(&peer->srcadr), up->state);
+#ifdef DEBUG
+ if (debug)
+ printf("usno: call failed %d\n", up->state);
+#endif
+ }
+
+ io_closeclock(&pp->io);
+ sprintf(lockfile, LOCKFILE, up->unit);
+ unlink(lockfile);
+
+ peer->nextdate = current_time + WAIT;
+}
+
+
+#if 0
+/*
+ * usno_write - write a message to the serial port
+ */
+static int
+usno_write(
+ struct peer *peer,
+ const char *str
+ )
+{
+ register struct usnounit *up;
+ struct refclockproc *pp;
+ int len;
+ int code;
+ char cr = '\r';
+
+ /*
+ * Not much to do here, other than send the message, handle
+ * debug and report faults.
+ */
+ pp = peer->procptr;
+ up = (struct usnounit *)pp->unitptr;
+ len = strlen(str);
+#ifdef DEBUG
+ if (debug)
+ printf("usno: state %d send %d %s\n", up->state, len,
+ str);
+#endif
+ code = write(pp->io.fd, str, (unsigned)len) == len;
+ code |= write(pp->io.fd, &cr, 1) == 1;
+ if (!code)
+ refclock_report(peer, CEVNT_FAULT);
+ return (code);
+}
+#endif /* 0 */
+
+#else
+int refclock_usno_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_wwv.c b/ntpd/refclock_wwv.c
new file mode 100644
index 0000000..11aae7f
--- /dev/null
+++ b/ntpd/refclock_wwv.c
@@ -0,0 +1,2859 @@
+/*
+ * refclock_wwv - clock driver for NIST WWV/H time/frequency station
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_WWV)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+#include "audio.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#define ICOM 1
+
+#ifdef ICOM
+#include "icom.h"
+#endif /* ICOM */
+
+/*
+ * Audio WWV/H demodulator/decoder
+ *
+ * This driver synchronizes the computer time using data encoded in
+ * radio transmissions from NIST time/frequency stations WWV in Boulder,
+ * CO, and WWVH in Kauai, HI. Transmissions are made continuously on
+ * 2.5, 5, 10, 15 and 20 MHz in AM mode. An ordinary shortwave receiver
+ * can be tuned manually to one of these frequencies or, in the case of
+ * ICOM receivers, the receiver can be tuned automatically using this
+ * program as propagation conditions change throughout the day and
+ * night.
+ *
+ * The driver receives, demodulates and decodes the radio signals when
+ * connected to the audio codec of a workstation running Solaris, SunOS
+ * FreeBSD or Linux, and with a little help, other workstations with
+ * similar codecs or sound cards. In this implementation, only one audio
+ * driver and codec can be supported on a single machine.
+ *
+ * The demodulation and decoding algorithms used in this driver are
+ * based on those developed for the TAPR DSP93 development board and the
+ * TI 320C25 digital signal processor described in: Mills, D.L. A
+ * precision radio clock for WWV transmissions. Electrical Engineering
+ * Report 97-8-1, University of Delaware, August 1997, 25 pp., available
+ * from www.eecis.udel.edu/~mills/reports.htm. The algorithms described
+ * in this report have been modified somewhat to improve performance
+ * under weak signal conditions and to provide an automatic station
+ * identification feature.
+ *
+ * The ICOM code is normally compiled in the driver. It isn't used,
+ * unless the mode keyword on the server configuration command specifies
+ * a nonzero ICOM ID select code. The C-IV trace is turned on if the
+ * debug level is greater than one.
+ */
+/*
+ * Interface definitions
+ */
+#define DEVICE_AUDIO "/dev/audio" /* audio device name */
+#define AUDIO_BUFSIZ 320 /* audio buffer size (50 ms) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define DESCRIPTION "WWV/H Audio Demodulator/Decoder" /* WRU */
+#define SECOND 8000 /* second epoch (sample rate) (Hz) */
+#define MINUTE (SECOND * 60) /* minute epoch */
+#define OFFSET 128 /* companded sample offset */
+#define SIZE 256 /* decompanding table size */
+#define MAXSIG 6000. /* max signal level reference */
+#define MAXCLP 100 /* max clips above reference per s */
+#define MAXSNR 30. /* max SNR reference */
+#define DGAIN 20. /* data channel gain reference */
+#define SGAIN 10. /* sync channel gain reference */
+#define MAXFREQ 1. /* max frequency tolerance (125 PPM) */
+#define PI 3.1415926535 /* the real thing */
+#define DATSIZ (170 * MS) /* data matched filter size */
+#define SYNSIZ (800 * MS) /* minute sync matched filter size */
+#define MAXERR 30 /* max data bit errors in minute */
+#define NCHAN 5 /* number of radio channels */
+#define AUDIO_PHI 5e-6 /* dispersion growth factor */
+#ifdef IRIG_SUCKS
+#define WIGGLE 11 /* wiggle filter length */
+#endif /* IRIG_SUCKS */
+
+/*
+ * General purpose status bits (status)
+ *
+ * SELV and/or SELH are set when WWV or WWVH has been heard and cleared
+ * on signal loss. SSYNC is set when the second sync pulse has been
+ * acquired and cleared by signal loss. MSYNC is set when the minute
+ * sync pulse has been acquired. DSYNC is set when a digit reaches the
+ * threshold and INSYNC is set when all nine digits have reached the
+ * threshold. The MSYNC, DSYNC and INSYNC bits are cleared only by
+ * timeout, upon which the driver starts over from scratch.
+ *
+ * DGATE is set if a data bit is invalid and BGATE is set if a BCD digit
+ * bit is invalid. SFLAG is set when during seconds 59, 0 and 1 while
+ * probing alternate frequencies. LEPDAY is set when SECWAR of the
+ * timecode is set on 30 June or 31 December. LEPSEC is set during the
+ * last minute of the day when LEPDAY is set. At the end of this minute
+ * the driver inserts second 60 in the seconds state machine and the
+ * minute sync slips a second. The SLOSS and SJITR bits are for monitor
+ * only.
+ */
+#define MSYNC 0x0001 /* minute epoch sync */
+#define SSYNC 0x0002 /* second epoch sync */
+#define DSYNC 0x0004 /* minute units sync */
+#define INSYNC 0x0008 /* clock synchronized */
+#define FGATE 0x0010 /* frequency gate */
+#define DGATE 0x0020 /* data bit error */
+#define BGATE 0x0040 /* BCD digit bit error */
+#define SFLAG 0x1000 /* probe flag */
+#define LEPDAY 0x2000 /* leap second day */
+#define LEPSEC 0x4000 /* leap second minute */
+
+/*
+ * Station scoreboard bits
+ *
+ * These are used to establish the signal quality for each of the five
+ * frequencies and two stations.
+ */
+#define SYNCNG 0x0001 /* sync or SNR below threshold */
+#define DATANG 0x0002 /* data or SNR below threshold */
+#define ERRRNG 0x0004 /* data error */
+#define SELV 0x0100 /* WWV station select */
+#define SELH 0x0200 /* WWVH station select */
+
+/*
+ * Alarm status bits (alarm)
+ *
+ * These bits indicate various alarm conditions, which are decoded to
+ * form the quality character included in the timecode. If not tracking
+ * second sync, the SYNERR alarm is raised. The data error counter is
+ * incremented for each invalid data bit. If too many data bit errors
+ * are encountered in one minute, the MODERR alarm is raised. The DECERR
+ * alarm is raised if a maximum likelihood digit fails to compare with
+ * the current clock digit. If the probability of any miscellaneous bit
+ * or any digit falls below the threshold, the SYMERR alarm is raised.
+ */
+#define DECERR 1 /* BCD digit compare error */
+#define SYMERR 2 /* low bit or digit probability */
+#define MODERR 4 /* too many data bit errors */
+#define SYNERR 8 /* not synchronized to station */
+
+/*
+ * Watchcat timeouts (watch)
+ *
+ * If these timeouts expire, the status bits are mashed to zero and the
+ * driver starts from scratch. Suitably more refined procedures may be
+ * developed in future. All these are in minutes.
+ */
+#define ACQSN 5 /* station acquisition timeout */
+#define DIGIT 30 /* minute unit digit timeout */
+#define HOLD 30 /* reachable timeout */
+#define PANIC (2 * 1440) /* panic timeout */
+
+/*
+ * Thresholds. These establish the minimum signal level, minimum SNR and
+ * maximum jitter thresholds which establish the error and false alarm
+ * rates of the driver. The values defined here may be on the
+ * adventurous side in the interest of the highest sensitivity.
+ */
+#define MTHR 13. /* acquisition signal gate (percent) */
+#define TTHR 50. /* tracking signal gate (percent) */
+#define ATHR 2000. /* acquisition amplitude threshold */
+#define ASNR 6. /* acquisition SNR threshold (dB) */
+#define AWND 20. /* acquisition jitter threshold (ms) */
+#define AMIN 3 /* min bit count */
+#define AMAX 6 /* max bit count */
+#define QTHR 2000 /* QSY sync threshold */
+#define QSNR 20. /* QSY sync SNR threshold (dB) */
+#define XTHR 1000. /* QSY data threshold */
+#define XSNR 10. /* QSY data SNR threshold (dB) */
+#define STHR 500 /* second sync amplitude threshold */
+#define SSNR 10. /* second sync SNR threshold */
+#define SCMP 10 /* second sync compare threshold */
+#define DTHR 1000 /* bit amplitude threshold */
+#define DSNR 10. /* bit SNR threshold (dB) */
+#define BTHR 1000 /* digit amplitude threshold */
+#define BSNR 3. /* digit likelihood threshold (dB) */
+#define BCMP 5 /* digit compare threshold */
+
+/*
+ * Tone frequency definitions. The increments are for 4.5-deg sine
+ * table.
+ */
+#define MS (SECOND / 1000) /* samples per millisecond */
+#define IN100 ((100 * 80) / SECOND) /* 100 Hz increment */
+#define IN1000 ((1000 * 80) / SECOND) /* 1000 Hz increment */
+#define IN1200 ((1200 * 80) / SECOND) /* 1200 Hz increment */
+
+/*
+ * Acquisition and tracking time constants. Usually powers of 2.
+ */
+#define MINAVG 8 /* min time constant */
+#define MAXAVG 1024 /* max time constant */
+#define TCONST 16 /* data bit/digit time constant */
+
+/*
+ * Miscellaneous status bits (misc)
+ *
+ * These bits correspond to designated bits in the WWV/H timecode. The
+ * bit probabilities are exponentially averaged over several minutes and
+ * processed by a integrator and threshold.
+ */
+#define DUT1 0x01 /* 56 DUT .1 */
+#define DUT2 0x02 /* 57 DUT .2 */
+#define DUT4 0x04 /* 58 DUT .4 */
+#define DUTS 0x08 /* 50 DUT sign */
+#define DST1 0x10 /* 55 DST1 leap warning */
+#define DST2 0x20 /* 2 DST2 DST1 delayed one day */
+#define SECWAR 0x40 /* 3 leap second warning */
+
+/*
+ * The on-time synchronization point for the driver is the second epoch
+ * sync pulse produced by the FIR matched filters. As the 5-ms delay of
+ * these filters is compensated, the program delay is 1.1 ms due to the
+ * 600-Hz IIR bandpass filter. The measured receiver delay is 4.7 ms and
+ * the codec delay less than 0.2 ms. The additional propagation delay
+ * specific to each receiver location can be programmed in the fudge
+ * time1 and time2 values for WWV and WWVH, respectively.
+ */
+#define PDELAY (.0011 + .0047 + .0002) /* net system delay (s) */
+
+/*
+ * Table of sine values at 4.5-degree increments. This is used by the
+ * synchronous matched filter demodulators. The integral of sine-squared
+ * over one complete cycle is PI, so the table is normallized by 1 / PI.
+ */
+double sintab[] = {
+ 0.000000e+00, 2.497431e-02, 4.979464e-02, 7.430797e-02, /* 0-3 */
+ 9.836316e-02, 1.218119e-01, 1.445097e-01, 1.663165e-01, /* 4-7 */
+ 1.870979e-01, 2.067257e-01, 2.250791e-01, 2.420447e-01, /* 8-11 */
+ 2.575181e-01, 2.714038e-01, 2.836162e-01, 2.940800e-01, /* 12-15 */
+ 3.027307e-01, 3.095150e-01, 3.143910e-01, 3.173286e-01, /* 16-19 */
+ 3.183099e-01, 3.173286e-01, 3.143910e-01, 3.095150e-01, /* 20-23 */
+ 3.027307e-01, 2.940800e-01, 2.836162e-01, 2.714038e-01, /* 24-27 */
+ 2.575181e-01, 2.420447e-01, 2.250791e-01, 2.067257e-01, /* 28-31 */
+ 1.870979e-01, 1.663165e-01, 1.445097e-01, 1.218119e-01, /* 32-35 */
+ 9.836316e-02, 7.430797e-02, 4.979464e-02, 2.497431e-02, /* 36-39 */
+-0.000000e+00, -2.497431e-02, -4.979464e-02, -7.430797e-02, /* 40-43 */
+-9.836316e-02, -1.218119e-01, -1.445097e-01, -1.663165e-01, /* 44-47 */
+-1.870979e-01, -2.067257e-01, -2.250791e-01, -2.420447e-01, /* 48-51 */
+-2.575181e-01, -2.714038e-01, -2.836162e-01, -2.940800e-01, /* 52-55 */
+-3.027307e-01, -3.095150e-01, -3.143910e-01, -3.173286e-01, /* 56-59 */
+-3.183099e-01, -3.173286e-01, -3.143910e-01, -3.095150e-01, /* 60-63 */
+-3.027307e-01, -2.940800e-01, -2.836162e-01, -2.714038e-01, /* 64-67 */
+-2.575181e-01, -2.420447e-01, -2.250791e-01, -2.067257e-01, /* 68-71 */
+-1.870979e-01, -1.663165e-01, -1.445097e-01, -1.218119e-01, /* 72-75 */
+-9.836316e-02, -7.430797e-02, -4.979464e-02, -2.497431e-02, /* 76-79 */
+ 0.000000e+00}; /* 80 */
+
+/*
+ * Decoder operations at the end of each second are driven by a state
+ * machine. The transition matrix consists of a dispatch table indexed
+ * by second number. Each entry in the table contains a case switch
+ * number and argument.
+ */
+struct progx {
+ int sw; /* case switch number */
+ int arg; /* argument */
+};
+
+/*
+ * Case switch numbers
+ */
+#define IDLE 0 /* no operation */
+#define COEF 1 /* BCD bit */
+#define COEF2 2 /* BCD bit ignored */
+#define DECIM9 3 /* BCD digit 0-9 */
+#define DECIM6 4 /* BCD digit 0-6 */
+#define DECIM3 5 /* BCD digit 0-3 */
+#define DECIM2 6 /* BCD digit 0-2 */
+#define MSCBIT 7 /* miscellaneous bit */
+#define MSC20 8 /* miscellaneous bit */
+#define MSC21 9 /* QSY probe channel */
+#define MIN1 10 /* minute */
+#define MIN2 11 /* leap second */
+#define SYNC2 12 /* QSY data channel */
+#define SYNC3 13 /* QSY data channel */
+
+/*
+ * Offsets in decoding matrix
+ */
+#define MN 0 /* minute digits (2) */
+#define HR 2 /* hour digits (2) */
+#define DA 4 /* day digits (3) */
+#define YR 7 /* year digits (2) */
+
+struct progx progx[] = {
+ {SYNC2, 0}, /* 0 latch sync max */
+ {SYNC3, 0}, /* 1 QSY data channel */
+ {MSCBIT, DST2}, /* 2 dst2 */
+ {MSCBIT, SECWAR}, /* 3 lw */
+ {COEF, 0}, /* 4 1 year units */
+ {COEF, 1}, /* 5 2 */
+ {COEF, 2}, /* 6 4 */
+ {COEF, 3}, /* 7 8 */
+ {DECIM9, YR}, /* 8 */
+ {IDLE, 0}, /* 9 p1 */
+ {COEF, 0}, /* 10 1 minute units */
+ {COEF, 1}, /* 11 2 */
+ {COEF, 2}, /* 12 4 */
+ {COEF, 3}, /* 13 8 */
+ {DECIM9, MN}, /* 14 */
+ {COEF, 0}, /* 15 10 minute tens */
+ {COEF, 1}, /* 16 20 */
+ {COEF, 2}, /* 17 40 */
+ {COEF2, 3}, /* 18 80 (not used) */
+ {DECIM6, MN + 1}, /* 19 p2 */
+ {COEF, 0}, /* 20 1 hour units */
+ {COEF, 1}, /* 21 2 */
+ {COEF, 2}, /* 22 4 */
+ {COEF, 3}, /* 23 8 */
+ {DECIM9, HR}, /* 24 */
+ {COEF, 0}, /* 25 10 hour tens */
+ {COEF, 1}, /* 26 20 */
+ {COEF2, 2}, /* 27 40 (not used) */
+ {COEF2, 3}, /* 28 80 (not used) */
+ {DECIM2, HR + 1}, /* 29 p3 */
+ {COEF, 0}, /* 30 1 day units */
+ {COEF, 1}, /* 31 2 */
+ {COEF, 2}, /* 32 4 */
+ {COEF, 3}, /* 33 8 */
+ {DECIM9, DA}, /* 34 */
+ {COEF, 0}, /* 35 10 day tens */
+ {COEF, 1}, /* 36 20 */
+ {COEF, 2}, /* 37 40 */
+ {COEF, 3}, /* 38 80 */
+ {DECIM9, DA + 1}, /* 39 p4 */
+ {COEF, 0}, /* 40 100 day hundreds */
+ {COEF, 1}, /* 41 200 */
+ {COEF2, 2}, /* 42 400 (not used) */
+ {COEF2, 3}, /* 43 800 (not used) */
+ {DECIM3, DA + 2}, /* 44 */
+ {IDLE, 0}, /* 45 */
+ {IDLE, 0}, /* 46 */
+ {IDLE, 0}, /* 47 */
+ {IDLE, 0}, /* 48 */
+ {IDLE, 0}, /* 49 p5 */
+ {MSCBIT, DUTS}, /* 50 dut+- */
+ {COEF, 0}, /* 51 10 year tens */
+ {COEF, 1}, /* 52 20 */
+ {COEF, 2}, /* 53 40 */
+ {COEF, 3}, /* 54 80 */
+ {MSC20, DST1}, /* 55 dst1 */
+ {MSCBIT, DUT1}, /* 56 0.1 dut */
+ {MSCBIT, DUT2}, /* 57 0.2 */
+ {MSC21, DUT4}, /* 58 0.4 QSY probe channel */
+ {MIN1, 0}, /* 59 p6 latch sync min */
+ {MIN2, 0} /* 60 leap second */
+};
+
+/*
+ * BCD coefficients for maximum likelihood digit decode
+ */
+#define P15 1. /* max positive number */
+#define N15 -1. /* max negative number */
+
+/*
+ * Digits 0-9
+ */
+#define P9 (P15 / 4) /* mark (+1) */
+#define N9 (N15 / 4) /* space (-1) */
+
+double bcd9[][4] = {
+ {N9, N9, N9, N9}, /* 0 */
+ {P9, N9, N9, N9}, /* 1 */
+ {N9, P9, N9, N9}, /* 2 */
+ {P9, P9, N9, N9}, /* 3 */
+ {N9, N9, P9, N9}, /* 4 */
+ {P9, N9, P9, N9}, /* 5 */
+ {N9, P9, P9, N9}, /* 6 */
+ {P9, P9, P9, N9}, /* 7 */
+ {N9, N9, N9, P9}, /* 8 */
+ {P9, N9, N9, P9}, /* 9 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * Digits 0-6 (minute tens)
+ */
+#define P6 (P15 / 3) /* mark (+1) */
+#define N6 (N15 / 3) /* space (-1) */
+
+double bcd6[][4] = {
+ {N6, N6, N6, 0}, /* 0 */
+ {P6, N6, N6, 0}, /* 1 */
+ {N6, P6, N6, 0}, /* 2 */
+ {P6, P6, N6, 0}, /* 3 */
+ {N6, N6, P6, 0}, /* 4 */
+ {P6, N6, P6, 0}, /* 5 */
+ {N6, P6, P6, 0}, /* 6 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * Digits 0-3 (day hundreds)
+ */
+#define P3 (P15 / 2) /* mark (+1) */
+#define N3 (N15 / 2) /* space (-1) */
+
+double bcd3[][4] = {
+ {N3, N3, 0, 0}, /* 0 */
+ {P3, N3, 0, 0}, /* 1 */
+ {N3, P3, 0, 0}, /* 2 */
+ {P3, P3, 0, 0}, /* 3 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * Digits 0-2 (hour tens)
+ */
+#define P2 (P15 / 2) /* mark (+1) */
+#define N2 (N15 / 2) /* space (-1) */
+
+double bcd2[][4] = {
+ {N2, N2, 0, 0}, /* 0 */
+ {P2, N2, 0, 0}, /* 1 */
+ {N2, P2, 0, 0}, /* 2 */
+ {0, 0, 0, 0} /* backstop */
+};
+
+/*
+ * DST decode (DST2 DST1) for prettyprint
+ */
+char dstcod[] = {
+ 'S', /* 00 standard time */
+ 'I', /* 01 set clock ahead at 0200 local */
+ 'O', /* 10 set clock back at 0200 local */
+ 'D' /* 11 daylight time */
+};
+
+/*
+ * The decoding matrix consists of nine row vectors, one for each digit
+ * of the timecode. The digits are stored from least to most significant
+ * order. The maximum likelihood timecode is formed from the digits
+ * corresponding to the maximum likelihood values reading in the
+ * opposite order: yy ddd hh:mm.
+ */
+struct decvec {
+ int radix; /* radix (3, 4, 6, 10) */
+ int digit; /* current clock digit */
+ int mldigit; /* maximum likelihood digit */
+ int phase; /* maximum likelihood digit phase */
+ int count; /* match count */
+ double digprb; /* max digit probability */
+ double digsnr; /* likelihood function (dB) */
+ double like[10]; /* likelihood integrator 0-9 */
+};
+
+/*
+ * The station structure is used to acquire the minute pulse from WWV
+ * and/or WWVH. These stations are distinguished by the frequency used
+ * for the second and minute sync pulses, 1000 Hz for WWV and 1200 Hz
+ * for WWVH. Other than frequency, the format is the same.
+ */
+struct sync {
+ double epoch; /* accumulated epoch differences */
+ double maxamp; /* sync max envelope (square) */
+ double noiamp; /* sync noise envelope (square) */
+ long pos; /* max amplitude position */
+ long lastpos; /* last max position */
+ long mepoch; /* minute synch epoch */
+
+ double amp; /* sync amplitude (I, Q squares) */
+ double synamp; /* sync max envelope at 800 ms */
+ double synmax; /* sync envelope at 0 s */
+ double synmin; /* sync envelope at 59, 1 s */
+ double synsnr; /* sync signal SNR */
+ int count; /* bit counter */
+ char refid[5]; /* reference identifier */
+ int select; /* select bits */
+ int reach; /* reachability register */
+};
+
+/*
+ * The channel structure is used to mitigate between channels.
+ */
+struct chan {
+ int gain; /* audio gain */
+ double sigamp; /* data max envelope (square) */
+ double noiamp; /* data noise envelope (square) */
+ double datsnr; /* data signal SNR */
+ struct sync wwv; /* wwv station */
+ struct sync wwvh; /* wwvh station */
+};
+
+/*
+ * WWV unit control structure
+ */
+struct wwvunit {
+ l_fp timestamp; /* audio sample timestamp */
+ l_fp tick; /* audio sample increment */
+ double phase, freq; /* logical clock phase and frequency */
+ double monitor; /* audio monitor point */
+ int fd_icom; /* ICOM file descriptor */
+ int errflg; /* error flags */
+ int watch; /* watchcat */
+
+ /*
+ * Audio codec variables
+ */
+ double comp[SIZE]; /* decompanding table */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int mongain; /* codec monitor gain */
+ int clipcnt; /* sample clipped count */
+#ifdef IRIG_SUCKS
+ l_fp wigwag; /* wiggle accumulator */
+ int wp; /* wiggle filter pointer */
+ l_fp wiggle[WIGGLE]; /* wiggle filter */
+ l_fp wigbot[WIGGLE]; /* wiggle bottom fisher*/
+#endif /* IRIG_SUCKS */
+
+ /*
+ * Variables used to establish basic system timing
+ */
+ int avgint; /* master time constant */
+ int tepoch; /* sync epoch median */
+ int yepoch; /* sync epoch */
+ int repoch; /* buffered sync epoch */
+ double epomax; /* second sync amplitude */
+ double eposnr; /* second sync SNR */
+ double irig; /* data I channel amplitude */
+ double qrig; /* data Q channel amplitude */
+ int datapt; /* 100 Hz ramp */
+ double datpha; /* 100 Hz VFO control */
+ int rphase; /* second sample counter */
+ long mphase; /* minute sample counter */
+
+ /*
+ * Variables used to mitigate which channel to use
+ */
+ struct chan mitig[NCHAN]; /* channel data */
+ struct sync *sptr; /* station pointer */
+ int dchan; /* data channel */
+ int schan; /* probe channel */
+ int achan; /* active channel */
+
+ /*
+ * Variables used by the clock state machine
+ */
+ struct decvec decvec[9]; /* decoding matrix */
+ int rsec; /* seconds counter */
+ int digcnt; /* count of digits synchronized */
+
+ /*
+ * Variables used to estimate signal levels and bit/digit
+ * probabilities
+ */
+ double sigsig; /* data max signal */
+ double sigamp; /* data max envelope (square) */
+ double noiamp; /* data noise envelope (square) */
+ double datsnr; /* data SNR (dB) */
+
+ /*
+ * Variables used to establish status and alarm conditions
+ */
+ int status; /* status bits */
+ int alarm; /* alarm flashers */
+ int misc; /* miscellaneous timecode bits */
+ int errcnt; /* data bit error counter */
+ int errbit; /* data bit errors in minute */
+};
+
+/*
+ * Function prototypes
+ */
+static int wwv_start P((int, struct peer *));
+static void wwv_shutdown P((int, struct peer *));
+static void wwv_receive P((struct recvbuf *));
+static void wwv_poll P((int, struct peer *));
+
+/*
+ * More function prototypes
+ */
+static void wwv_epoch P((struct peer *));
+static void wwv_rf P((struct peer *, double));
+static void wwv_endpoc P((struct peer *, int));
+static void wwv_rsec P((struct peer *, double));
+static void wwv_qrz P((struct peer *, struct sync *,
+ double, int));
+static void wwv_corr4 P((struct peer *, struct decvec *,
+ double [], double [][4]));
+static void wwv_gain P((struct peer *));
+static void wwv_tsec P((struct wwvunit *));
+static double wwv_data P((struct wwvunit *, double));
+static int timecode P((struct wwvunit *, char *));
+static double wwv_snr P((double, double));
+static int carry P((struct decvec *));
+static void wwv_newchan P((struct peer *));
+static void wwv_newgame P((struct peer *));
+static double wwv_metric P((struct sync *));
+#ifdef ICOM
+static int wwv_qsy P((struct peer *, int));
+#endif /* ICOM */
+
+static double qsy[NCHAN] = {2.5, 5, 10, 15, 20}; /* frequencies (MHz) */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_wwv = {
+ wwv_start, /* start up driver */
+ wwv_shutdown, /* shut down driver */
+ wwv_poll, /* transmit poll message */
+ noentry, /* not used (old wwv_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old wwv_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * wwv_start - open the devices and initialize data for processing
+ */
+static int
+wwv_start(
+ int unit, /* instance number (used by PCM) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+#ifdef ICOM
+ int temp;
+#endif /* ICOM */
+
+ /*
+ * Local variables
+ */
+ int fd; /* file descriptor */
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device
+ */
+ fd = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit);
+ if (fd < 0)
+ return (0);
+#ifdef DEBUG
+ if (debug)
+ audio_show();
+#endif
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct wwvunit *)emalloc(sizeof(struct wwvunit)))) {
+ close(fd);
+ return (0);
+ }
+ memset(up, 0, sizeof(struct wwvunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = wwv_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+
+ /*
+ * The companded samples are encoded sign-magnitude. The table
+ * contains all the 256 values in the interest of speed.
+ */
+ up->comp[0] = up->comp[OFFSET] = 0.;
+ up->comp[1] = 1; up->comp[OFFSET + 1] = -1.;
+ up->comp[2] = 3; up->comp[OFFSET + 2] = -3.;
+ step = 2.;
+ for (i = 3; i < OFFSET; i++) {
+ up->comp[i] = up->comp[i - 1] + step;
+ up->comp[OFFSET + i] = -up->comp[i];
+ if (i % 16 == 0)
+ step *= 2.;
+ }
+ DTOLFP(1. / SECOND, &up->tick);
+
+ /*
+ * Initialize the decoding matrix with the radix for each digit
+ * position.
+ */
+ up->decvec[MN].radix = 10; /* minutes */
+ up->decvec[MN + 1].radix = 6;
+ up->decvec[HR].radix = 10; /* hours */
+ up->decvec[HR + 1].radix = 3;
+ up->decvec[DA].radix = 10; /* days */
+ up->decvec[DA + 1].radix = 10;
+ up->decvec[DA + 2].radix = 4;
+ up->decvec[YR].radix = 10; /* years */
+ up->decvec[YR + 1].radix = 10;
+ wwv_newgame(peer);
+ up->schan = up->achan = 3;
+
+ /*
+ * Initialize autotune if available. Start out at 15 MHz. Note
+ * that the ICOM select code must be less than 128, so the high
+ * order bit can be used to select the line speed.
+ */
+#ifdef ICOM
+ temp = 0;
+#ifdef DEBUG
+ if (debug > 1)
+ temp = P_TRACE;
+#endif
+ if (peer->ttl != 0) {
+ if (peer->ttl & 0x80)
+ up->fd_icom = icom_init("/dev/icom", B1200,
+ temp);
+ else
+ up->fd_icom = icom_init("/dev/icom", B9600,
+ temp);
+ }
+ if (up->fd_icom > 0) {
+ if ((temp = wwv_qsy(peer, up->schan)) != 0) {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "icom: radio not found");
+ up->errflg = CEVNT_FAULT;
+ close(up->fd_icom);
+ up->fd_icom = 0;
+ } else {
+ NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE,
+ "icom: autotune enabled");
+ }
+ }
+#endif /* ICOM */
+ return (1);
+}
+
+
+/*
+ * wwv_shutdown - shut down the clock
+ */
+static void
+wwv_shutdown(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ if (up->fd_icom > 0)
+ close(up->fd_icom);
+ free(up);
+}
+
+
+/*
+ * wwv_receive - receive data from the audio device
+ *
+ * This routine reads input samples and adjusts the logical clock to
+ * track the A/D sample clock by dropping or duplicating codec samples.
+ * It also controls the A/D signal level with an AGC loop to mimimize
+ * quantization noise and avoid overload.
+ */
+static void
+wwv_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ /*
+ * Local variables
+ */
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ int bufcnt; /* buffer counter */
+ l_fp ltemp;
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Main loop - read until there ain't no more. Note codec
+ * samples are bit-inverted.
+ */
+ DTOLFP((double)rbufp->recv_length / SECOND, &ltemp);
+ L_SUB(&rbufp->recv_time, &ltemp);
+ up->timestamp = rbufp->recv_time;
+ dpt = rbufp->recv_buffer;
+ for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) {
+ sample = up->comp[~*dpt++ & 0xff];
+
+ /*
+ * Clip noise spikes greater than MAXSIG. If no clips,
+ * increase the gain a tad; if the clips are too high,
+ * decrease a tad.
+ */
+ if (sample > MAXSIG) {
+ sample = MAXSIG;
+ up->clipcnt++;
+ } else if (sample < -MAXSIG) {
+ sample = -MAXSIG;
+ up->clipcnt++;
+ }
+
+ /*
+ * Variable frequency oscillator. The codec oscillator
+ * runs at the nominal rate of 8000 samples per second,
+ * or 125 us per sample. A frequency change of one unit
+ * results in either duplicating or deleting one sample
+ * per second, which results in a frequency change of
+ * 125 PPM.
+ */
+ up->phase += up->freq / SECOND;
+ if (up->phase >= .5) {
+ up->phase -= 1.;
+ } else if (up->phase < -.5) {
+ up->phase += 1.;
+ wwv_rf(peer, sample);
+ wwv_rf(peer, sample);
+ } else {
+ wwv_rf(peer, sample);
+ }
+ L_ADD(&up->timestamp, &up->tick);
+ }
+
+ /*
+ * Set the input port and monitor gain for the next buffer.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = 2;
+ else
+ up->port = 1;
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ up->mongain = MONGAIN;
+ else
+ up->mongain = 0;
+}
+
+
+/*
+ * wwv_poll - called by the transmit procedure
+ *
+ * This routine keeps track of status. If no offset samples have been
+ * processed during a poll interval, a timeout event is declared. If
+ * errors have have occurred during the interval, they are reported as
+ * well. Once the clock is set, it always appears reachable, unless
+ * reset by watchdog timeout.
+ */
+static void
+wwv_poll(
+ int unit, /* instance number (not used) */
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (pp->coderecv == pp->codeproc)
+ up->errflg = CEVNT_TIMEOUT;
+ if (up->errflg)
+ refclock_report(peer, up->errflg);
+ up->errflg = 0;
+ pp->polls++;
+}
+
+
+/*
+ * wwv_rf - process signals and demodulate to baseband
+ *
+ * This routine grooms and filters decompanded raw audio samples. The
+ * output signals include the 100-Hz baseband data signal in quadrature
+ * form, plus the epoch index of the second sync signal and the second
+ * index of the minute sync signal.
+ *
+ * There are two 1-s ramps used by this program. Both count the 8000
+ * logical clock samples spanning exactly one second. The epoch ramp
+ * counts the samples starting at an arbitrary time. The rphase ramp
+ * counts the samples starting at the 5-ms second sync pulse found
+ * during the epoch ramp.
+ *
+ * There are two 1-m ramps used by this program. The mphase ramp counts
+ * the 480,000 logical clock samples spanning exactly one minute and
+ * starting at an arbitrary time. The rsec ramp counts the 60 seconds of
+ * the minute starting at the 800-ms minute sync pulse found during the
+ * mphase ramp. The rsec ramp drives the seconds state machine to
+ * determine the bits and digits of the timecode.
+ *
+ * Demodulation operations are based on three synthesized quadrature
+ * sinusoids: 100 Hz for the data signal, 1000 Hz for the WWV sync
+ * signal and 1200 Hz for the WWVH sync signal. These drive synchronous
+ * matched filters for the data signal (170 ms at 100 Hz), WWV minute
+ * sync signal (800 ms at 1000 Hz) and WWVH minute sync signal (800 ms
+ * at 1200 Hz). Two additional matched filters are switched in
+ * as required for the WWV second sync signal (5 ms at 1000 Hz) and
+ * WWVH second sync signal (5 ms at 1200 Hz).
+ */
+static void
+wwv_rf(
+ struct peer *peer, /* peerstructure pointer */
+ double isig /* input signal */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct sync *sp;
+
+ static double lpf[5]; /* 150-Hz lpf delay line */
+ double data; /* lpf output */
+ static double bpf[9]; /* 1000/1200-Hz bpf delay line */
+ double syncx; /* bpf output */
+ static double mf[41]; /* 1000/1200-Hz mf delay line */
+ double mfsync; /* mf output */
+
+ static int iptr; /* data channel pointer */
+ static double ibuf[DATSIZ]; /* data I channel delay line */
+ static double qbuf[DATSIZ]; /* data Q channel delay line */
+
+ static int jptr; /* sync channel pointer */
+ static double cibuf[SYNSIZ]; /* wwv I channel delay line */
+ static double cqbuf[SYNSIZ]; /* wwv Q channel delay line */
+ static double ciamp; /* wwv I channel amplitude */
+ static double cqamp; /* wwv Q channel amplitude */
+ static int csinptr; /* wwv channel phase */
+ static double hibuf[SYNSIZ]; /* wwvh I channel delay line */
+ static double hqbuf[SYNSIZ]; /* wwvh Q channel delay line */
+ static double hiamp; /* wwvh I channel amplitude */
+ static double hqamp; /* wwvh Q channel amplitude */
+ static int hsinptr; /* wwvh channels phase */
+
+ static double epobuf[SECOND]; /* epoch sync comb filter */
+ static double epomax; /* epoch sync amplitude buffer */
+ static int epopos; /* epoch sync position buffer */
+
+ static int iniflg; /* initialization flag */
+ int epoch; /* comb filter index */
+ int pdelay; /* propagation delay (samples) */
+ double dtemp;
+ int i;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ if (!iniflg) {
+ iniflg = 1;
+ memset((char *)lpf, 0, sizeof(lpf));
+ memset((char *)bpf, 0, sizeof(bpf));
+ memset((char *)mf, 0, sizeof(mf));
+ memset((char *)ibuf, 0, sizeof(ibuf));
+ memset((char *)qbuf, 0, sizeof(qbuf));
+ memset((char *)cibuf, 0, sizeof(cibuf));
+ memset((char *)cqbuf, 0, sizeof(cqbuf));
+ memset((char *)hibuf, 0, sizeof(hibuf));
+ memset((char *)hqbuf, 0, sizeof(hqbuf));
+ memset((char *)epobuf, 0, sizeof(epobuf));
+ }
+
+ /*
+ * Baseband data demodulation. The 100-Hz subcarrier is
+ * extracted using a 150-Hz IIR lowpass filter. This attenuates
+ * the 1000/1200-Hz sync signals, as well as the 440-Hz and
+ * 600-Hz tones and most of the noise and voice modulation
+ * components.
+ *
+ * Matlab IIR 4th-order IIR elliptic, 150 Hz lowpass, 0.2 dB
+ * passband ripple, -50 dB stopband ripple.
+ */
+ data = (lpf[4] = lpf[3]) * 8.360961e-01;
+ data += (lpf[3] = lpf[2]) * -3.481740e+00;
+ data += (lpf[2] = lpf[1]) * 5.452988e+00;
+ data += (lpf[1] = lpf[0]) * -3.807229e+00;
+ lpf[0] = isig - data;
+ data = lpf[0] * 3.281435e-03
+ + lpf[1] * -1.149947e-02
+ + lpf[2] * 1.654858e-02
+ + lpf[3] * -1.149947e-02
+ + lpf[4] * 3.281435e-03;
+
+ /*
+ * The I and Q quadrature data signals are produced by
+ * multiplying the filtered signal by 100-Hz sine and cosine
+ * signals, respectively. The data signals are demodulated by
+ * 170-ms synchronous matched filters to produce the amplitude
+ * and phase signals used by the decoder.
+ */
+ i = up->datapt;
+ up->datapt = (up->datapt + IN100) % 80;
+ dtemp = sintab[i] * data / DATSIZ * DGAIN;
+ up->irig -= ibuf[iptr];
+ ibuf[iptr] = dtemp;
+ up->irig += dtemp;
+ i = (i + 20) % 80;
+ dtemp = sintab[i] * data / DATSIZ * DGAIN;
+ up->qrig -= qbuf[iptr];
+ qbuf[iptr] = dtemp;
+ up->qrig += dtemp;
+ iptr = (iptr + 1) % DATSIZ;
+
+ /*
+ * Baseband sync demodulation. The 1000/1200 sync signals are
+ * extracted using a 600-Hz IIR bandpass filter. This removes
+ * the 100-Hz data subcarrier, as well as the 440-Hz and 600-Hz
+ * tones and most of the noise and voice modulation components.
+ *
+ * Matlab 4th-order IIR elliptic, 800-1400 Hz bandpass, 0.2 dB
+ * passband ripple, -50 dB stopband ripple.
+ */
+ syncx = (bpf[8] = bpf[7]) * 4.897278e-01;
+ syncx += (bpf[7] = bpf[6]) * -2.765914e+00;
+ syncx += (bpf[6] = bpf[5]) * 8.110921e+00;
+ syncx += (bpf[5] = bpf[4]) * -1.517732e+01;
+ syncx += (bpf[4] = bpf[3]) * 1.975197e+01;
+ syncx += (bpf[3] = bpf[2]) * -1.814365e+01;
+ syncx += (bpf[2] = bpf[1]) * 1.159783e+01;
+ syncx += (bpf[1] = bpf[0]) * -4.735040e+00;
+ bpf[0] = isig - syncx;
+ syncx = bpf[0] * 8.203628e-03
+ + bpf[1] * -2.375732e-02
+ + bpf[2] * 3.353214e-02
+ + bpf[3] * -4.080258e-02
+ + bpf[4] * 4.605479e-02
+ + bpf[5] * -4.080258e-02
+ + bpf[6] * 3.353214e-02
+ + bpf[7] * -2.375732e-02
+ + bpf[8] * 8.203628e-03;
+
+ /*
+ * The I and Q quadrature minute sync signals are produced by
+ * multiplying the filtered signal by 1000-Hz (WWV) and 1200-Hz
+ * (WWVH) sine and cosine signals, respectively. The resulting
+ * signals are demodulated by 800-ms synchronous matched filters
+ * to synchronize the second and minute and to detect which one
+ * (or both) the WWV or WWVH signal is present.
+ *
+ * Note the master timing ramps, which run continuously. The
+ * minute counter (mphase) counts the samples in the minute,
+ * while the second counter (epoch) counts the samples in the
+ * second.
+ */
+ up->mphase = (up->mphase + 1) % MINUTE;
+ epoch = up->mphase % SECOND;
+ i = csinptr;
+ csinptr = (csinptr + IN1000) % 80;
+ dtemp = sintab[i] * syncx / SYNSIZ * SGAIN;
+ ciamp = ciamp - cibuf[jptr] + dtemp;
+ cibuf[jptr] = dtemp;
+ i = (i + 20) % 80;
+ dtemp = sintab[i] * syncx / SYNSIZ * SGAIN;
+ cqamp = cqamp - cqbuf[jptr] + dtemp;
+ cqbuf[jptr] = dtemp;
+ sp = &up->mitig[up->schan].wwv;
+ dtemp = ciamp * ciamp + cqamp * cqamp;
+ sp->amp = dtemp;
+ if (!(up->status & MSYNC))
+ wwv_qrz(peer, sp, dtemp, (int)(pp->fudgetime1 *
+ SECOND));
+ i = hsinptr;
+ hsinptr = (hsinptr + IN1200) % 80;
+ dtemp = sintab[i] * syncx / SYNSIZ * SGAIN;
+ hiamp = hiamp - hibuf[jptr] + dtemp;
+ hibuf[jptr] = dtemp;
+ i = (i + 20) % 80;
+ dtemp = sintab[i] * syncx / SYNSIZ * SGAIN;
+ hqamp = hqamp - hqbuf[jptr] + dtemp;
+ hqbuf[jptr] = dtemp;
+ sp = &up->mitig[up->schan].wwvh;
+ dtemp = hiamp * hiamp + hqamp * hqamp;
+ sp->amp = dtemp;
+ if (!(up->status & MSYNC))
+ wwv_qrz(peer, sp, dtemp, (int)(pp->fudgetime2 *
+ SECOND));
+ jptr = (jptr + 1) % SYNSIZ;
+
+ /*
+ * The following section is called once per minute. It does
+ * housekeeping and timeout functions and empties the dustbins.
+ */
+ if (up->mphase == 0) {
+ up->watch++;
+ if (!(up->status & MSYNC)) {
+
+ /*
+ * If minute sync has not been acquired before
+ * timeout, or if no signal is heard, the
+ * program cycles to the next frequency and
+ * tries again.
+ */
+ wwv_newchan(peer);
+ if (!(up->status & (SELV | SELH)) || up->watch >
+ ACQSN) {
+ wwv_newgame(peer);
+#ifdef ICOM
+ if (up->fd_icom > 0) {
+ up->schan = (up->schan + 1) %
+ NCHAN;
+ wwv_qsy(peer, up->schan);
+ }
+#endif /* ICOM */
+ }
+ } else {
+
+ /*
+ * If the leap bit is set, set the minute epoch
+ * back one second so the station processes
+ * don't miss a beat.
+ */
+ if (up->status & LEPSEC) {
+ up->mphase -= SECOND;
+ if (up->mphase < 0)
+ up->mphase += MINUTE;
+ }
+ }
+ }
+
+ /*
+ * When the channel metric reaches threshold and the second
+ * counter matches the minute epoch within the second, the
+ * driver has synchronized to the station. The second number is
+ * the remaining seconds until the next minute epoch, while the
+ * sync epoch is zero. Watch out for the first second; if
+ * already synchronized to the second, the buffered sync epoch
+ * must be set.
+ */
+ if (up->status & MSYNC) {
+ wwv_epoch(peer);
+ } else if ((sp = up->sptr) != NULL) {
+ struct chan *cp;
+
+ if (sp->count >= AMIN && epoch == sp->mepoch % SECOND) {
+ up->rsec = 60 - sp->mepoch / SECOND;
+ up->rphase = 0;
+ up->status |= MSYNC;
+ up->watch = 0;
+ if (!(up->status & SSYNC))
+ up->repoch = up->yepoch = epoch;
+ else
+ up->repoch = up->yepoch;
+ for (i = 0; i < NCHAN; i++) {
+ cp = &up->mitig[i];
+ cp->wwv.count = cp->wwv.reach = 0;
+ cp->wwvh.count = cp->wwvh.reach = 0;
+ }
+ }
+ }
+
+ /*
+ * The second sync pulse is extracted using 5-ms (40 sample) FIR
+ * matched filters at 1000 Hz for WWV or 1200 Hz for WWVH. This
+ * pulse is used for the most precise synchronization, since if
+ * provides a resolution of one sample (125 us). The filters run
+ * only if the station has been reliably determined.
+ */
+ if (up->status & SELV) {
+ pdelay = (int)(pp->fudgetime1 * SECOND);
+
+ /*
+ * WWV FIR matched filter, five cycles of 1000-Hz
+ * sinewave.
+ */
+ mf[40] = mf[39];
+ mfsync = (mf[39] = mf[38]) * 4.224514e-02;
+ mfsync += (mf[38] = mf[37]) * 5.974365e-02;
+ mfsync += (mf[37] = mf[36]) * 4.224514e-02;
+ mf[36] = mf[35];
+ mfsync += (mf[35] = mf[34]) * -4.224514e-02;
+ mfsync += (mf[34] = mf[33]) * -5.974365e-02;
+ mfsync += (mf[33] = mf[32]) * -4.224514e-02;
+ mf[32] = mf[31];
+ mfsync += (mf[31] = mf[30]) * 4.224514e-02;
+ mfsync += (mf[30] = mf[29]) * 5.974365e-02;
+ mfsync += (mf[29] = mf[28]) * 4.224514e-02;
+ mf[28] = mf[27];
+ mfsync += (mf[27] = mf[26]) * -4.224514e-02;
+ mfsync += (mf[26] = mf[25]) * -5.974365e-02;
+ mfsync += (mf[25] = mf[24]) * -4.224514e-02;
+ mf[24] = mf[23];
+ mfsync += (mf[23] = mf[22]) * 4.224514e-02;
+ mfsync += (mf[22] = mf[21]) * 5.974365e-02;
+ mfsync += (mf[21] = mf[20]) * 4.224514e-02;
+ mf[20] = mf[19];
+ mfsync += (mf[19] = mf[18]) * -4.224514e-02;
+ mfsync += (mf[18] = mf[17]) * -5.974365e-02;
+ mfsync += (mf[17] = mf[16]) * -4.224514e-02;
+ mf[16] = mf[15];
+ mfsync += (mf[15] = mf[14]) * 4.224514e-02;
+ mfsync += (mf[14] = mf[13]) * 5.974365e-02;
+ mfsync += (mf[13] = mf[12]) * 4.224514e-02;
+ mf[12] = mf[11];
+ mfsync += (mf[11] = mf[10]) * -4.224514e-02;
+ mfsync += (mf[10] = mf[9]) * -5.974365e-02;
+ mfsync += (mf[9] = mf[8]) * -4.224514e-02;
+ mf[8] = mf[7];
+ mfsync += (mf[7] = mf[6]) * 4.224514e-02;
+ mfsync += (mf[6] = mf[5]) * 5.974365e-02;
+ mfsync += (mf[5] = mf[4]) * 4.224514e-02;
+ mf[4] = mf[3];
+ mfsync += (mf[3] = mf[2]) * -4.224514e-02;
+ mfsync += (mf[2] = mf[1]) * -5.974365e-02;
+ mfsync += (mf[1] = mf[0]) * -4.224514e-02;
+ mf[0] = syncx;
+ } else if (up->status & SELH) {
+ pdelay = (int)(pp->fudgetime2 * SECOND);
+
+ /*
+ * WWVH FIR matched filter, six cycles of 1200-Hz
+ * sinewave.
+ */
+ mf[40] = mf[39];
+ mfsync = (mf[39] = mf[38]) * 4.833363e-02;
+ mfsync += (mf[38] = mf[37]) * 5.681959e-02;
+ mfsync += (mf[37] = mf[36]) * 1.846180e-02;
+ mfsync += (mf[36] = mf[35]) * -3.511644e-02;
+ mfsync += (mf[35] = mf[34]) * -5.974365e-02;
+ mfsync += (mf[34] = mf[33]) * -3.511644e-02;
+ mfsync += (mf[33] = mf[32]) * 1.846180e-02;
+ mfsync += (mf[32] = mf[31]) * 5.681959e-02;
+ mfsync += (mf[31] = mf[30]) * 4.833363e-02;
+ mf[30] = mf[29];
+ mfsync += (mf[29] = mf[28]) * -4.833363e-02;
+ mfsync += (mf[28] = mf[27]) * -5.681959e-02;
+ mfsync += (mf[27] = mf[26]) * -1.846180e-02;
+ mfsync += (mf[26] = mf[25]) * 3.511644e-02;
+ mfsync += (mf[25] = mf[24]) * 5.974365e-02;
+ mfsync += (mf[24] = mf[23]) * 3.511644e-02;
+ mfsync += (mf[23] = mf[22]) * -1.846180e-02;
+ mfsync += (mf[22] = mf[21]) * -5.681959e-02;
+ mfsync += (mf[21] = mf[20]) * -4.833363e-02;
+ mf[20] = mf[19];
+ mfsync += (mf[19] = mf[18]) * 4.833363e-02;
+ mfsync += (mf[18] = mf[17]) * 5.681959e-02;
+ mfsync += (mf[17] = mf[16]) * 1.846180e-02;
+ mfsync += (mf[16] = mf[15]) * -3.511644e-02;
+ mfsync += (mf[15] = mf[14]) * -5.974365e-02;
+ mfsync += (mf[14] = mf[13]) * -3.511644e-02;
+ mfsync += (mf[13] = mf[12]) * 1.846180e-02;
+ mfsync += (mf[12] = mf[11]) * 5.681959e-02;
+ mfsync += (mf[11] = mf[10]) * 4.833363e-02;
+ mf[10] = mf[9];
+ mfsync += (mf[9] = mf[8]) * -4.833363e-02;
+ mfsync += (mf[8] = mf[7]) * -5.681959e-02;
+ mfsync += (mf[7] = mf[6]) * -1.846180e-02;
+ mfsync += (mf[6] = mf[5]) * 3.511644e-02;
+ mfsync += (mf[5] = mf[4]) * 5.974365e-02;
+ mfsync += (mf[4] = mf[3]) * 3.511644e-02;
+ mfsync += (mf[3] = mf[2]) * -1.846180e-02;
+ mfsync += (mf[2] = mf[1]) * -5.681959e-02;
+ mfsync += (mf[1] = mf[0]) * -4.833363e-02;
+ mf[0] = syncx;
+ } else {
+ mfsync = 0;
+ pdelay = 0;
+ }
+
+ /*
+ * Enhance the seconds sync pulse using a 1-s (8000-sample) comb
+ * filter. Correct for the FIR matched filter delay, which is 5
+ * ms for both the WWV and WWVH filters, and also for the
+ * propagation delay. Once each second look for second sync. If
+ * not in minute sync, fiddle the codec gain. Note the SNR is
+ * computed from the maximum sample and the two samples 6 ms
+ * before and 6 ms after it, so if we slip more than a cycle the
+ * SNR should plummet.
+ */
+ dtemp = (epobuf[epoch] += (mfsync - epobuf[epoch]) /
+ up->avgint);
+ if (dtemp > epomax) {
+ epomax = dtemp;
+ epopos = epoch;
+ }
+ if (epoch == 0) {
+ int k, j;
+
+ up->epomax = epomax;
+ k = epopos - 6 * MS;
+ if (k < 0)
+ k += SECOND;
+ j = epopos + 6 * MS;
+ if (j >= SECOND)
+ i -= SECOND;
+ up->eposnr = wwv_snr(epomax, max(abs(epobuf[k]),
+ abs(epobuf[j])));
+ epopos -= pdelay + 5 * MS;
+ if (epopos < 0)
+ epopos += SECOND;
+ wwv_endpoc(peer, epopos);
+ if (!(up->status & SSYNC))
+ up->alarm |= SYNERR;
+ epomax = 0;
+ if (!(up->status & MSYNC))
+ wwv_gain(peer);
+ }
+}
+
+
+/*
+ * wwv_qrz - identify and acquire WWV/WWVH minute sync pulse
+ *
+ * This routine implements a virtual station process used to acquire
+ * minute sync and to mitigate among the ten frequency and station
+ * combinations. During minute sync acquisition the process probes each
+ * frequency in turn for the minute pulse from either station, which
+ * involves searching through the entire minute of samples. After
+ * finding a candidate, the process searches only the seconds before and
+ * after the candidate for the signal and all other seconds for the
+ * noise.
+ *
+ * Students of radar receiver technology will discover this algorithm
+ * amounts to a range gate discriminator. The discriminator requires
+ * that the peak minute pulse amplitude be at least 2000 and the SNR be
+ * at least 6 dB. In addition after finding a candidate, The peak second
+ * pulse amplitude must be at least 2000, the SNR at least 6 dB and the
+ * difference between the current and previous epoch must be less than
+ * 7.5 ms, which corresponds to a frequency error of 125 PPM.. A compare
+ * counter keeps track of the number of successive intervals which
+ * satisfy these criteria.
+ *
+ * Note that, while the minute pulse is found by by the discriminator,
+ * the actual value is determined from the second epoch. The assumption
+ * is that the discriminator peak occurs about 800 ms into the second,
+ * so the timing is retarted to the previous second epoch.
+ */
+static void
+wwv_qrz(
+ struct peer *peer, /* peer structure pointer */
+ struct sync *sp, /* sync channel structure */
+ double syncx, /* bandpass filtered sync signal */
+ int pdelay /* propagation delay (samples) */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ char tbuf[80]; /* monitor buffer */
+ double snr; /* on-pulse/off-pulse ratio (dB) */
+ long epoch, fpoch;
+ int isgood;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Find the sample with peak energy, which defines the minute
+ * epoch. If a sample has been found with good amplitude,
+ * accumulate the noise squares for all except the second before
+ * and after that position.
+ */
+ isgood = up->epomax > STHR && up->eposnr > SSNR;
+ if (isgood) {
+ fpoch = up->mphase % SECOND - up->tepoch;
+ if (fpoch < 0)
+ fpoch += SECOND;
+ } else {
+ fpoch = pdelay + SYNSIZ;
+ }
+ epoch = up->mphase - fpoch;
+ if (epoch < 0)
+ epoch += MINUTE;
+ if (syncx > sp->maxamp) {
+ sp->maxamp = syncx;
+ sp->pos = epoch;
+ }
+ if (abs((epoch - sp->lastpos) % MINUTE) > SECOND)
+ sp->noiamp += syncx;
+
+ /*
+ * At the end of the minute, determine the epoch of the
+ * sync pulse, as well as the SNR and difference between
+ * the current and previous epoch, which represents the
+ * intrinsic frequency error plus jitter.
+ */
+ if (up->mphase == 0) {
+ sp->synmax = sqrt(sp->maxamp);
+ sp->synmin = sqrt(sp->noiamp / (MINUTE - 2 * SECOND));
+ epoch = (sp->pos - sp->lastpos) % MINUTE;
+
+ /*
+ * If not yet in minute sync, we have to do a little
+ * dance to find a valid minute sync pulse, emphasis
+ * valid.
+ */
+ snr = wwv_snr(sp->synmax, sp->synmin);
+ isgood = isgood && sp->synmax > ATHR && snr > ASNR;
+ switch (sp->count) {
+
+ /*
+ * In state 0 the station was not heard during the
+ * previous probe. Look for the biggest blip greater
+ * than the amplitude threshold in the minute and assume
+ * that the minute sync pulse. We're fishing here, since
+ * the range gate has not yet been determined. If found,
+ * bump to state 1.
+ */
+ case 0:
+ if (sp->synmax >= ATHR)
+ sp->count++;
+ break;
+
+ /*
+ * In state 1 a candidate blip has been found and the
+ * next minute has been searched for another blip. If
+ * none are found acceptable, drop back to state 0 and
+ * hunt some more. Otherwise, a legitimate minute pulse
+ * may have been found, so bump to state 2.
+ */
+ case 1:
+ if (!isgood) {
+ sp->count = 0;
+ break;
+ }
+ sp->count++;
+ break;
+
+ /*
+ * In states 2 and above, continue to groom samples as
+ * before and drop back to state 0 if the groom fails.
+ * If it succeeds, set the epoch and bump to the next
+ * state until reaching the threshold, if ever.
+ */
+ default:
+ if (!isgood || abs(epoch) > AWND * MS) {
+ sp->count = 0;
+ break;
+ }
+ sp->mepoch = sp->pos;
+ sp->count++;
+ break;
+ }
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ sprintf(tbuf,
+ "wwv8 %d %3d %s %d %5.0f %5.1f %5ld %5d %ld",
+ up->port, up->gain, sp->refid, sp->count,
+ sp->synmax, snr, sp->pos, up->tepoch,
+ epoch);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif
+ }
+ sp->lastpos = sp->pos;
+ sp->maxamp = sp->noiamp = 0;
+ }
+}
+
+
+/*
+ * wwv_endpoc - identify and acquire second sync pulse
+ *
+ * This routine is called at the end of the second sync interval. It
+ * determines the second sync epoch position within the interval and
+ * disciplines the sample clock using a frequency-lock loop (FLL).
+ *
+ * Second sync is determined in the RF input routine as the maximum
+ * over all 8000 samples in the second comb filter. To assure accurate
+ * and reliable time and frequency discipline, this routine performs a
+ * great deal of heavy-handed heuristic data filtering and grooming.
+ *
+ * Note that, since the minute sync pulse is very wide (800 ms), precise
+ * minute sync epoch acquisition requires at least a rough estimate of
+ * the second sync pulse (5 ms). This becomes more important in choppy
+ * conditions at the lower frequencies at night, since sferics and
+ * cochannel crude can badly distort the minute pulse.
+ */
+static void
+wwv_endpoc(
+ struct peer *peer, /* peer structure pointer */
+ int epopos /* epoch max position */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ static int epoch_mf[3]; /* epoch median filter */
+ static int xepoch; /* last second epoch */
+ static int zepoch; /* last averaging interval epoch */
+ static int syncnt; /* run length counter */
+ static int maxrun; /* longest run length */
+ static int mepoch; /* longest run epoch */
+ static int avgcnt; /* averaging interval counter */
+ static int avginc; /* averaging ratchet */
+ static int iniflg; /* initialization flag */
+ char tbuf[80]; /* monitor buffer */
+ double dtemp;
+ int tmp2;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (!iniflg) {
+ iniflg = 1;
+ memset((char *)epoch_mf, 0, sizeof(epoch_mf));
+ }
+
+ /*
+ * A three-stage median filter is used to help denoise the
+ * second sync pulse. The median sample becomes the candidate
+ * epoch.
+ */
+ epoch_mf[2] = epoch_mf[1];
+ epoch_mf[1] = epoch_mf[0];
+ epoch_mf[0] = epopos;
+ if (epoch_mf[0] > epoch_mf[1]) {
+ if (epoch_mf[1] > epoch_mf[2])
+ up->tepoch = epoch_mf[1]; /* 0 1 2 */
+ else if (epoch_mf[2] > epoch_mf[0])
+ up->tepoch = epoch_mf[0]; /* 2 0 1 */
+ else
+ up->tepoch = epoch_mf[2]; /* 0 2 1 */
+ } else {
+ if (epoch_mf[1] < epoch_mf[2])
+ up->tepoch = epoch_mf[1]; /* 2 1 0 */
+ else if (epoch_mf[2] < epoch_mf[0])
+ up->tepoch = epoch_mf[0]; /* 1 0 2 */
+ else
+ up->tepoch = epoch_mf[2]; /* 1 2 0 */
+ }
+
+ /*
+ * If the signal amplitude or SNR fall below thresholds or if no
+ * stations are heard, dim the second sync lamp and start over.
+ */
+ if (!(up->status & (SELV | SELH)) || up->epomax < STHR ||
+ up->eposnr < SSNR) {
+ up->status &= ~(SSYNC | FGATE);
+ avgcnt = syncnt = maxrun = 0;
+ return;
+ }
+ avgcnt++;
+
+ /*
+ * If the epoch candidate is the same as the last one, increment
+ * the compare counter. If not, save the length and epoch of the
+ * current run for use later and reset the counter.
+ */
+ tmp2 = (up->tepoch - xepoch) % SECOND;
+ if (tmp2 == 0) {
+ syncnt++;
+ } else {
+ if (maxrun > 0 && mepoch == xepoch) {
+ maxrun += syncnt;
+ } else if (syncnt > maxrun) {
+ maxrun = syncnt;
+ mepoch = xepoch;
+ }
+ syncnt = 0;
+ }
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & (SSYNC |
+ MSYNC))) {
+ sprintf(tbuf,
+ "wwv1 %04x %5.0f %5.1f %5d %5d %4d %4d",
+ up->status, up->epomax, up->eposnr, up->tepoch,
+ tmp2, avgcnt, syncnt);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+
+ /*
+ * The sample clock frequency is disciplined using a first order
+ * feedback loop with time constant consistent with the Allan
+ * intercept of typical computer clocks.
+ *
+ * The frequency update is calculated from the epoch change in
+ * 125-us units divided by the averaging interval in seconds.
+ * The averaging interval affects other receiver functions,
+ * including the the 1000/1200-Hz comb filter and codec clock
+ * loop. It also affects the 100-Hz subcarrier loop and the bit
+ * and digit comparison counter thresholds.
+ */
+ if (avgcnt < up->avgint) {
+ xepoch = up->tepoch;
+ return;
+ }
+
+ /*
+ * During the averaging interval the longest run of identical
+ * epoches is determined. If the longest run is at least 10
+ * seconds, the SSYNC bit is lit and the value becomes the
+ * reference epoch for the next interval. If not, the second
+ * synd lamp is dark and flashers set.
+ */
+ if (maxrun > 0 && mepoch == xepoch) {
+ maxrun += syncnt;
+ } else if (syncnt > maxrun) {
+ maxrun = syncnt;
+ mepoch = xepoch;
+ }
+ xepoch = up->tepoch;
+ if (maxrun > SCMP) {
+ up->status |= SSYNC;
+ up->yepoch = mepoch;
+ } else {
+ up->status &= ~SSYNC;
+ }
+
+ /*
+ * If the epoch change over the averaging interval is less than
+ * 1 ms, the frequency is adjusted, but clamped at +-125 PPM. If
+ * greater than 1 ms, the counter is decremented. If the epoch
+ * change is less than 0.5 ms, the counter is incremented. If
+ * the counter increments to +3, the averaging interval is
+ * doubled and the counter set to zero; if it increments to -3,
+ * the interval is halved and the counter set to zero.
+ *
+ * Here be spooks. From careful observations, the epoch
+ * sometimes makes a long run of identical samples, then takes a
+ * lurch due apparently to lost interrupts or spooks. If this
+ * happens, the epoch change times the maximum run length will
+ * be greater than the averaging interval, so the lurch should
+ * be believed but the frequency left alone. Really intricate
+ * here.
+ */
+ if (maxrun == 0)
+ mepoch = up->tepoch;
+ dtemp = (mepoch - zepoch) % SECOND;
+ if (up->status & FGATE) {
+ if (abs(dtemp) < MAXFREQ * MINAVG) {
+ if (maxrun * abs(mepoch - zepoch) <
+ avgcnt) {
+ up->freq += dtemp / avgcnt;
+ if (up->freq > MAXFREQ)
+ up->freq = MAXFREQ;
+ else if (up->freq < -MAXFREQ)
+ up->freq = -MAXFREQ;
+ }
+ if (abs(dtemp) < MAXFREQ * MINAVG / 2) {
+ if (avginc < 3) {
+ avginc++;
+ } else {
+ if (up->avgint < MAXAVG) {
+ up->avgint <<= 1;
+ avginc = 0;
+ }
+ }
+ }
+ } else {
+ if (avginc > -3) {
+ avginc--;
+ } else {
+ if (up->avgint > MINAVG) {
+ up->avgint >>= 1;
+ avginc = 0;
+ }
+ }
+ }
+ }
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ sprintf(tbuf,
+ "wwv2 %04x %4.0f %4d %4d %2d %4d %4.0f %6.1f",
+ up->status, up->epomax, mepoch, maxrun, avginc,
+ avgcnt, dtemp, up->freq * 1e6 / SECOND);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ up->status |= FGATE;
+ zepoch = mepoch;
+ avgcnt = syncnt = maxrun = 0;
+}
+
+
+/*
+ * wwv_epoch - epoch scanner
+ *
+ * This routine scans the receiver second epoch to determine the signal
+ * amplitudes and pulse timings. Receiver synchronization is determined
+ * by the minute sync pulse detected in the wwv_rf() routine and the
+ * second sync pulse detected in the wwv_epoch() routine. A pulse width
+ * discriminator extracts data signals from the 100-Hz subcarrier. The
+ * transmitted signals are delayed by the propagation delay, receiver
+ * delay and filter delay of this program. Delay corrections are
+ * introduced separately for WWV and WWVH.
+ *
+ * Most communications radios use a highpass filter in the audio stages,
+ * which can do nasty things to the subcarrier phase relative to the
+ * sync pulses. Therefore, the data subcarrier reference phase is
+ * disciplined using the hardlimited quadrature-phase signal sampled at
+ * the same time as the in-phase signal. The phase tracking loop uses
+ * phase adjustments of plus-minus one sample (125 us).
+ */
+static void
+wwv_epoch(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct chan *cp;
+ static double dpulse; /* data pulse length */
+ double dtemp;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Sample the minute sync pulse envelopes at epoch 800 for both
+ * the WWV and WWVH stations. This will be used later for
+ * channel and station mitigation. Note that the seconds epoch
+ * is set here well before the end of the second to make sure we
+ * never seet the epoch backwards.
+ */
+ if (up->rphase == 800 * MS) {
+ up->repoch = up->yepoch;
+ cp = &up->mitig[up->achan];
+ cp->wwv.synamp = cp->wwv.amp;
+ cp->wwvh.synamp = cp->wwvh.amp;
+ }
+
+ /*
+ * Sample the data subcarrier at epoch 15 ms, giving a guard
+ * time of +-15 ms from the beginning of the second until the
+ * pulse rises at 30 ms. The I-channel amplitude is used to
+ * calculate the slice level. The envelope amplitude is used
+ * during the probe seconds to determine the SNR. There is a
+ * compromise here; we want to delay the sample as long as
+ * possible to give the radio time to change frequency and the
+ * AGC to stabilize, but as early as possible if the second
+ * epoch is not exact.
+ */
+ if (up->rphase == 15 * MS) {
+ up->noiamp = up->irig * up->irig + up->qrig * up->qrig;
+
+ /*
+ * Sample the data subcarrier at epoch 215 ms, giving a guard
+ * time of +-15 ms from the earliest the pulse peak can be
+ * reached to the earliest it can begin to fall. For the data
+ * channel latch the I-channel amplitude for all except the
+ * probe seconds and adjust the 100-Hz reference oscillator
+ * phase using the Q-channel amplitude at this epoch. For the
+ * probe channel latch the envelope amplitude.
+ */
+ } else if (up->rphase == 215 * MS) {
+ up->sigsig = up->irig;
+ if (up->sigsig < 0)
+ up->sigsig = 0;
+ up->datpha = up->qrig / up->avgint;
+ if (up->datpha >= 0) {
+ up->datapt++;
+ if (up->datapt >= 80)
+ up->datapt -= 80;
+ } else {
+ up->datapt--;
+ if (up->datapt < 0)
+ up->datapt += 80;
+ }
+ up->sigamp = up->irig * up->irig + up->qrig * up->qrig;
+
+ /*
+ * The slice level is set half way between the peak signal and
+ * noise levels. Sample the negative zero crossing after epoch
+ * 200 ms and record the epoch at that time. This defines the
+ * length of the data pulse, which will later be converted into
+ * scaled bit probabilities.
+ */
+ } else if (up->rphase > 200 * MS) {
+ dtemp = (up->sigsig + sqrt(up->noiamp)) / 2;
+ if (up->irig < dtemp && dpulse == 0)
+ dpulse = up->rphase;
+ }
+
+ /*
+ * At the end of the second crank the clock state machine and
+ * adjust the codec gain. Note the epoch is buffered from the
+ * center of the second in order to avoid jitter while the
+ * seconds synch is diddling the epoch. Then, determine the true
+ * offset and update the median filter in the driver interface.
+ *
+ * Sample the data subcarrier envelope at the end of the second
+ * to determine the SNR for the pulse. This gives a guard time
+ * of +-30 ms from the decay of the longest pulse to the rise of
+ * the next pulse.
+ */
+ up->rphase++;
+ if (up->mphase % SECOND == up->repoch) {
+ up->datsnr = wwv_snr(up->sigsig, sqrt(up->noiamp));
+ wwv_rsec(peer, dpulse);
+ wwv_gain(peer);
+ up->rphase = dpulse = 0;
+ }
+}
+
+
+/*
+ * wwv_rsec - process receiver second
+ *
+ * This routine is called at the end of each receiver second to
+ * implement the per-second state machine. The machine assembles BCD
+ * digit bits, decodes miscellaneous bits and dances the leap seconds.
+ *
+ * Normally, the minute has 60 seconds numbered 0-59. If the leap
+ * warning bit is set, the last minute (1439) of 30 June (day 181 or 182
+ * for leap years) or 31 December (day 365 or 366 for leap years) is
+ * augmented by one second numbered 60. This is accomplished by
+ * extending the minute interval by one second and teaching the state
+ * machine to ignore it.
+ */
+static void
+wwv_rsec(
+ struct peer *peer, /* peer structure pointer */
+ double dpulse
+ )
+{
+ static int iniflg; /* initialization flag */
+ static double bcddld[4]; /* BCD data bits */
+ static double bitvec[61]; /* bit integrator for misc bits */
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct chan *cp;
+ struct sync *sp, *rp;
+ l_fp offset; /* offset in NTP seconds */
+ double bit; /* bit likelihood */
+ char tbuf[80]; /* monitor buffer */
+ int sw, arg, nsec;
+#ifdef IRIG_SUCKS
+ int i;
+ l_fp ltemp;
+#endif /* IRIG_SUCKS */
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (!iniflg) {
+ iniflg = 1;
+ memset((char *)bitvec, 0, sizeof(bitvec));
+ }
+
+ /*
+ * The bit represents the probability of a hit on zero (negative
+ * values), a hit on one (positive values) or a miss (zero
+ * value). The likelihood vector is the exponential average of
+ * these probabilities. Only the bits of this vector
+ * corresponding to the miscellaneous bits of the timecode are
+ * used, but it's easier to do them all. After that, crank the
+ * seconds state machine.
+ */
+ nsec = up->rsec + 1;
+ bit = wwv_data(up, dpulse);
+ bitvec[up->rsec] += (bit - bitvec[up->rsec]) / TCONST;
+ sw = progx[up->rsec].sw;
+ arg = progx[up->rsec].arg;
+ switch (sw) {
+
+ /*
+ * Ignore this second.
+ */
+ case IDLE: /* 9, 45-49 */
+ break;
+
+ /*
+ * Probe channel stuff
+ *
+ * The WWV/H format contains data pulses in second 59 (position
+ * identifier), second 1 (not used) and the minute sync pulse in
+ * second 0. At the end of second 58, QSY to the probe channel,
+ * which rotates over all WWV/H frequencies. At the end of
+ * second 1 QSY back to the data channel.
+ *
+ * At the end of second 0 save the minute sync pulse peak value
+ * previously latched at 800 ms.
+ */
+ case SYNC2: /* 0 */
+ cp = &up->mitig[up->achan];
+ cp->wwv.synmax = sqrt(cp->wwv.synamp);
+ cp->wwvh.synmax = sqrt(cp->wwvh.synamp);
+ break;
+
+ /*
+ * At the end of second 1 determine the minute sync pulse
+ * amplitude and SNR and set SYNCNG if these values are below
+ * thresholds. Determine the data pulse amplitude and SNR and
+ * set DATANG if these values are below thresholds. Set ERRRNG
+ * if data pulses in second 59 and second 1 are decoded in
+ * error. Shift a 1 into the reachability register if SYNCNG and
+ * DATANG are both lit; otherwise shift a 0. Ignore ERRRNG for
+ * the present. The number of 1 bits in the last six intervals
+ * represents the channel metric used by the mitigation routine.
+ * Finally, QSY back to the data channel.
+ */
+ case SYNC3: /* 1 */
+ cp = &up->mitig[up->achan];
+ cp->sigamp = sqrt(up->sigamp);
+ cp->noiamp = sqrt(up->noiamp);
+ cp->datsnr = wwv_snr(cp->sigamp, cp->noiamp);
+
+ /*
+ * WWV station
+ */
+ sp = &cp->wwv;
+ sp->synmin = sqrt((sp->synmin + sp->synamp) / 2.);
+ sp->synsnr = wwv_snr(sp->synmax, sp->synmin);
+ sp->select &= ~(SYNCNG | DATANG | ERRRNG);
+ if (sp->synmax < QTHR || sp->synsnr < QSNR)
+ sp->select |= SYNCNG;
+ if (cp->sigamp < XTHR || cp->datsnr < XSNR)
+ sp->select |= DATANG;
+ if (up->errcnt > 2)
+ sp->select |= ERRRNG;
+ sp->reach <<= 1;
+ if (sp->reach & (1 << AMAX))
+ sp->count--;
+ if (!(sp->select & (SYNCNG | DATANG))) {
+ sp->reach |= 1;
+ sp->count++;
+ }
+
+ /*
+ * WWVH station
+ */
+ rp = &cp->wwvh;
+ rp->synmin = sqrt((rp->synmin + rp->synamp) / 2.);
+ rp->synsnr = wwv_snr(rp->synmax, rp->synmin);
+ rp->select &= ~(SYNCNG | DATANG | ERRRNG);
+ if (rp->synmax < QTHR || rp->synsnr < QSNR)
+ rp->select |= SYNCNG;
+ if (cp->sigamp < XTHR || cp->datsnr < XSNR)
+ rp->select |= DATANG;
+ if (up->errcnt > 2)
+ rp->select |= ERRRNG;
+ rp->reach <<= 1;
+ if (rp->reach & (1 << AMAX))
+ rp->count--;
+ if (!(rp->select & (SYNCNG | DATANG | ERRRNG))) {
+ rp->reach |= 1;
+ rp->count++;
+ }
+
+ /*
+ * Set up for next minute.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ sprintf(tbuf,
+ "wwv5 %2d %04x %3d %4d %d %.0f/%.1f %s %04x %.0f %.0f/%.1f %s %04x %.0f %.0f/%.1f",
+ up->port, up->status, up->gain, up->yepoch,
+ up->errcnt, cp->sigamp, cp->datsnr,
+ sp->refid, sp->reach & 0xffff,
+ wwv_metric(sp), sp->synmax, sp->synsnr,
+ rp->refid, rp->reach & 0xffff,
+ wwv_metric(rp), rp->synmax, rp->synsnr);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+#ifdef ICOM
+ if (up->fd_icom > 0)
+ wwv_qsy(peer, up->dchan);
+#endif /* ICOM */
+ up->status &= ~SFLAG;
+ up->errcnt = 0;
+ up->alarm = 0;
+ wwv_newchan(peer);
+ break;
+
+ /*
+ * Save the bit probability in the BCD data vector at the index
+ * given by the argument. Note that all bits of the vector have
+ * to be above the data gate threshold for the digit to be
+ * considered valid. Bits not used in the digit are forced to
+ * zero and not checked for errors.
+ */
+ case COEF: /* 4-7, 10-13, 15-17, 20-23,
+ 25-26, 30-33, 35-38, 40-41,
+ 51-54 */
+ if (up->status & DGATE)
+ up->status |= BGATE;
+ bcddld[arg] = bit;
+ break;
+
+ case COEF2: /* 18, 27-28, 42-43 */
+ bcddld[arg] = 0;
+ break;
+
+ /*
+ * Correlate coefficient vector with each valid digit vector and
+ * save in decoding matrix. We step through the decoding matrix
+ * digits correlating each with the coefficients and saving the
+ * greatest and the next lower for later SNR calculation.
+ */
+ case DECIM2: /* 29 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd2);
+ break;
+
+ case DECIM3: /* 44 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd3);
+ break;
+
+ case DECIM6: /* 19 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd6);
+ break;
+
+ case DECIM9: /* 8, 14, 24, 34, 39 */
+ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd9);
+ break;
+
+ /*
+ * Miscellaneous bits. If above the positive threshold, declare
+ * 1; if below the negative threshold, declare 0; otherwise
+ * raise the SYMERR alarm. At the end of second 58, QSY to the
+ * probe channel. The design is intended to preserve the bits
+ * over periods of signal loss.
+ */
+ case MSC20: /* 55 */
+ wwv_corr4(peer, &up->decvec[YR + 1], bcddld, bcd9);
+ /* fall through */
+
+ case MSCBIT: /* 2-3, 50, 56-57 */
+ if (bitvec[up->rsec] > BTHR)
+ up->misc |= arg;
+ else if (bitvec[up->rsec] < -BTHR)
+ up->misc &= ~arg;
+ else
+ up->alarm |= SYMERR;
+ break;
+
+ /*
+ * Save the data channel gain, then QSY to the probe channel.
+ */
+ case MSC21: /* 58 */
+ if (bitvec[up->rsec] > BTHR)
+ up->misc |= arg;
+ else if (bitvec[up->rsec] < -BTHR)
+ up->misc &= ~arg;
+ else
+ up->alarm |= SYMERR;
+ up->mitig[up->dchan].gain = up->gain;
+#ifdef ICOM
+ if (up->fd_icom > 0) {
+ up->schan = (up->schan + 1) % NCHAN;
+ wwv_qsy(peer, up->schan);
+ }
+#endif /* ICOM */
+ up->status |= SFLAG | SELV | SELH;
+ up->errbit = up->errcnt;
+ up->errcnt = 0;
+ break;
+
+ /*
+ * The endgames
+ *
+ * During second 59 the receiver and codec AGC are settling
+ * down, so the data pulse is unusable. At the end of this
+ * second, latch the minute sync pulse noise floor. Then do the
+ * minute processing and update the system clock. If a leap
+ * second sail on to the next second (60); otherwise, set up for
+ * the next minute.
+ */
+ case MIN1: /* 59 */
+ cp = &up->mitig[up->achan];
+ cp->wwv.synmin = cp->wwv.synamp;
+ cp->wwvh.synmin = cp->wwvh.synamp;
+
+ /*
+ * Dance the leap if necessary and the kernel has the
+ * right stuff. Then, wind up the clock and initialize
+ * for the following minute. If the leap dance, note the
+ * kernel is armed one second before the actual leap is
+ * scheduled.
+ */
+ if (up->status & SSYNC && up->digcnt >= 9)
+ up->status |= INSYNC;
+ if (up->status & LEPDAY) {
+ pp->leap = LEAP_ADDSECOND;
+ } else {
+ pp->leap = LEAP_NOWARNING;
+ wwv_tsec(up);
+ nsec = up->digcnt = 0;
+ }
+ pp->lencode = timecode(up, pp->a_lastcode);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("wwv: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif /* DEBUG */
+ if (up->status & INSYNC && up->watch < HOLD)
+ refclock_receive(peer);
+ break;
+
+ /*
+ * If LEPDAY is set on the last minute of 30 June or 31
+ * December, the LEPSEC bit is set. At the end of the minute in
+ * which LEPSEC is set the transmitter and receiver insert an
+ * extra second (60) in the timescale and the minute sync skips
+ * a second. We only get to test this wrinkle at intervals of
+ * about 18 months; the actual mileage may vary.
+ */
+ case MIN2: /* 60 */
+ wwv_tsec(up);
+ nsec = up->digcnt = 0;
+ break;
+ }
+
+ /*
+ * If digit sync has not been acquired before timeout or if no
+ * station has been heard, game over and restart from scratch.
+ */
+ if (!(up->status & DSYNC) && (!(up->status & (SELV | SELH)) ||
+ up->watch > DIGIT)) {
+ wwv_newgame(peer);
+ return;
+ }
+
+ /*
+ * If no timestamps have been struck before timeout, game over
+ * and restart from scratch.
+ */
+ if (up->watch > PANIC) {
+ wwv_newgame(peer);
+ return;
+ }
+ pp->disp += AUDIO_PHI;
+ up->rsec = nsec;
+
+#ifdef IRIG_SUCKS
+ /*
+ * You really don't wanna know what comes down here. Leave it to
+ * say Solaris 2.8 broke the nice clean audio stream, apparently
+ * affected by a 5-ms sawtooth jitter. Sundown on Solaris. This
+ * leaves a little twilight.
+ *
+ * The scheme involves differentiation, forward learning and
+ * integration. The sawtooth has a period of 11 seconds. The
+ * timestamp differences are integrated and subtracted from the
+ * signal.
+ */
+ ltemp = pp->lastrec;
+ L_SUB(&ltemp, &pp->lastref);
+ if (ltemp.l_f < 0)
+ ltemp.l_i = -1;
+ else
+ ltemp.l_i = 0;
+ pp->lastref = pp->lastrec;
+ if (!L_ISNEG(&ltemp))
+ L_CLR(&up->wigwag);
+ else
+ L_ADD(&up->wigwag, &ltemp);
+ L_SUB(&pp->lastrec, &up->wigwag);
+ up->wiggle[up->wp] = ltemp;
+
+ /*
+ * Bottom fisher. To understand this, you have to know about
+ * velocity microphones and AM transmitters. No further
+ * explanation is offered, as this is truly a black art.
+ */
+ up->wigbot[up->wp] = pp->lastrec;
+ for (i = 0; i < WIGGLE; i++) {
+ if (i != up->wp)
+ up->wigbot[i].l_ui++;
+ L_SUB(&pp->lastrec, &up->wigbot[i]);
+ if (L_ISNEG(&pp->lastrec))
+ L_ADD(&pp->lastrec, &up->wigbot[i]);
+ else
+ pp->lastrec = up->wigbot[i];
+ }
+ up->wp++;
+ up->wp %= WIGGLE;
+#endif /* IRIG_SUCKS */
+
+ /*
+ * If victory has been declared and seconds sync is lit, strike
+ * a timestamp. It should not be a surprise, especially if the
+ * radio is not tunable, that sometimes no stations are above
+ * the noise and the reference ID set to NONE.
+ */
+ if (up->status & INSYNC && up->status & SSYNC) {
+ pp->second = up->rsec;
+ pp->minute = up->decvec[MN].digit + up->decvec[MN +
+ 1].digit * 10;
+ pp->hour = up->decvec[HR].digit + up->decvec[HR +
+ 1].digit * 10;
+ pp->day = up->decvec[DA].digit + up->decvec[DA +
+ 1].digit * 10 + up->decvec[DA + 2].digit * 100;
+ pp->year = up->decvec[YR].digit + up->decvec[YR +
+ 1].digit * 10;
+ pp->year += 2000;
+ L_CLR(&offset);
+ if (!clocktime(pp->day, pp->hour, pp->minute,
+ pp->second, GMT, up->timestamp.l_ui,
+ &pp->yearstart, &offset.l_ui)) {
+ up->errflg = CEVNT_BADTIME;
+ } else {
+ up->watch = 0;
+ pp->disp = 0;
+ refclock_process_offset(pp, offset,
+ up->timestamp, PDELAY);
+ }
+ }
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status &
+ DSYNC)) {
+ sprintf(tbuf,
+ "wwv3 %2d %04x %5.0f %5.1f %5.0f %5.1f %5.0f",
+ up->rsec, up->status, up->epomax, up->eposnr,
+ up->sigsig, up->datsnr, bit);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+}
+
+
+/*
+ * wwv_data - calculate bit probability
+ *
+ * This routine is called at the end of the receiver second to calculate
+ * the probabilities that the previous second contained a zero (P0), one
+ * (P1) or position indicator (P2) pulse. If not in sync or if the data
+ * bit is bad, a bit error is declared and the probabilities are forced
+ * to zero. Otherwise, the data gate is enabled and the probabilities
+ * are calculated. Note that the data matched filter contributes half
+ * the pulse width, or 85 ms.
+ *
+ * It's important to observe that there are three conditions to
+ * determine: average to +1 (hit), average to -1 (miss) or average to
+ * zero (erasure). The erasure condition results from insufficient
+ * signal (noise) and has no bias toward either a hit or miss.
+ */
+static double
+wwv_data(
+ struct wwvunit *up, /* driver unit pointer */
+ double pulse /* pulse length (sample units) */
+ )
+{
+ double p0, p1, p2; /* probabilities */
+ double dpulse; /* pulse length in ms */
+
+ p0 = p1 = p2 = 0;
+ dpulse = pulse - DATSIZ / 2;
+
+ /*
+ * If no station is being tracked, if either the data amplitude
+ * or SNR are below threshold or if the pulse length is less
+ * than 170 ms, declare an erasure.
+ */
+ if (!(up->status & (SELV | SELH)) || up->sigsig < DTHR ||
+ up->datsnr < DSNR || dpulse < DATSIZ) {
+ up->status |= DGATE;
+ up->errcnt++;
+ if (up->errcnt > MAXERR)
+ up->alarm |= MODERR;
+ return (0);
+ }
+
+ /*
+ * The probability of P0 is one below 200 ms falling to zero at
+ * 500 ms. The probability of P1 is zero below 200 ms rising to
+ * one at 500 ms and falling to zero at 800 ms. The probability
+ * of P2 is zero below 500 ms, rising to one above 800 ms.
+ */
+ up->status &= ~DGATE;
+ if (dpulse < (200 * MS)) {
+ p0 = 1;
+ } else if (dpulse < 500 * MS) {
+ dpulse -= 200 * MS;
+ p1 = dpulse / (300 * MS);
+ p0 = 1 - p1;
+ } else if (dpulse < 800 * MS) {
+ dpulse -= 500 * MS;
+ p2 = dpulse / (300 * MS);
+ p1 = 1 - p2;
+ } else {
+ p2 = 1;
+ }
+
+ /*
+ * The ouput is a metric that ranges from -1 (P0), to +1 (P1)
+ * scaled for convenience. An output of zero represents an
+ * erasure, either because of a data error or pulse length
+ * greater than 500 ms. At the moment, we don't use P2.
+ */
+ return ((p1 - p0) * MAXSIG);
+}
+
+
+/*
+ * wwv_corr4 - determine maximum likelihood digit
+ *
+ * This routine correlates the received digit vector with the BCD
+ * coefficient vectors corresponding to all valid digits at the given
+ * position in the decoding matrix. The maximum value corresponds to the
+ * maximum likelihood digit, while the ratio of this value to the next
+ * lower value determines the likelihood function. Note that, if the
+ * digit is invalid, the likelihood vector is averaged toward a miss.
+ */
+static void
+wwv_corr4(
+ struct peer *peer, /* peer unit pointer */
+ struct decvec *vp, /* decoding table pointer */
+ double data[], /* received data vector */
+ double tab[][4] /* correlation vector array */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ double topmax, nxtmax; /* metrics */
+ double acc; /* accumulator */
+ char tbuf[80]; /* monitor buffer */
+ int mldigit; /* max likelihood digit */
+ int diff; /* decoding difference */
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Correlate digit vector with each BCD coefficient vector. If
+ * any BCD digit bit is bad, consider all bits a miss.
+ */
+ mldigit = 0;
+ topmax = nxtmax = -MAXSIG;
+ for (i = 0; tab[i][0] != 0; i++) {
+ acc = 0;
+ for (j = 0; j < 4; j++) {
+ if (!(up->status & BGATE))
+ acc += data[j] * tab[i][j];
+ }
+ acc = (vp->like[i] += (acc - vp->like[i]) / TCONST);
+ if (acc > topmax) {
+ nxtmax = topmax;
+ topmax = acc;
+ mldigit = i;
+ } else if (acc > nxtmax) {
+ nxtmax = acc;
+ }
+ }
+ vp->mldigit = mldigit;
+ vp->digprb = topmax;
+ vp->digsnr = wwv_snr(topmax, nxtmax);
+
+ /*
+ * The maximum likelihood digit is compared with the current
+ * clock digit. The difference represents the decoding phase
+ * error. If the clock is not yet synchronized, the phase error
+ * is corrected even of the digit probability and likelihood are
+ * below thresholds. This avoids lengthy averaging times should
+ * a carry mistake occur. However, the digit is not declared
+ * synchronized until these values are above thresholds and the
+ * last five decoded values are identical. If the clock is
+ * synchronized, the phase error is not corrected unless the
+ * last five digits are all above thresholds and identical. This
+ * avoids mistakes when the signal is coming out of the noise
+ * and the SNR is very marginal.
+ */
+ diff = mldigit - vp->digit;
+ if (diff < 0)
+ diff += vp->radix;
+ if (diff != vp->phase) {
+ vp->count = 0;
+ vp->phase = diff;
+ }
+ if (vp->digsnr < BSNR) {
+ vp->count = 0;
+ up->alarm |= SYMERR;
+ } else if (vp->digprb < BTHR) {
+ vp->count = 0;
+ up->alarm |= SYMERR;
+ if (!(up->status & INSYNC)) {
+ vp->phase = 0;
+ vp->digit = mldigit;
+ }
+ } else if (vp->count < BCMP) {
+ vp->count++;
+ up->status |= DSYNC;
+ if (!(up->status & INSYNC)) {
+ vp->phase = 0;
+ vp->digit = mldigit;
+ }
+ } else {
+ vp->phase = 0;
+ vp->digit = mldigit;
+ up->digcnt++;
+ }
+ if (vp->digit != mldigit)
+ up->alarm |= DECERR;
+ if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status &
+ INSYNC)) {
+ sprintf(tbuf,
+ "wwv4 %2d %04x %5.0f %2d %d %d %d %d %5.0f %5.1f",
+ up->rsec, up->status, up->epomax, vp->radix,
+ vp->digit, vp->mldigit, vp->phase, vp->count,
+ vp->digprb, vp->digsnr);
+ record_clock_stats(&peer->srcadr, tbuf);
+#ifdef DEBUG
+ if (debug)
+ printf("%s\n", tbuf);
+#endif /* DEBUG */
+ }
+ up->status &= ~BGATE;
+}
+
+
+/*
+ * wwv_tsec - transmitter minute processing
+ *
+ * This routine is called at the end of the transmitter minute. It
+ * implements a state machine that advances the logical clock subject to
+ * the funny rules that govern the conventional clock and calendar.
+ */
+static void
+wwv_tsec(
+ struct wwvunit *up /* driver structure pointer */
+ )
+{
+ int minute, day, isleap;
+ int temp;
+
+ /*
+ * Advance minute unit of the day.
+ */
+ temp = carry(&up->decvec[MN]); /* minute units */
+
+ /*
+ * Propagate carries through the day.
+ */
+ if (temp == 0) /* carry minutes */
+ temp = carry(&up->decvec[MN + 1]);
+ if (temp == 0) /* carry hours */
+ temp = carry(&up->decvec[HR]);
+ if (temp == 0)
+ temp = carry(&up->decvec[HR + 1]);
+
+ /*
+ * Decode the current minute and day. Set leap day if the
+ * timecode leap bit is set on 30 June or 31 December. Set leap
+ * minute if the last minute on leap day. This code fails in
+ * 2400 AD.
+ */
+ minute = up->decvec[MN].digit + up->decvec[MN + 1].digit *
+ 10 + up->decvec[HR].digit * 60 + up->decvec[HR +
+ 1].digit * 600;
+ day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 +
+ up->decvec[DA + 2].digit * 100;
+ isleap = (up->decvec[YR].digit & 0x3) == 0;
+ if (up->misc & SECWAR && (day == (isleap ? 182 : 183) || day ==
+ (isleap ? 365 : 366)) && up->status & INSYNC && up->status &
+ SSYNC)
+ up->status |= LEPDAY;
+ else
+ up->status &= ~LEPDAY;
+ if (up->status & LEPDAY && minute == 1439)
+ up->status |= LEPSEC;
+ else
+ up->status &= ~LEPSEC;
+
+ /*
+ * Roll the day if this the first minute and propagate carries
+ * through the year.
+ */
+ if (minute != 1440)
+ return;
+ minute = 0;
+ while (carry(&up->decvec[HR]) != 0); /* advance to minute 0 */
+ while (carry(&up->decvec[HR + 1]) != 0);
+ day++;
+ temp = carry(&up->decvec[DA]); /* carry days */
+ if (temp == 0)
+ temp = carry(&up->decvec[DA + 1]);
+ if (temp == 0)
+ temp = carry(&up->decvec[DA + 2]);
+
+ /*
+ * Roll the year if this the first day and propagate carries
+ * through the century.
+ */
+ if (day != (isleap ? 365 : 366))
+ return;
+ day = 1;
+ while (carry(&up->decvec[DA]) != 1); /* advance to day 1 */
+ while (carry(&up->decvec[DA + 1]) != 0);
+ while (carry(&up->decvec[DA + 2]) != 0);
+ temp = carry(&up->decvec[YR]); /* carry years */
+ if (temp)
+ carry(&up->decvec[YR + 1]);
+}
+
+
+/*
+ * carry - process digit
+ *
+ * This routine rotates a likelihood vector one position and increments
+ * the clock digit modulo the radix. It returns the new clock digit or
+ * zero if a carry occurred. Once synchronized, the clock digit will
+ * match the maximum likelihood digit corresponding to that position.
+ */
+static int
+carry(
+ struct decvec *dp /* decoding table pointer */
+ )
+{
+ int temp;
+ int j;
+
+ dp->digit++; /* advance clock digit */
+ if (dp->digit == dp->radix) { /* modulo radix */
+ dp->digit = 0;
+ }
+ temp = dp->like[dp->radix - 1]; /* rotate likelihood vector */
+ for (j = dp->radix - 1; j > 0; j--)
+ dp->like[j] = dp->like[j - 1];
+ dp->like[0] = temp;
+ return (dp->digit);
+}
+
+
+/*
+ * wwv_snr - compute SNR or likelihood function
+ */
+static double
+wwv_snr(
+ double signal, /* signal */
+ double noise /* noise */
+ )
+{
+ double rval;
+
+ /*
+ * This is a little tricky. Due to the way things are measured,
+ * either or both the signal or noise amplitude can be negative
+ * or zero. The intent is that, if the signal is negative or
+ * zero, the SNR must always be zero. This can happen with the
+ * subcarrier SNR before the phase has been aligned. On the
+ * other hand, in the likelihood function the "noise" is the
+ * next maximum down from the peak and this could be negative.
+ * However, in this case the SNR is truly stupendous, so we
+ * simply cap at MAXSNR dB.
+ */
+ if (signal <= 0) {
+ rval = 0;
+ } else if (noise <= 0) {
+ rval = MAXSNR;
+ } else {
+ rval = 20 * log10(signal / noise);
+ if (rval > MAXSNR)
+ rval = MAXSNR;
+ }
+ return (rval);
+}
+
+
+/*
+ * wwv_newchan - change to new data channel
+ *
+ * The radio actually appears to have ten channels, one channel for each
+ * of five frequencies and each of two stations (WWV and WWVH), although
+ * if not tunable only the 15 MHz channels appear live. While the radio
+ * is tuned to the working data channel frequency and station for most
+ * of the minute, during seconds 59, 0 and 1 the radio is tuned to a
+ * probe frequency in order to search for minute sync pulse and data
+ * subcarrier from other transmitters.
+ *
+ * The search for WWV and WWVH operates simultaneously, with WWV minute
+ * sync pulse at 1000 Hz and WWVH at 1200 Hz. The probe frequency
+ * rotates each minute over 2.5, 5, 10, 15 and 20 MHz in order and yes,
+ * we all know WWVH is dark on 20 MHz, but few remember when WWV was lit
+ * on 25 MHz.
+ *
+ * This routine selects the best channel using a metric computed from
+ * the reachability register and minute pulse amplitude. Normally, the
+ * award goes to the the channel with the highest metric; but, in case
+ * of ties, the award goes to the channel with the highest minute sync
+ * pulse amplitude and then to the highest frequency.
+ *
+ * The routine performs an important squelch function to keep dirty data
+ * from polluting the integrators. During acquisition and until the
+ * clock is synchronized, the signal metric must be at least MTR (13);
+ * after that the metrict must be at least TTHR (50). If either of these
+ * is not true, the station select bits are cleared so the second sync
+ * is disabled and the data bit integrators averaged to a miss.
+ */
+static void
+wwv_newchan(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct sync *sp, *rp;
+ double rank, dtemp;
+ int i, j;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Search all five station pairs looking for the channel with
+ * maximum metric. If no station is found above thresholds, the
+ * reference ID is set to NONE and we wait for hotter ions.
+ */
+ j = 0;
+ sp = NULL;
+ rank = 0;
+ for (i = 0; i < NCHAN; i++) {
+ rp = &up->mitig[i].wwvh;
+ dtemp = wwv_metric(rp);
+ if (dtemp >= rank) {
+ rank = dtemp;
+ sp = rp;
+ j = i;
+ }
+ rp = &up->mitig[i].wwv;
+ dtemp = wwv_metric(rp);
+ if (dtemp >= rank) {
+ rank = dtemp;
+ sp = rp;
+ j = i;
+ }
+ }
+ up->dchan = j;
+ up->sptr = sp;
+ up->status &= ~(SELV | SELH);
+ memcpy(&pp->refid, "NONE", 4);
+ if ((!(up->status & INSYNC) && rank >= MTHR) || ((up->status &
+ INSYNC) && rank >= TTHR)) {
+ up->status |= sp->select & (SELV | SELH);
+ memcpy(&pp->refid, sp->refid, 4);
+ }
+ if (peer->stratum <= 1)
+ memcpy(&peer->refid, &pp->refid, 4);
+}
+
+
+/*
+ * www_newgame - reset and start over
+ */
+static void
+wwv_newgame(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+ struct chan *cp;
+ int i;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Initialize strategic values. Note we set the leap bits
+ * NOTINSYNC and the refid "NONE".
+ */
+ peer->leap = LEAP_NOTINSYNC;
+ up->watch = up->status = up->alarm = 0;
+ up->avgint = MINAVG;
+ up->freq = 0;
+ up->sptr = NULL;
+ up->gain = MAXGAIN / 2;
+
+ /*
+ * Initialize the station processes for audio gain, select bit,
+ * station/frequency identifier and reference identifier.
+ */
+ memset(up->mitig, 0, sizeof(up->mitig));
+ for (i = 0; i < NCHAN; i++) {
+ cp = &up->mitig[i];
+ cp->gain = up->gain;
+ cp->wwv.select = SELV;
+ sprintf(cp->wwv.refid, "WV%.0f", floor(qsy[i]));
+ cp->wwvh.select = SELH;
+ sprintf(cp->wwvh.refid, "WH%.0f", floor(qsy[i]));
+ }
+ wwv_newchan(peer);
+}
+
+/*
+ * wwv_metric - compute station metric
+ *
+ * The most significant bits represent the number of ones in the
+ * reachability register. The least significant bits represent the
+ * minute sync pulse amplitude. The combined value is scaled 0-100.
+ */
+double
+wwv_metric(
+ struct sync *sp /* station pointer */
+ )
+{
+ double dtemp;
+
+ dtemp = sp->count * MAXSIG;
+ if (sp->synmax < MAXSIG)
+ dtemp += sp->synmax;
+ else
+ dtemp += MAXSIG - 1;
+ dtemp /= (AMAX + 1) * MAXSIG;
+ return (dtemp * 100.);
+}
+
+
+#ifdef ICOM
+/*
+ * wwv_qsy - Tune ICOM receiver
+ *
+ * This routine saves the AGC for the current channel, switches to a new
+ * channel and restores the AGC for that channel. If a tunable receiver
+ * is not available, just fake it.
+ */
+static int
+wwv_qsy(
+ struct peer *peer, /* peer structure pointer */
+ int chan /* channel */
+ )
+{
+ int rval = 0;
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+ if (up->fd_icom > 0) {
+ up->mitig[up->achan].gain = up->gain;
+ rval = icom_freq(up->fd_icom, peer->ttl & 0x7f,
+ qsy[chan]);
+ up->achan = chan;
+ up->gain = up->mitig[up->achan].gain;
+ }
+ return (rval);
+}
+#endif /* ICOM */
+
+
+/*
+ * timecode - assemble timecode string and length
+ *
+ * Prettytime format - similar to Spectracom
+ *
+ * sq yy ddd hh:mm:ss ld dut lset agc iden sig errs freq avgt
+ *
+ * s sync indicator ('?' or ' ')
+ * q error bits (hex 0-F)
+ * yyyy year of century
+ * ddd day of year
+ * hh hour of day
+ * mm minute of hour
+ * ss second of minute)
+ * l leap second warning (' ' or 'L')
+ * d DST state ('S', 'D', 'I', or 'O')
+ * dut DUT sign and magnitude (0.1 s)
+ * lset minutes since last clock update
+ * agc audio gain (0-255)
+ * iden reference identifier (station and frequency)
+ * sig signal quality (0-100)
+ * errs bit errors in last minute
+ * freq frequency offset (PPM)
+ * avgt averaging time (s)
+ */
+static int
+timecode(
+ struct wwvunit *up, /* driver structure pointer */
+ char *ptr /* target string */
+ )
+{
+ struct sync *sp;
+ int year, day, hour, minute, second, dut;
+ char synchar, leapchar, dst;
+ char cptr[50];
+
+
+ /*
+ * Common fixed-format fields
+ */
+ synchar = (up->status & INSYNC) ? ' ' : '?';
+ year = up->decvec[YR].digit + up->decvec[YR + 1].digit * 10 +
+ 2000;
+ day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 +
+ up->decvec[DA + 2].digit * 100;
+ hour = up->decvec[HR].digit + up->decvec[HR + 1].digit * 10;
+ minute = up->decvec[MN].digit + up->decvec[MN + 1].digit * 10;
+ second = 0;
+ leapchar = (up->misc & SECWAR) ? 'L' : ' ';
+ dst = dstcod[(up->misc >> 4) & 0x3];
+ dut = up->misc & 0x7;
+ if (!(up->misc & DUTS))
+ dut = -dut;
+ sprintf(ptr, "%c%1X", synchar, up->alarm);
+ sprintf(cptr, " %4d %03d %02d:%02d:%02d %c%c %+d",
+ year, day, hour, minute, second, leapchar, dst, dut);
+ strcat(ptr, cptr);
+
+ /*
+ * Specific variable-format fields
+ */
+ sp = up->sptr;
+ sprintf(cptr, " %d %d %s %.0f %d %.1f %d", up->watch,
+ up->mitig[up->dchan].gain, sp->refid, wwv_metric(sp),
+ up->errbit, up->freq / SECOND * 1e6, up->avgint);
+ strcat(ptr, cptr);
+ return (strlen(ptr));
+}
+
+
+/*
+ * wwv_gain - adjust codec gain
+ *
+ * This routine is called at the end of each second. It counts the
+ * number of signal clips above the MAXSIG threshold during the previous
+ * second. If there are no clips, the gain is bumped up; if too many
+ * clips, it is bumped down. The decoder is relatively insensitive to
+ * amplitude, so this crudity works just fine. The input port is set and
+ * the error flag is cleared, mostly to be ornery.
+ */
+static void
+wwv_gain(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct refclockproc *pp;
+ struct wwvunit *up;
+
+ pp = peer->procptr;
+ up = (struct wwvunit *)pp->unitptr;
+
+ /*
+ * Apparently, the codec uses only the high order bits of the
+ * gain control field. Thus, it may take awhile for changes to
+ * wiggle the hardware bits.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > MAXGAIN)
+ up->gain = MAXGAIN;
+ } else if (up->clipcnt > MAXCLP) {
+ up->gain -= 4;
+ if (up->gain < 0)
+ up->gain = 0;
+ }
+ audio_gain(up->gain, up->mongain, up->port);
+ up->clipcnt = 0;
+#if DEBUG
+ if (debug > 1)
+ audio_show();
+#endif
+}
+
+
+#else
+int refclock_wwv_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_wwvb.c b/ntpd/refclock_wwvb.c
new file mode 100644
index 0000000..c5ef9f9
--- /dev/null
+++ b/ntpd/refclock_wwvb.c
@@ -0,0 +1,441 @@
+/*
+ * refclock_wwvb - clock driver for Spectracom WWVB receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_SPECTRACOM)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
+ * Synchronized Clocks and the Netclock/GPS Master Clock. Both the WWVB
+ * and GPS clocks have proven reliable sources of time; however, the
+ * WWVB clocks have proven vulnerable to high ambient conductive RF
+ * interference. The claimed accuracy of the WWVB clocks is 100 us
+ * relative to the broadcast signal, while the claimed accuracy of the
+ * GPS clock is 50 ns; however, in most cases the actual accuracy is
+ * limited by the resolution of the timecode and the latencies of the
+ * serial interface and operating system.
+ *
+ * The WWVB and GPS clocks should be configured for 24-hour display,
+ * AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and
+ * baud rate 9600. If the clock is to used as the source for the IRIG
+ * Audio Decoder (refclock_irig.c in this distribution), it should be
+ * configured for AM IRIG output and IRIG format 1 (IRIG B with
+ * signature control). The GPS clock can be configured either to respond
+ * to a 'T' poll character or left running continuously.
+ *
+ * There are two timecode formats used by these clocks. Format 0, which
+ * is available with both the Netclock/2 and 8170, and format 2, which
+ * is available only with the Netclock/2, specially modified 8170 and
+ * GPS.
+ *
+ * Format 0 (22 ASCII printing characters):
+ *
+ * <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf>
+ *
+ * on-time = first <cr>
+ * hh:mm:ss = hours, minutes, seconds
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ *
+ * The alarm condition is indicated by other than ' ' at a, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours.
+ *
+ * Format 2 (24 ASCII printing characters):
+ *
+ * <cr><lf>iqyy ddd hh:mm:ss.fff ld
+ *
+ * on-time = <cr>
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ * q = quality indicator (' ' = locked, 'A'...'D' = unlocked)
+ * yy = year (as broadcast)
+ * ddd = day of year
+ * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ *
+ * The alarm condition is indicated by other than ' ' at a, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours. The unlock condition is indicated by other than ' '
+ * at q.
+ *
+ * The q is normally ' ' when the time error is less than 1 ms and a
+ * character in the set 'A'...'D' when the time error is less than 10,
+ * 100, 500 and greater than 500 ms respectively. The l is normally ' ',
+ * but is set to 'L' early in the month of an upcoming UTC leap second
+ * and reset to ' ' on the first day of the following month. The d is
+ * set to 'S' for standard time 'I' on the day preceding a switch to
+ * daylight time, 'D' for daylight time and 'O' on the day preceding a
+ * switch to standard time. The start bit of the first <cr> is
+ * synchronized to the indicated time as returned.
+ *
+ * This driver does not need to be told which format is in use - it
+ * figures out which one from the length of the message.The driver makes
+ * no attempt to correct for the intrinsic jitter of the radio itself,
+ * which is a known problem with the older radios.
+ *
+ * Fudge Factors
+ *
+ * This driver can retrieve a table of quality data maintained
+ * internally by the Netclock/2 clock. If flag4 of the fudge
+ * configuration command is set to 1, the driver will retrieve this
+ * table and write it to the clockstats file on when the first timecode
+ * message of a new day is received.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwvb%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "WWVB" /* reference ID */
+#define DESCRIPTION "Spectracom WWVB/GPS Receivers" /* WRU */
+
+#define LENWWVB0 22 /* format 0 timecode length */
+#define LENWWVB1 22 /* format 1 timecode length */
+#define LENWWVB2 24 /* format 2 timecode length */
+#define LENWWVB3 29 /* format 3 timecode length */
+#define MONLIN 15 /* number of monitoring lines */
+
+/*
+ * WWVB unit control structure
+ */
+struct wwvbunit {
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ u_char linect; /* count ignored lines (for monitor */
+};
+
+/*
+ * Function prototypes
+ */
+static int wwvb_start P((int, struct peer *));
+static void wwvb_shutdown P((int, struct peer *));
+static void wwvb_receive P((struct recvbuf *));
+static void wwvb_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_wwvb = {
+ wwvb_start, /* start up driver */
+ wwvb_shutdown, /* shut down driver */
+ wwvb_poll, /* transmit poll message */
+ noentry, /* not used (old wwvb_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old wwvb_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * wwvb_start - open the devices and initialize data for processing
+ */
+static int
+wwvb_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct wwvbunit *)
+ emalloc(sizeof(struct wwvbunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct wwvbunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+ pp->io.clock_recv = wwvb_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->burst = MAXSTAGE;
+ return (1);
+}
+
+
+/*
+ * wwvb_shutdown - shut down the clock
+ */
+static void
+wwvb_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * wwvb_receive - receive data from the serial interface
+ */
+static void
+wwvb_receive(
+ struct recvbuf *rbufp
+ )
+{
+ struct wwvbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ l_fp trtmp; /* arrival timestamp */
+ int tz; /* time zone */
+ int day, month; /* ddd conversion */
+ int temp; /* int temp */
+ char syncchar; /* synchronization indicator */
+ char qualchar; /* quality indicator */
+ char leapchar; /* leap indicator */
+ char dstchar; /* daylight/standard indicator */
+ char tmpchar; /* trashbin */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained. Note: in format 0 on
+ * a Netclock/2 or upgraded 8170 the start bit is delayed 100
+ * +-50 us relative to the pps; however, on an unmodified 8170
+ * the start bit can be delayed up to 10 ms. In format 2 the
+ * reading precision is only to the millisecond. Thus, unless
+ * you have a pps gadget and don't have to have the year, format
+ * 0 provides the lowest jitter.
+ */
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code uses the timecode length to determine
+ * format 0, 2 or 3. If the timecode has invalid length or is
+ * not in proper format, we declare bad format and exit.
+ */
+ syncchar = qualchar = leapchar = dstchar = ' ';
+ tz = 0;
+ switch (pp->lencode) {
+
+ case LENWWVB0:
+
+ /*
+ * Timecode format 0: "I ddd hh:mm:ss DTZ=nn"
+ */
+ if (sscanf(pp->a_lastcode,
+ "%c %3d %2d:%2d:%2d%c%cTZ=%2d",
+ &syncchar, &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &tmpchar, &dstchar, &tz) == 8)
+ pp->nsec = 0;
+ break;
+
+ case LENWWVB2:
+
+ /*
+ * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */
+ if (sscanf(pp->a_lastcode,
+ "%c%c %2d %3d %2d:%2d:%2d.%3ld %c",
+ &syncchar, &qualchar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->nsec,
+ &leapchar) == 9)
+ pp->nsec *= 1000000;
+ break;
+
+ case LENWWVB3:
+
+ /*
+ * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#"
+ */
+ if (sscanf(pp->a_lastcode,
+ "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c",
+ &syncchar, &pp->year, &month, &day, &pp->hour,
+ &pp->minute, &pp->second, &dstchar, &leapchar) == 8)
+ {
+ pp->day = ymd2yd(pp->year, month, day);
+ pp->nsec = 0;
+ break;
+ }
+
+ default:
+
+ /*
+ * Unknown format: If dumping internal table, record
+ * stats; otherwise, declare bad format.
+ */
+ if (up->linect > 0) {
+ up->linect--;
+ record_clock_stats(&peer->srcadr,
+ pp->a_lastcode);
+ } else {
+ refclock_report(peer, CEVNT_BADREPLY);
+ }
+ return;
+ }
+
+ /*
+ * Decode synchronization, quality and leap characters. If
+ * unsynchronized, set the leap bits accordingly and exit.
+ * Otherwise, set the leap bits according to the leap character.
+ * Once synchronized, the dispersion depends only on the
+ * quality character.
+ */
+ switch (qualchar) {
+
+ case ' ':
+ pp->disp = .001;
+ pp->lastref = pp->lastrec;
+ break;
+
+ case 'A':
+ pp->disp = .01;
+ break;
+
+ case 'B':
+ pp->disp = .1;
+ break;
+
+ case 'C':
+ pp->disp = .5;
+ break;
+
+ case 'D':
+ pp->disp = MAXDISPERSE;
+ break;
+
+ default:
+ pp->disp = MAXDISPERSE;
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ if (syncchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else if (leapchar == 'L')
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = LEAP_NOWARNING;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * timecode timestamp.
+ */
+ if (!refclock_process(pp))
+ refclock_report(peer, CEVNT_BADTIME);
+}
+
+
+/*
+ * wwvb_poll - called by the transmit procedure
+ */
+static void
+wwvb_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ char pollchar; /* character sent to clock */
+
+ /*
+ * Time to poll the clock. The Spectracom clock responds to a
+ * 'T' by returning a timecode in the format(s) specified above.
+ * Note there is no checking on state, since this may not be the
+ * only customer reading the clock. Only one customer need poll
+ * the clock; all others just listen in. If the clock becomes
+ * unreachable, declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ if (up->linect > 0)
+ pollchar = 'R';
+ else
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+ refclock_receive(peer);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("wwvb: timecode %d %s\n", pp->lencode,
+ pp->a_lastcode);
+#endif
+ peer->burst = MAXSTAGE;
+ pp->polls++;
+
+ /*
+ * If the monitor flag is set (flag4), we dump the internal
+ * quality table at the first timecode beginning the day.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
+ (int)up->lasthour)
+ up->linect = MONLIN;
+ up->lasthour = pp->hour;
+}
+
+#else
+int refclock_wwvb_bs;
+#endif /* REFCLOCK */
diff --git a/ntpd/refclock_zyfer.c b/ntpd/refclock_zyfer.c
new file mode 100644
index 0000000..44f2c4d
--- /dev/null
+++ b/ntpd/refclock_zyfer.c
@@ -0,0 +1,346 @@
+/*
+ * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
+ *
+ * Harlan Stenn, Jan 2002
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ZYFER)
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+
+/*
+ * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
+ * This clock also provides PPS as well as IRIG outputs.
+ * Precision is limited by the serial driver, etc.
+ *
+ * If I was really brave I'd hack/generalize the serial driver to deal
+ * with arbitrary on-time characters. This clock *begins* the stream with
+ * `!`, the on-time character, and the string is *not* EOL-terminated.
+ *
+ * Configure the beast for 9600, 8N1. While I see leap-second stuff
+ * in the documentation, the published specs on the TOD format only show
+ * the seconds going to '59'. I see no leap warning in the TOD format.
+ *
+ * The clock sends the following message once per second:
+ *
+ * !TIME,2002,017,07,59,32,2,4,1
+ * YYYY DDD HH MM SS m T O
+ *
+ * ! On-time character
+ * YYYY Year
+ * DDD 001-366 Day of Year
+ * HH 00-23 Hour
+ * MM 00-59 Minute
+ * SS 00-59 Second (probably 00-60)
+ * m 1-5 Time Mode:
+ * 1 = GPS time
+ * 2 = UTC time
+ * 3 = LGPS time (Local GPS)
+ * 4 = LUTC time (Local UTC)
+ * 5 = Manual time
+ * T 4-9 Time Figure Of Merit:
+ * 4 x <= 1us
+ * 5 1us < x <= 10 us
+ * 6 10us < x <= 100us
+ * 7 100us < x <= 1ms
+ * 8 1ms < x <= 10ms
+ * 9 10ms < x
+ * O 0-4 Operation Mode:
+ * 0 Warm-up
+ * 1 Time Locked
+ * 2 Coasting
+ * 3 Recovering
+ * 4 Manual
+ *
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/zyfer%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "Zyfer GPStarplus" /* WRU */
+
+#define LENZYFER 29 /* timecode length */
+
+/*
+ * Unit control structure
+ */
+struct zyferunit {
+ u_char Rcvbuf[LENZYFER + 1];
+ u_char polled; /* poll message flag */
+ int pollcnt;
+ l_fp tstamp; /* timestamp of last poll */
+ int Rcvptr;
+};
+
+/*
+ * Function prototypes
+ */
+static int zyfer_start P((int, struct peer *));
+static void zyfer_shutdown P((int, struct peer *));
+static void zyfer_receive P((struct recvbuf *));
+static void zyfer_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_zyfer = {
+ zyfer_start, /* start up driver */
+ zyfer_shutdown, /* shut down driver */
+ zyfer_poll, /* transmit poll message */
+ noentry, /* not used (old zyfer_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old zyfer_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * zyfer_start - open the devices and initialize data for processing
+ */
+static int
+zyfer_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port.
+ * Something like LDISC_ACTS that looked for ! would be nice...
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) )
+ return (0);
+
+ msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct zyferunit *)
+ emalloc(sizeof(struct zyferunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct zyferunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = zyfer_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ up->polled = 0; /* May not be needed... */
+
+ return (1);
+}
+
+
+/*
+ * zyfer_shutdown - shut down the clock
+ */
+static void
+zyfer_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct zyferunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * zyfer_receive - receive data from the serial interface
+ */
+static void
+zyfer_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ int tmode; /* Time mode */
+ int tfom; /* Time Figure Of Merit */
+ int omode; /* Operation mode */
+ u_char *p;
+#ifdef PPS
+ struct ppsclockev ppsev;
+ int request;
+#ifdef HAVE_CIOGETEV
+ request = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ request = TIOCGPPSEV;
+#endif
+#endif /* PPS */
+
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct zyferunit *)pp->unitptr;
+ p = (u_char *) &rbufp->recv_space;
+ /*
+ * If lencode is 0:
+ * - if *rbufp->recv_space is !
+ * - - call refclock_gtlin to get things going
+ * - else flush
+ * else stuff it on the end of lastcode
+ * If we don't have LENZYFER bytes
+ * - wait for more data
+ * Crack the beast, and if it's OK, process it.
+ *
+ * We use refclock_gtlin() because we might use LDISC_CLK.
+ *
+ * Under FreeBSD, we get the ! followed by two 14-byte packets.
+ */
+
+ if (pp->lencode >= LENZYFER)
+ pp->lencode = 0;
+
+ if (!pp->lencode) {
+ if (*p == '!')
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
+ BMAX, &pp->lastrec);
+ else
+ return;
+ } else {
+ memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
+ pp->lencode += rbufp->recv_length;
+ pp->a_lastcode[pp->lencode] = '\0';
+ }
+
+ if (pp->lencode < LENZYFER)
+ return;
+
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+
+ if (pp->lencode != LENZYFER) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
+ */
+ if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
+ &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
+ &tmode, &tfom, &omode) != 8) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ if (tmode != 2) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /* Should we make sure tfom is 4? */
+
+ if (omode != 1) {
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+#ifdef PPS
+ if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
+ ppsev.tv.tv_sec += (u_int32) JAN_1970;
+ TVTOTS(&ppsev.tv,&up->tstamp);
+ }
+ /* record the last ppsclock event time stamp */
+ pp->lastrec = up->tstamp;
+#endif /* PPS */
+ if (!refclock_process(pp)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Good place for record_clock_stats()
+ */
+ up->pollcnt = 2;
+
+ if (up->polled) {
+ up->polled = 0;
+ refclock_receive(peer);
+ }
+}
+
+
+/*
+ * zyfer_poll - called by the transmit procedure
+ */
+static void
+zyfer_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct zyferunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * We don't really do anything here, except arm the receiving
+ * side to capture a sample and check for timeouts.
+ */
+ pp = peer->procptr;
+ up = (struct zyferunit *)pp->unitptr;
+ if (!up->pollcnt)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ pp->polls++;
+ up->polled = 1;
+}
+
+#else
+int refclock_zyfer_bs;
+#endif /* REFCLOCK */
OpenPOWER on IntegriCloud