summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/ntpd
diff options
context:
space:
mode:
authorroberto <roberto@FreeBSD.org>1999-12-09 13:01:21 +0000
committerroberto <roberto@FreeBSD.org>1999-12-09 13:01:21 +0000
commitef64b99e8412f2273dd2e8b3291c2f78ffc4667f (patch)
treefc0cfa1aab0ff6b228f511b410733ef4f35d1ead /contrib/ntp/ntpd
downloadFreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.zip
FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.tar.gz
Virgin import of ntpd 4.0.98f
Diffstat (limited to 'contrib/ntp/ntpd')
-rw-r--r--contrib/ntp/ntpd/Makefile.am45
-rw-r--r--contrib/ntp/ntpd/Makefile.in907
-rw-r--r--contrib/ntp/ntpd/check_y2k.c624
-rw-r--r--contrib/ntp/ntpd/jupiter.h255
-rw-r--r--contrib/ntp/ntpd/map_vme.c135
-rw-r--r--contrib/ntp/ntpd/ntp_config.c2405
-rw-r--r--contrib/ntp/ntpd/ntp_control.c2652
-rw-r--r--contrib/ntp/ntpd/ntp_filegen.c547
-rw-r--r--contrib/ntp/ntpd/ntp_intres.c970
-rw-r--r--contrib/ntp/ntpd/ntp_io.c1754
-rw-r--r--contrib/ntp/ntpd/ntp_loopfilter.c847
-rw-r--r--contrib/ntp/ntpd/ntp_monitor.c354
-rw-r--r--contrib/ntp/ntpd/ntp_peer.c795
-rw-r--r--contrib/ntp/ntpd/ntp_proto.c2165
-rw-r--r--contrib/ntp/ntpd/ntp_refclock.c1322
-rw-r--r--contrib/ntp/ntpd/ntp_request.c2334
-rw-r--r--contrib/ntp/ntpd/ntp_restrict.c460
-rw-r--r--contrib/ntp/ntpd/ntp_timer.c308
-rw-r--r--contrib/ntp/ntpd/ntp_util.c640
-rw-r--r--contrib/ntp/ntpd/ntpd.c1121
-rw-r--r--contrib/ntp/ntpd/refclock_acts.c981
-rw-r--r--contrib/ntp/ntpd/refclock_arbiter.c429
-rw-r--r--contrib/ntp/ntpd/refclock_arc.c1323
-rw-r--r--contrib/ntp/ntpd/refclock_as2201.c388
-rw-r--r--contrib/ntp/ntpd/refclock_atom.c488
-rw-r--r--contrib/ntp/ntpd/refclock_bancomm.c483
-rw-r--r--contrib/ntp/ntpd/refclock_chronolog.c343
-rw-r--r--contrib/ntp/ntpd/refclock_chu.c1464
-rw-r--r--contrib/ntp/ntpd/refclock_conf.c262
-rw-r--r--contrib/ntp/ntpd/refclock_datum.c873
-rw-r--r--contrib/ntp/ntpd/refclock_dumbclock.c381
-rw-r--r--contrib/ntp/ntpd/refclock_gpsvme.c613
-rw-r--r--contrib/ntp/ntpd/refclock_heath.c487
-rw-r--r--contrib/ntp/ntpd/refclock_hpgps.c603
-rw-r--r--contrib/ntp/ntpd/refclock_irig.c1079
-rw-r--r--contrib/ntp/ntpd/refclock_jupiter.c1262
-rw-r--r--contrib/ntp/ntpd/refclock_leitch.c619
-rw-r--r--contrib/ntp/ntpd/refclock_local.c258
-rw-r--r--contrib/ntp/ntpd/refclock_msfees.c1444
-rw-r--r--contrib/ntp/ntpd/refclock_mx4200.c1774
-rw-r--r--contrib/ntp/ntpd/refclock_nmea.c411
-rw-r--r--contrib/ntp/ntpd/refclock_oncore.c1660
-rw-r--r--contrib/ntp/ntpd/refclock_palisade.c880
-rw-r--r--contrib/ntp/ntpd/refclock_palisade.h167
-rw-r--r--contrib/ntp/ntpd/refclock_parse.c5340
-rw-r--r--contrib/ntp/ntpd/refclock_pst.c318
-rw-r--r--contrib/ntp/ntpd/refclock_ptbacts.c16
-rw-r--r--contrib/ntp/ntpd/refclock_shm.c320
-rw-r--r--contrib/ntp/ntpd/refclock_tpro.c209
-rw-r--r--contrib/ntp/ntpd/refclock_trak.c361
-rw-r--r--contrib/ntp/ntpd/refclock_true.c852
-rw-r--r--contrib/ntp/ntpd/refclock_ulink.c319
-rw-r--r--contrib/ntp/ntpd/refclock_usno.c674
-rw-r--r--contrib/ntp/ntpd/refclock_wwvb.c440
54 files changed, 48161 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/Makefile.am b/contrib/ntp/ntpd/Makefile.am
new file mode 100644
index 0000000..9926684
--- /dev/null
+++ b/contrib/ntp/ntpd/Makefile.am
@@ -0,0 +1,45 @@
+#AUTOMAKE_OPTIONS = ../util/ansi2knr no-dependencies
+AUTOMAKE_OPTIONS = ../util/ansi2knr
+bin_PROGRAMS = ntpd
+INCLUDES = -I$(top_srcdir)/include
+# LDADD might need RESLIB and ADJLIB
+LDADD = version.o @LIBPARSE@ ../libntp/libntp.a @LIBRSAREF@
+ntpd_LDADD = $(LDADD) -lm
+DISTCLEANFILES = .version version.c
+#EXTRA_DIST = ntpd.mak
+ETAGS_ARGS = Makefile.am
+### Y2Kfixes
+check_PROGRAMS = @MAKE_CHECK_Y2K@
+EXTRA_PROGRAMS = check_y2k
+
+check-local: @MAKE_CHECK_Y2K@
+ [ -z "@MAKE_CHECK_Y2K@" ] || ./@MAKE_CHECK_Y2K@
+
+ntpd_SOURCES = map_vme.c ntp_config.c ntp_control.c ntp_io.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 ntp_intres.c ntp_filegen.c ntpd.c \
+ refclock_conf.c refclock_chu.c refclock_local.c \
+ refclock_pst.c refclock_wwvb.c refclock_mx4200.c \
+ refclock_parse.c refclock_as2201.c refclock_bancomm.c \
+ refclock_tpro.c refclock_leitch.c refclock_irig.c \
+ refclock_msfees.c refclock_trak.c refclock_datum.c \
+ refclock_acts.c refclock_heath.c refclock_nmea.c \
+ refclock_atom.c refclock_ptbacts.c refclock_jupiter.c \
+ refclock_usno.c refclock_true.c refclock_hpgps.c \
+ refclock_shm.c refclock_gpsvme.c refclock_arbiter.c \
+ refclock_arc.c refclock_palisade.c refclock_palisade.h \
+ refclock_oncore.c refclock_chronolog.c refclock_dumbclock.c \
+ refclock_ulink.c jupiter.h
+
+$(PROGRAMS): $(LDADD)
+
+../libntp/libntp.a:
+ cd ../libntp && $(MAKE)
+
+../libparse/libparse.a:
+ cd ../libparse && $(MAKE)
+
+version.o: $(ntpd_OBJECTS) ../libntp/libntp.a @LIBPARSE@ @LIBRSAREF@ Makefile
+ $(top_builddir)/scripts/mkver ntpd
+ $(COMPILE) -c version.c
diff --git a/contrib/ntp/ntpd/Makefile.in b/contrib/ntp/ntpd/Makefile.in
new file mode 100644
index 0000000..ac5e591
--- /dev/null
+++ b/contrib/ntp/ntpd/Makefile.in
@@ -0,0 +1,907 @@
+# Makefile.in generated automatically by automake 1.4a from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 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.
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_FLAG =
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_alias = @build_alias@
+build_triplet = @build@
+host_alias = @host_alias@
+host_triplet = @host@
+target_alias = @target_alias@
+target_triplet = @target@
+AMTAR = @AMTAR@
+AMTARFLAGS = @AMTARFLAGS@
+AWK = @AWK@
+CC = @CC@
+CFLAGS = @CFLAGS@
+CHUTEST = @CHUTEST@
+CLKTEST = @CLKTEST@
+CPP = @CPP@
+DCFD = @DCFD@
+LDFLAGS = @LDFLAGS@
+LIBPARSE = @LIBPARSE@
+LIBRSAREF = @LIBRSAREF@
+LN_S = @LN_S@
+MAKEINFO = @MAKEINFO@
+MAKE_ADJTIMED = @MAKE_ADJTIMED@
+MAKE_CHECK_Y2K = @MAKE_CHECK_Y2K@
+MAKE_LIBPARSE = @MAKE_LIBPARSE@
+MAKE_LIBPARSE_KERNEL = @MAKE_LIBPARSE_KERNEL@
+MAKE_LIBRSAREF = @MAKE_LIBRSAREF@
+MAKE_NTPTIME = @MAKE_NTPTIME@
+MAKE_PARSEKMODULE = @MAKE_PARSEKMODULE@
+MAKE_TICKADJ = @MAKE_TICKADJ@
+PACKAGE = @PACKAGE@
+PATH_SH = @PATH_SH@
+PROPDELAY = @PROPDELAY@
+RANLIB = @RANLIB@
+RSAREF = @RSAREF@
+TESTDCF = @TESTDCF@
+U = @U@
+VERSION = @VERSION@
+
+#AUTOMAKE_OPTIONS = ../util/ansi2knr no-dependencies
+
+
+AUTOMAKE_OPTIONS = ../util/ansi2knr
+bin_PROGRAMS = ntpd
+INCLUDES = -I$(top_srcdir)/include
+# LDADD might need RESLIB and ADJLIB
+LDADD = version.o @LIBPARSE@ ../libntp/libntp.a @LIBRSAREF@
+ntpd_LDADD = $(LDADD) -lm
+DISTCLEANFILES = .version version.c
+#EXTRA_DIST = ntpd.mak
+ETAGS_ARGS = Makefile.am
+### Y2Kfixes
+check_PROGRAMS = @MAKE_CHECK_Y2K@
+EXTRA_PROGRAMS = check_y2k
+
+ntpd_SOURCES = map_vme.c ntp_config.c ntp_control.c ntp_io.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 ntp_intres.c ntp_filegen.c ntpd.c \
+ refclock_conf.c refclock_chu.c refclock_local.c \
+ refclock_pst.c refclock_wwvb.c refclock_mx4200.c \
+ refclock_parse.c refclock_as2201.c refclock_bancomm.c \
+ refclock_tpro.c refclock_leitch.c refclock_irig.c \
+ refclock_msfees.c refclock_trak.c refclock_datum.c \
+ refclock_acts.c refclock_heath.c refclock_nmea.c \
+ refclock_atom.c refclock_ptbacts.c refclock_jupiter.c \
+ refclock_usno.c refclock_true.c refclock_hpgps.c \
+ refclock_shm.c refclock_gpsvme.c refclock_arbiter.c \
+ refclock_arc.c refclock_palisade.c refclock_palisade.h \
+ refclock_oncore.c refclock_chronolog.c refclock_dumbclock.c \
+ refclock_ulink.c jupiter.h
+
+subdir = ntpd
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(bin_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) -I..
+CPPFLAGS = @CPPFLAGS@
+LIBS = @LIBS@
+ANSI2KNR = ../util/ansi2knr
+check_y2k_SOURCES = check_y2k.c
+check_y2k_OBJECTS = check_y2k$U.o
+check_y2k_LDADD = $(LDADD)
+check_y2k_DEPENDENCIES = version.o ../libntp/libntp.a
+check_y2k_LDFLAGS =
+am_ntpd_OBJECTS = map_vme$U.o ntp_config$U.o ntp_control$U.o ntp_io$U.o \
+ntp_loopfilter$U.o ntp_monitor$U.o ntp_peer$U.o ntp_proto$U.o \
+ntp_refclock$U.o ntp_request$U.o ntp_restrict$U.o ntp_timer$U.o \
+ntp_util$U.o ntp_intres$U.o ntp_filegen$U.o ntpd$U.o refclock_conf$U.o \
+refclock_chu$U.o refclock_local$U.o refclock_pst$U.o refclock_wwvb$U.o \
+refclock_mx4200$U.o refclock_parse$U.o refclock_as2201$U.o \
+refclock_bancomm$U.o refclock_tpro$U.o refclock_leitch$U.o \
+refclock_irig$U.o refclock_msfees$U.o refclock_trak$U.o \
+refclock_datum$U.o refclock_acts$U.o refclock_heath$U.o \
+refclock_nmea$U.o refclock_atom$U.o refclock_ptbacts$U.o \
+refclock_jupiter$U.o refclock_usno$U.o refclock_true$U.o \
+refclock_hpgps$U.o refclock_shm$U.o refclock_gpsvme$U.o \
+refclock_arbiter$U.o refclock_arc$U.o refclock_palisade$U.o \
+refclock_oncore$U.o refclock_chronolog$U.o refclock_dumbclock$U.o \
+refclock_ulink$U.o
+ntpd_OBJECTS = $(am_ntpd_OBJECTS)
+ntpd_DEPENDENCIES = version.o ../libntp/libntp.a
+ntpd_LDFLAGS =
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+DIST_SOURCES = check_y2k.c $(ntpd_SOURCES)
+DIST_COMMON = Makefile.am Makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+GZIP_ENV = --best
+SOURCES = check_y2k.c $(ntpd_SOURCES)
+OBJECTS = check_y2k$U.o $(am_ntpd_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .c .o
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps ntpd/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-binPROGRAMS:
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+distclean-binPROGRAMS:
+
+maintainer-clean-binPROGRAMS:
+
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ if test -f $$p; then \
+ f="`echo $$p|sed -e 's/$(EXEEXT)$$//' -e '$(transform)' -e 's/$$/$(EXEEXT)/'`"; \
+ echo " $(INSTALL_PROGRAM) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(bindir)/$$f"; \
+ $(INSTALL_PROGRAM) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(bindir)/$$f; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f="`echo $$p|sed -e 's/$(EXEEXT)$$//' -e '$(transform)' -e 's/$$/$(EXEEXT)/'`"; \
+ echo " rm -f $(DESTDIR)$(bindir)/$$f"; \
+ rm -f $(DESTDIR)$(bindir)/$$f; \
+ done
+
+mostlyclean-checkPROGRAMS:
+
+clean-checkPROGRAMS:
+ -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS)
+
+distclean-checkPROGRAMS:
+
+maintainer-clean-checkPROGRAMS:
+
+.c.o:
+ $(COMPILE) -c $<
+
+mostlyclean-compile:
+ -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+ -rm -f *.tab.c
+
+maintainer-clean-compile:
+../util/ansi2knr: ../util/ansi2knr.o
+ cd ../util && $(MAKE) $(AM_MAKEFLAGS) ansi2knr
+
+../util/ansi2knr.o:
+ cd ../util && $(MAKE) $(AM_MAKEFLAGS) ansi2knr.o
+
+
+mostlyclean-kr:
+ -rm -f *_.c
+
+clean-kr:
+
+distclean-kr:
+
+maintainer-clean-kr:
+check_y2k$U.o:
+
+check_y2k: $(check_y2k_OBJECTS) $(check_y2k_DEPENDENCIES)
+ @rm -f check_y2k
+ $(LINK) $(check_y2k_LDFLAGS) $(check_y2k_OBJECTS) $(check_y2k_LDADD) $(LIBS)
+map_vme$U.o:
+ntp_config$U.o:
+ntp_control$U.o:
+ntp_io$U.o:
+ntp_loopfilter$U.o:
+ntp_monitor$U.o:
+ntp_peer$U.o:
+ntp_proto$U.o:
+ntp_refclock$U.o:
+ntp_request$U.o:
+ntp_restrict$U.o:
+ntp_timer$U.o:
+ntp_util$U.o:
+ntp_intres$U.o:
+ntp_filegen$U.o:
+ntpd$U.o:
+refclock_conf$U.o:
+refclock_chu$U.o:
+refclock_local$U.o:
+refclock_pst$U.o:
+refclock_wwvb$U.o:
+refclock_mx4200$U.o:
+refclock_parse$U.o:
+refclock_as2201$U.o:
+refclock_bancomm$U.o:
+refclock_tpro$U.o:
+refclock_leitch$U.o:
+refclock_irig$U.o:
+refclock_msfees$U.o:
+refclock_trak$U.o:
+refclock_datum$U.o:
+refclock_acts$U.o:
+refclock_heath$U.o:
+refclock_nmea$U.o:
+refclock_atom$U.o:
+refclock_ptbacts$U.o:
+refclock_jupiter$U.o:
+refclock_usno$U.o:
+refclock_true$U.o:
+refclock_hpgps$U.o:
+refclock_shm$U.o:
+refclock_gpsvme$U.o:
+refclock_arbiter$U.o:
+refclock_arc$U.o:
+refclock_palisade$U.o:
+refclock_oncore$U.o:
+refclock_chronolog$U.o:
+refclock_dumbclock$U.o:
+refclock_ulink$U.o:
+
+ntpd: $(ntpd_OBJECTS) $(ntpd_DEPENDENCIES)
+ @rm -f ntpd
+ $(LINK) $(ntpd_LDFLAGS) $(ntpd_OBJECTS) $(ntpd_LDADD) $(LIBS)
+check_y2k_.c: check_y2k.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > check_y2k_.c
+map_vme_.c: map_vme.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > map_vme_.c
+ntp_config_.c: ntp_config.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_config_.c
+ntp_control_.c: ntp_control.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_control_.c
+ntp_filegen_.c: ntp_filegen.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_filegen_.c
+ntp_intres_.c: ntp_intres.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_intres_.c
+ntp_io_.c: ntp_io.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_io_.c
+ntp_loopfilter_.c: ntp_loopfilter.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_loopfilter_.c
+ntp_monitor_.c: ntp_monitor.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_monitor_.c
+ntp_peer_.c: ntp_peer.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_peer_.c
+ntp_proto_.c: ntp_proto.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_proto_.c
+ntp_refclock_.c: ntp_refclock.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_refclock_.c
+ntp_request_.c: ntp_request.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_request_.c
+ntp_restrict_.c: ntp_restrict.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_restrict_.c
+ntp_timer_.c: ntp_timer.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_timer_.c
+ntp_util_.c: ntp_util.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntp_util_.c
+ntpd_.c: ntpd.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > ntpd_.c
+refclock_acts_.c: refclock_acts.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_acts_.c
+refclock_arbiter_.c: refclock_arbiter.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_arbiter_.c
+refclock_arc_.c: refclock_arc.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_arc_.c
+refclock_as2201_.c: refclock_as2201.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_as2201_.c
+refclock_atom_.c: refclock_atom.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_atom_.c
+refclock_bancomm_.c: refclock_bancomm.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_bancomm_.c
+refclock_chronolog_.c: refclock_chronolog.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_chronolog_.c
+refclock_chu_.c: refclock_chu.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_chu_.c
+refclock_conf_.c: refclock_conf.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_conf_.c
+refclock_datum_.c: refclock_datum.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_datum_.c
+refclock_dumbclock_.c: refclock_dumbclock.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_dumbclock_.c
+refclock_gpsvme_.c: refclock_gpsvme.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_gpsvme_.c
+refclock_heath_.c: refclock_heath.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_heath_.c
+refclock_hpgps_.c: refclock_hpgps.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_hpgps_.c
+refclock_irig_.c: refclock_irig.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_irig_.c
+refclock_jupiter_.c: refclock_jupiter.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_jupiter_.c
+refclock_leitch_.c: refclock_leitch.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_leitch_.c
+refclock_local_.c: refclock_local.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_local_.c
+refclock_msfees_.c: refclock_msfees.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_msfees_.c
+refclock_mx4200_.c: refclock_mx4200.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_mx4200_.c
+refclock_nmea_.c: refclock_nmea.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_nmea_.c
+refclock_oncore_.c: refclock_oncore.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_oncore_.c
+refclock_palisade_.c: refclock_palisade.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_palisade_.c
+refclock_parse_.c: refclock_parse.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_parse_.c
+refclock_pst_.c: refclock_pst.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_pst_.c
+refclock_ptbacts_.c: refclock_ptbacts.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_ptbacts_.c
+refclock_shm_.c: refclock_shm.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_shm_.c
+refclock_tpro_.c: refclock_tpro.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_tpro_.c
+refclock_trak_.c: refclock_trak.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_trak_.c
+refclock_true_.c: refclock_true.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_true_.c
+refclock_ulink_.c: refclock_ulink.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_ulink_.c
+refclock_usno_.c: refclock_usno.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_usno_.c
+refclock_wwvb_.c: refclock_wwvb.c $(ANSI2KNR)
+ $(CPP) $(DEFS) $(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) > refclock_wwvb_.c
+check_y2k_.o map_vme_.o ntp_config_.o ntp_control_.o ntp_filegen_.o \
+ntp_intres_.o ntp_io_.o ntp_loopfilter_.o ntp_monitor_.o ntp_peer_.o \
+ntp_proto_.o ntp_refclock_.o ntp_request_.o ntp_restrict_.o \
+ntp_timer_.o ntp_util_.o ntpd_.o refclock_acts_.o refclock_arbiter_.o \
+refclock_arc_.o refclock_as2201_.o refclock_atom_.o refclock_bancomm_.o \
+refclock_chronolog_.o refclock_chu_.o refclock_conf_.o \
+refclock_datum_.o refclock_dumbclock_.o refclock_gpsvme_.o \
+refclock_heath_.o refclock_hpgps_.o refclock_irig_.o \
+refclock_jupiter_.o refclock_leitch_.o refclock_local_.o \
+refclock_msfees_.o refclock_mx4200_.o refclock_nmea_.o \
+refclock_oncore_.o refclock_palisade_.o refclock_parse_.o \
+refclock_pst_.o refclock_ptbacts_.o refclock_shm_.o refclock_tpro_.o \
+refclock_trak_.o refclock_true_.o refclock_ulink_.o refclock_usno_.o \
+refclock_wwvb_.o : $(ANSI2KNR)
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ ${AWK:-awk} ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ ${AWK:-awk} ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+distdir: $(DISTFILES)
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$d/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+check_y2k.o: check_y2k.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_malloc.h \
+ ../include/ntp_refclock.h ../include/recvbuff.h \
+ ../include/ntp_calendar.h ../include/parse.h \
+ ../include/parse_conf.h
+map_vme.o: map_vme.c ../config.h
+ntp_config.o: ntp_config.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_filegen.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+ntp_control.o: ntp_control.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_control.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+ntp_filegen.o: ntp_filegen.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_string.h ../include/ntp_calendar.h \
+ ../include/ntp_filegen.h ../include/ntp_stdlib.h \
+ ../include/l_stdlib.h
+ntp_intres.o: ntp_intres.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_request.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+ntp_io.o: ntp_io.c ../config.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h ../include/iosignal.h \
+ ../include/ntp_if.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+ntp_loopfilter.o: ntp_loopfilter.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h \
+ ../include/ntp_syscall.h
+ntp_monitor.o: ntp_monitor.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h ../include/ntp_if.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+ntp_peer.o: ntp_peer.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+ntp_proto.o: ntp_proto.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h \
+ ../include/ntp_unixtime.h ../include/ntp_control.h
+ntp_refclock.o: ntp_refclock.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+ntp_request.o: ntp_request.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_request.h ../include/ntp_control.h \
+ ../include/ntp_if.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h \
+ ../include/ntp_syscall.h
+ntp_restrict.o: ntp_restrict.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_if.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+ntp_timer.o: ntp_timer.c ../config.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+ntp_util.o: ntp_util.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_filegen.h \
+ ../include/ntp_if.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+ntpd.o: ntpd.c ../config.h ../include/ntpd.h ../include/ntp_syslog.h \
+ ../include/ntp_fp.h ../include/ntp_types.h \
+ ../include/ntp_machine.h ../include/ntp_proto.h \
+ ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+refclock_acts.o: refclock_acts.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h \
+ ../include/ntp_control.h
+refclock_arbiter.o: refclock_arbiter.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+refclock_arc.o: refclock_arc.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+refclock_as2201.o: refclock_as2201.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_atom.o: refclock_atom.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_bancomm.o: refclock_bancomm.c ../config.h
+refclock_chronolog.o: refclock_chronolog.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_calendar.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_chu.o: refclock_chu.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_calendar.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_conf.o: refclock_conf.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_datum.o: refclock_datum.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_dumbclock.o: refclock_dumbclock.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_calendar.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_gpsvme.o: refclock_gpsvme.c ../config.h
+refclock_heath.o: refclock_heath.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+refclock_hpgps.o: refclock_hpgps.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+refclock_irig.o: refclock_irig.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_calendar.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_jupiter.o: refclock_jupiter.c ../config.h
+refclock_leitch.o: refclock_leitch.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_local.o: refclock_local.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h \
+ ../include/ntp_syscall.h
+refclock_msfees.o: refclock_msfees.c ../config.h
+refclock_mx4200.o: refclock_mx4200.c ../config.h
+refclock_nmea.o: refclock_nmea.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+refclock_oncore.o: refclock_oncore.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_palisade.o: refclock_palisade.c ../config.h refclock_palisade.h \
+ ../include/ntpd.h ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_control.h ../include/ntp_unixtime.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+refclock_parse.o: refclock_parse.c ../config.h
+refclock_pst.o: refclock_pst.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h
+refclock_ptbacts.o: refclock_ptbacts.c ../config.h refclock_acts.c \
+ ../include/ntpd.h ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h \
+ ../include/ntp_control.h
+refclock_shm.o: refclock_shm.c ../config.h
+refclock_tpro.o: refclock_tpro.c ../config.h
+refclock_trak.o: refclock_trak.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_stdlib.h ../include/ntp_string.h \
+ ../include/l_stdlib.h ../include/ntp_unixtime.h
+refclock_true.o: refclock_true.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_ulink.o: refclock_ulink.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_calendar.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+refclock_usno.o: refclock_usno.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_unixtime.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h \
+ ../include/ntp_control.h
+refclock_wwvb.o: refclock_wwvb.c ../config.h ../include/ntpd.h \
+ ../include/ntp_syslog.h ../include/ntp_fp.h \
+ ../include/ntp_types.h ../include/ntp_machine.h \
+ ../include/ntp_proto.h ../include/ntp.h ../include/ntp_select.h \
+ ../include/ntp_malloc.h ../include/ntp_refclock.h \
+ ../include/recvbuff.h ../include/ntp_io.h \
+ ../include/ntp_calendar.h ../include/ntp_stdlib.h \
+ ../include/ntp_string.h ../include/l_stdlib.h
+
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-local
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am: install-binPROGRAMS
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-binPROGRAMS
+uninstall: uninstall-am
+all-am: Makefile $(PROGRAMS)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_STRIP_FLAG=-s install
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-checkPROGRAMS \
+ mostlyclean-compile mostlyclean-kr mostlyclean-tags \
+ mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-compile clean-kr \
+ clean-tags clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-binPROGRAMS distclean-checkPROGRAMS \
+ distclean-compile distclean-kr distclean-tags \
+ distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-binPROGRAMS \
+ maintainer-clean-checkPROGRAMS maintainer-clean-compile \
+ maintainer-clean-kr maintainer-clean-tags \
+ maintainer-clean-generic distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
+maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
+mostlyclean-checkPROGRAMS distclean-checkPROGRAMS clean-checkPROGRAMS \
+maintainer-clean-checkPROGRAMS mostlyclean-compile distclean-compile \
+clean-compile maintainer-clean-compile mostlyclean-kr distclean-kr \
+clean-kr maintainer-clean-kr tags mostlyclean-tags distclean-tags \
+clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi \
+check-local check check-am installcheck-am installcheck install-exec-am \
+install-exec install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all install-strip \
+installdirs mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+check-local: @MAKE_CHECK_Y2K@
+ [ -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@ @LIBRSAREF@ Makefile
+ $(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/contrib/ntp/ntpd/check_y2k.c b/contrib/ntp/ntpd/check_y2k.c
new file mode 100644
index 0000000..3cc05fc
--- /dev/null
+++ b/contrib/ntp/ntpd/check_y2k.c
@@ -0,0 +1,624 @@
+/* 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 <sys/types.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 */
+# include <sys/signal.h>
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif /* HAVE_SYS_IOCTL_H */
+# include <sys/time.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 */
+
+#include "ntpd.h"
+
+/* } end definitions lifted from ntpd.c */
+
+#include "ntp_calendar.h"
+#include "parse.h"
+
+#define GoodLeap(Year) (((Year)%4 || (!((Year)%100) && (Year)%400)) ? 0 : 13 )
+
+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/contrib/ntp/ntpd/jupiter.h b/contrib/ntp/ntpd/jupiter.h
new file mode 100644
index 0000000..84b9a59
--- /dev/null
+++ b/contrib/ntp/ntpd/jupiter.h
@@ -0,0 +1,255 @@
+/* @(#) $Header: /cvs/ntp/ntpd/jupiter.h,v 1.1.1.1 1999/05/26 00:48:19 stenn Exp $ (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/contrib/ntp/ntpd/map_vme.c b/contrib/ntp/ntpd/map_vme.c
new file mode 100644
index 0000000..3c161cc
--- /dev/null
+++ b/contrib/ntp/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/contrib/ntp/ntpd/ntp_config.c b/contrib/ntp/ntpd/ntp_config.c
new file mode 100644
index 0000000..e21f5b0
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_config.c
@@ -0,0 +1,2405 @@
+/*
+ * ntp_config.c - read and apply configuration information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <signal.h>
+#ifndef SIGCHLD
+#define SIGCHLD SIGCLD
+#endif
+#if !defined(VMS)
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#endif /* VMS */
+#include <sys/time.h>
+
+#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"
+
+#ifdef SYS_WINNT
+#include <io.h>
+extern HANDLE ResolverThreadHandle;
+#endif /* SYS_WINNT */
+
+/*
+ * 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.
+ */
+
+/*
+ * Configuration file name
+ */
+#ifndef CONFIG_FILE
+# ifndef SYS_WINNT
+# define CONFIG_FILE "/etc/ntp.conf"
+# else /* SYS_WINNT */
+# define CONFIG_FILE "%windir%\\system32\\drivers\\etc\\ntp.conf"
+# define ALT_CONFIG_FILE "%windir%\\ntp.conf"
+# endif /* SYS_WINNT */
+#endif /* not CONFIG_FILE */
+
+/*
+ * We understand the following configuration entries and defaults.
+ *
+ * peer [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
+ * server [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
+ * broadcast [ addr ] [ version 3 ] [ key 0 ] [ ttl 1 ]
+ * broadcastclient
+ * multicastclient [ 224.0.1.1 ]
+ * manycastclient [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
+ * manycastserver [ 224.0.1.1 ]
+ * broadcastdelay 0.0102
+ * restrict [ addr ] [ mask 255.255.255.0 ] ignore|noserve|notrust|noquery
+ * driftfile file_name
+ * keys file_name
+ * statsdir /var/NTP/
+ * filegen peerstats [ file peerstats ] [ type day ] [ link ]
+ * clientlimit [ n ]
+ * clientperiod [ 3600 ]
+ * trustedkey [ key ]
+ * requestkey [ key]
+ * controlkey [ key ]
+ * trap [ addr ]
+ * fudge [ addr ] [ stratum ] [ refid ] ...
+ * pidfile [ ]
+ * setvar [ ]
+ * logfile logfile
+ * logconfig [+|-|=][{sync|sys|peer|clock}{{,all}{info|statistics|events|status}}]...
+ * enable auth|bclient|pll|kernel|monitor|stats
+ * disable auth|bclient|pll|kernel|monitor|stats
+ * phone ...
+ * pps device [assert|clear] [hardpps]
+ */
+
+/*
+ * Types of entries we understand.
+ */
+#define CONFIG_UNKNOWN 0
+
+#define CONFIG_PEER 1
+#define CONFIG_SERVER 2
+#define CONFIG_AUTOMAX 3
+#define CONFIG_DRIFTFILE 4
+#define CONFIG_BROADCAST 5
+#define CONFIG_BROADCASTCLIENT 6
+#define CONFIG_AUTHENTICATE 7
+#define CONFIG_KEYS 8
+#define CONFIG_REVOKE 9
+#define CONFIG_PPS 10
+#define CONFIG_RESTRICT 11
+#define CONFIG_BDELAY 12
+#define CONFIG_TRUSTEDKEY 13
+#define CONFIG_REQUESTKEY 14
+#define CONFIG_CONTROLKEY 15
+#define CONFIG_TRAP 16
+#define CONFIG_FUDGE 17
+#define CONFIG_18 18 /* unused */
+#define CONFIG_STATSDIR 19
+#define CONFIG_FILEGEN 20
+#define CONFIG_STATISTICS 21
+#define CONFIG_PIDFILE 22
+#define CONFIG_SETVAR 23
+#define CONFIG_CLIENTLIMIT 24
+#define CONFIG_CLIENTPERIOD 25
+#define CONFIG_MULTICASTCLIENT 26
+#define CONFIG_ENABLE 27
+#define CONFIG_DISABLE 28
+#define CONFIG_PHONE 29
+#define CONFIG_LOGFILE 30
+#define CONFIG_LOGCONFIG 31
+#define CONFIG_MANYCASTCLIENT 32
+#define CONFIG_MANYCASTSERVER 33
+
+#define CONF_MOD_VERSION 1
+#define CONF_MOD_KEY 2
+#define CONF_MOD_MINPOLL 3
+#define CONF_MOD_MAXPOLL 4
+#define CONF_MOD_PREFER 5
+#define CONF_MOD_BURST 6
+#define CONF_MOD_SKEY 7
+#define CONF_MOD_TTL 8
+#define CONF_MOD_MODE 9
+#define CONF_MOD_NOSELECT 10
+
+#define CONF_RES_MASK 1
+#define CONF_RES_IGNORE 2
+#define CONF_RES_NOSERVE 3
+#define CONF_RES_NOTRUST 4
+#define CONF_RES_NOQUERY 5
+#define CONF_RES_NOMODIFY 6
+#define CONF_RES_NOPEER 7
+#define CONF_RES_NOTRAP 8
+#define CONF_RES_LPTRAP 9
+#define CONF_RES_NTPPORT 10
+#define CONF_RES_LIMITED 11
+
+#define CONF_TRAP_PORT 1
+#define CONF_TRAP_INTERFACE 2
+
+#define CONF_FDG_TIME1 1
+#define CONF_FDG_TIME2 2
+#define CONF_FDG_STRATUM 3
+#define CONF_FDG_REFID 4
+#define CONF_FDG_FLAG1 5
+#define CONF_FDG_FLAG2 6
+#define CONF_FDG_FLAG3 7
+#define CONF_FDG_FLAG4 8
+
+#define CONF_FGEN_FILE 1
+#define CONF_FGEN_TYPE 2
+#define CONF_FGEN_FLAG_LINK 3
+#define CONF_FGEN_FLAG_NOLINK 4
+#define CONF_FGEN_FLAG_ENABLE 5
+#define CONF_FGEN_FLAG_DISABLE 6
+
+#define CONF_PPS_ASSERT 1
+#define CONF_PPS_CLEAR 2
+#define CONF_PPS_HARDPPS 3
+
+/*
+ * Translation table - keywords to function index
+ */
+struct keyword {
+ const char *text;
+ int keytype;
+};
+
+/*
+ * Command keywords
+ */
+static struct keyword keywords[] = {
+ { "peer", CONFIG_PEER },
+ { "server", CONFIG_SERVER },
+ { "driftfile", CONFIG_DRIFTFILE },
+ { "broadcast", CONFIG_BROADCAST },
+ { "broadcastclient", CONFIG_BROADCASTCLIENT },
+ { "multicastclient", CONFIG_MULTICASTCLIENT },
+ { "manycastclient", CONFIG_MANYCASTCLIENT },
+ { "manycastserver", CONFIG_MANYCASTSERVER },
+ { "authenticate", CONFIG_AUTHENTICATE },
+ { "keys", CONFIG_KEYS },
+ { "revoke", CONFIG_REVOKE },
+ { "pps", CONFIG_PPS },
+ { "automax", CONFIG_AUTOMAX },
+ { "restrict", CONFIG_RESTRICT },
+ { "broadcastdelay", CONFIG_BDELAY },
+ { "trustedkey", CONFIG_TRUSTEDKEY },
+ { "requestkey", CONFIG_REQUESTKEY },
+ { "controlkey", CONFIG_CONTROLKEY },
+ { "trap", CONFIG_TRAP },
+ { "fudge", CONFIG_FUDGE },
+ { "statsdir", CONFIG_STATSDIR },
+ { "filegen", CONFIG_FILEGEN },
+ { "statistics", CONFIG_STATISTICS },
+ { "pidfile", CONFIG_PIDFILE },
+ { "setvar", CONFIG_SETVAR },
+ { "clientlimit", CONFIG_CLIENTLIMIT },
+ { "clientperiod", CONFIG_CLIENTPERIOD },
+ { "enable", CONFIG_ENABLE },
+ { "disable", CONFIG_DISABLE },
+ { "phone", CONFIG_PHONE },
+ { "logfile", CONFIG_LOGFILE },
+ { "logconfig", CONFIG_LOGCONFIG },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "peer", "server", "broadcast" modifier keywords
+ */
+static struct keyword mod_keywords[] = {
+ { "version", CONF_MOD_VERSION },
+ { "key", CONF_MOD_KEY },
+ { "minpoll", CONF_MOD_MINPOLL },
+ { "maxpoll", CONF_MOD_MAXPOLL },
+ { "prefer", CONF_MOD_PREFER },
+ { "noselect", CONF_MOD_NOSELECT },
+ { "burst", CONF_MOD_BURST },
+ { "autokey", CONF_MOD_SKEY },
+ { "mode", CONF_MOD_MODE }, /* reference clocks */
+ { "ttl", CONF_MOD_TTL }, /* NTP peers */
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "restrict" modifier keywords
+ */
+static struct keyword res_keywords[] = {
+ { "mask", CONF_RES_MASK },
+ { "ignore", CONF_RES_IGNORE },
+ { "noserve", CONF_RES_NOSERVE },
+ { "notrust", CONF_RES_NOTRUST },
+ { "noquery", CONF_RES_NOQUERY },
+ { "nomodify", CONF_RES_NOMODIFY },
+ { "nopeer", CONF_RES_NOPEER },
+ { "notrap", CONF_RES_NOTRAP },
+ { "lowpriotrap", CONF_RES_LPTRAP },
+ { "ntpport", CONF_RES_NTPPORT },
+ { "limited", CONF_RES_LIMITED },
+ { "", 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[] = {
+ { "time1", CONF_FDG_TIME1 },
+ { "time2", CONF_FDG_TIME2 },
+ { "stratum", CONF_FDG_STRATUM },
+ { "refid", CONF_FDG_REFID },
+ { "flag1", CONF_FDG_FLAG1 },
+ { "flag2", CONF_FDG_FLAG2 },
+ { "flag3", CONF_FDG_FLAG3 },
+ { "flag4", CONF_FDG_FLAG4 },
+ { "", CONFIG_UNKNOWN }
+};
+
+
+/*
+ * "filegen" modifier keywords
+ */
+static struct keyword filegen_keywords[] = {
+ { "file", CONF_FGEN_FILE },
+ { "type", CONF_FGEN_TYPE },
+ { "link", CONF_FGEN_FLAG_LINK },
+ { "nolink", CONF_FGEN_FLAG_NOLINK },
+ { "enable", CONF_FGEN_FLAG_ENABLE },
+ { "disable", CONF_FGEN_FLAG_DISABLE },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "type" modifier keywords
+ */
+static struct keyword fgen_types[] = {
+ { "none", FILEGEN_NONE },
+ { "pid", FILEGEN_PID },
+ { "day", FILEGEN_DAY },
+ { "week", FILEGEN_WEEK },
+ { "month", FILEGEN_MONTH },
+ { "year", FILEGEN_YEAR },
+ { "age", FILEGEN_AGE },
+ { "", CONFIG_UNKNOWN}
+};
+
+/*
+ * "enable", "disable" modifier keywords
+ */
+static struct keyword flags_keywords[] = {
+ { "auth", PROTO_AUTHENTICATE },
+ { "bclient", PROTO_BROADCLIENT },
+ { "ntp", PROTO_NTP },
+ { "kernel", PROTO_KERNEL },
+ { "monitor", PROTO_MONITOR },
+ { "stats", PROTO_FILEGEN },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * pps modifier keywords
+ */
+static struct keyword pps_keywords[] = {
+ { "assert", CONF_PPS_ASSERT },
+ { "clear", CONF_PPS_CLEAR },
+ { "hardpps", CONF_PPS_HARDPPS },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "logconfig" building blocks
+ */
+struct masks {
+ const char *name;
+ unsigned long mask;
+};
+
+static struct masks logcfg_class[] = {
+ { "sys", NLOG_OSYS },
+ { "peer", NLOG_OPEER },
+ { "clock", NLOG_OCLOCK },
+ { "sync", NLOG_OSYNC },
+ { (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 MAXFILENAME 128 /* maximum length of a file name (alloca()?) */
+
+
+/*
+ * 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)
+
+/*
+ * File descriptor used by the resolver save routines, and temporary file
+ * name.
+ */
+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 pps_device[MAXPPS + 1]; /* PPS device name */
+int pps_assert = 1;
+int pps_hardpps;
+int listen_to_virtual_ips = 0;
+#if defined(HAVE_SCHED_SETSCHEDULER)
+int config_priority_override = 0;
+int config_priority;
+#endif
+
+static const char *ntp_options = "aAbc:dD:f:gk:l:Lmnp:P:r:s:t:v:V:x";
+
+#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 *));
+static int getnetnum P((const char *, struct sockaddr_in *, int));
+static void save_resolve P((char *, int, int, int, int, int, int, u_long));
+static void do_resolve_internal P((void));
+static void abort_resolve P((void));
+#if !defined(VMS)
+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;
+}
+
+/*
+ * getstartup - search through the options looking for a debugging flag
+ */
+void
+getstartup(
+ int argc,
+ char *argv[]
+ )
+{
+ int errflg;
+ 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");
+ ++errflg;
+ break;
+#endif
+ case 'L':
+ listen_to_virtual_ips = 1;
+ break;
+ case 'l':
+ {
+ FILE *new_file;
+
+ 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':
+ ++nofork;
+ break;
+
+ case '?':
+ ++errflg;
+ break;
+
+ default:
+ 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
+ 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
+}
+
+/*
+ * getconfig - get command line options and read the configuration file
+ */
+void
+getconfig(
+ int argc,
+ char *argv[]
+ )
+{
+ register int i;
+ int c;
+ int errflg;
+ int peerversion;
+ int minpoll;
+ int maxpoll;
+ int ttl;
+ u_long peerkey;
+ u_long lpeerkey;
+ int peerflags;
+ int hmode;
+ struct sockaddr_in peeraddr;
+ struct sockaddr_in maskaddr;
+ FILE *fp;
+ char line[MAXLINE];
+ char *(tokens[MAXTOKENS]);
+ int ntokens;
+ int tok = CONFIG_UNKNOWN;
+ struct interface *localaddr;
+ 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 */
+ struct refclockstat clock_stat;
+ FILEGEN *filegen;
+
+ /*
+ * Initialize, initialize
+ */
+ errflg = 0;
+#ifdef DEBUG
+ debug = 0;
+#endif /* DEBUG */
+#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.);
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, ntp_options)) != EOF) {
+ switch (c) {
+ case 'a':
+ proto_config(PROTO_AUTHENTICATE, 1, 0.);
+ break;
+
+ case 'A':
+ proto_config(PROTO_AUTHENTICATE, 0, 0.);
+ break;
+
+ case 'b':
+ proto_config(PROTO_BROADCLIENT, 1, 0.);
+ 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':
+ correct_any = TRUE;
+ break;
+
+ case 'k':
+ getauthkeys(ntp_optarg);
+ break;
+
+ case 'L': /* already done at pre-scan */
+ case 'l': /* already done at pre-scan */
+ break;
+
+ case 'm':
+ proto_config(PROTO_MULTICAST_ADD, htonl(INADDR_NTP), 0.);
+ sys_bclient = 1;
+ 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 '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);
+ }
+ } while (0);
+ 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,
+ RW | ((c == 'V') ? DEF : 0));
+ break;
+
+ case 'x':
+ allow_set_backward = FALSE;
+ break;
+
+ 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
+ exit(2);
+ }
+
+ if (
+ (fp = 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 = 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)
+ tok = gettokens(fp, line, tokens, &ntokens);
+#ifdef HAVE_NETINFO
+ else
+ tok = gettokens_netinfo(config_netinfo, tokens, &ntokens);
+#endif /* HAVE_NETINFO */
+
+ if (tok == CONFIG_UNKNOWN) 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;
+ }
+
+ if (!getnetnum(tokens[1], &peeraddr, 0)) {
+ errflg = -1;
+ } else {
+ errflg = 0;
+
+ if (
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ ISBADADR(&peeraddr)) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ ntoa(&peeraddr));
+ break;
+ }
+ /*
+ * Shouldn't be able to specify multicast
+ * address for server/peer!
+ * and unicast address for manycastclient!
+ */
+ if (((tok == CONFIG_SERVER) ||
+ (tok == CONFIG_PEER)) &&
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ IN_CLASSD(ntohl(peeraddr.sin_addr.s_addr))) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ ntoa(&peeraddr));
+ break;
+ }
+ if ((tok == CONFIG_MANYCASTCLIENT) &&
+ !IN_CLASSD(ntohl(peeraddr.sin_addr.s_addr))) {
+ msyslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ ntoa(&peeraddr));
+ break;
+ }
+ }
+
+ peerversion = NTP_VERSION;
+ minpoll = NTP_MINDPOLL;
+ maxpoll = NTP_MAXDPOLL;
+ peerkey = 0;
+ peerflags = 0;
+ ttl = 0;
+ for (i = 2; i < ntokens; i++)
+ switch (matchkey(tokens[i], mod_keywords)) {
+ 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)
+ 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)
+ 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_SKEY:
+ peerflags |= FLAG_SKEY | FLAG_AUTHENABLE;
+ break;
+
+ case CONF_MOD_TTL:
+ if (i >= ntokens-1) {
+ msyslog(LOG_ERR,
+ "ttl: argument required");
+ errflg = 1;
+ break;
+ }
+ ttl = atoi(tokens[++i]);
+ 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,
+ (struct interface *)0, hmode,
+ peerversion, minpoll, maxpoll,
+ peerflags, ttl, peerkey)
+ == 0) {
+ msyslog(LOG_ERR,
+ "configuration of %s failed",
+ ntoa(&peeraddr));
+ }
+ } else if (errflg == -1) {
+ save_resolve(tokens[1], hmode, peerversion,
+ minpoll, maxpoll, peerflags, ttl,
+ peerkey);
+ }
+ 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_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.);
+ break;
+
+ case CONFIG_MULTICASTCLIENT:
+ case CONFIG_MANYCASTSERVER:
+ if (ntokens > 1) {
+ for (i = 1; i < ntokens; i++) {
+ if (getnetnum(tokens[i], &peeraddr, 1))
+ proto_config(PROTO_MULTICAST_ADD,
+ peeraddr.sin_addr.s_addr, 0.);
+ }
+ } else
+ proto_config(PROTO_MULTICAST_ADD,
+ htonl(INADDR_NTP), 0.);
+ if (tok == CONFIG_MULTICASTCLIENT) {
+ sys_bclient = 1;
+#ifdef DEBUG
+ if (debug)
+ printf("sys_bclient\n");
+#endif /* DEBUG */
+ }
+ else if (tok == CONFIG_MANYCASTSERVER) {
+ sys_manycastserver = 1;
+#ifdef DEBUG
+ if (debug)
+ printf("sys_manycastserver\n");
+#endif /* DEBUG */
+ }
+ break;
+
+ case CONFIG_AUTHENTICATE:
+ errflg = 0;
+ if (ntokens >= 2) {
+ if (STREQ(tokens[1], "yes"))
+ proto_config(PROTO_AUTHENTICATE, 1, 0.);
+ else if (STREQ(tokens[1], "no"))
+ proto_config(PROTO_AUTHENTICATE, 0, 0.);
+ else
+ errflg++;
+ } else {
+ errflg++;
+ }
+
+ if (errflg)
+ msyslog(LOG_ERR,
+ "should be `authenticate yes|no'");
+ break;
+
+ case CONFIG_KEYS:
+ if (ntokens >= 2) {
+ getauthkeys(tokens[1]);
+ }
+ break;
+
+ case CONFIG_REVOKE:
+ if (ntokens >= 2) {
+ sys_revoke = 1 << max(atoi(tokens[1]), 10);
+ }
+ break;
+
+ case CONFIG_AUTOMAX:
+ if (ntokens >= 2) {
+ sys_automax = 1 << max(atoi(tokens[1]), 10);
+ }
+ break;
+
+ case CONFIG_RESTRICT:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR, "restrict requires an address");
+ break;
+ }
+ if (STREQ(tokens[1], "default"))
+ peeraddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ else if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ /*
+ * Use peerversion as flags, peerkey as mflags. Ick.
+ */
+ peerversion = 0;
+ peerkey = 0;
+ errflg = 0;
+ maskaddr.sin_addr.s_addr = ~(u_int32)0;
+ for (i = 2; i < ntokens; i++) {
+ switch (matchkey(tokens[i], res_keywords)) {
+ 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_LIMITED:
+ peerversion |= RES_LIMITED;
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+ }
+ if (SRCADR(&peeraddr) == htonl(INADDR_ANY))
+ maskaddr.sin_addr.s_addr = 0;
+ 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);
+ }
+ }
+ break;
+
+ case CONFIG_TRUSTEDKEY:
+ for (i = 1; i < ntokens; i++) {
+ u_long 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) {
+ u_long rkey;
+
+ if (!atouint(tokens[1], &rkey)) {
+ msyslog(LOG_ERR,
+ "%s is undecodable as request key",
+ tokens[1]);
+ } else if (rkey == 0) {
+ msyslog(LOG_ERR,
+ "%s makes a poor request keyid",
+ tokens[1]);
+ } else {
+#ifdef DEBUG
+ if (debug > 3)
+ printf(
+ "set info_auth_key to %lu\n", rkey);
+#endif
+ info_auth_keyid = rkey;
+ }
+ }
+ break;
+
+ case CONFIG_CONTROLKEY:
+ if (ntokens >= 2) {
+ u_long 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;
+ }
+ if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ /*
+ * Use peerversion for port number. Barf.
+ */
+ errflg = 0;
+ peerversion = 0;
+ localaddr = 0;
+ for (i = 2; i < ntokens-1; i++)
+ switch (matchkey(tokens[i], trap_keywords)) {
+ 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;
+ }
+
+ 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",
+ ntoa(&maskaddr));
+ errflg = 1;
+ }
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+
+ if (!errflg) {
+ if (peerversion != 0)
+ peeraddr.sin_port = htons( (u_short) peerversion);
+ else
+ peeraddr.sin_port = htons(TRAPPORT);
+ if (localaddr == NULL)
+ localaddr = any_interface;
+ if (!ctlsettrap(&peeraddr, localaddr, 0,
+ NTP_VERSION))
+ msyslog(LOG_ERR,
+ "can't set trap for %s, no resources",
+ ntoa(&peeraddr));
+ }
+ break;
+
+ case CONFIG_FUDGE:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no address for fudge command, line ignored");
+ break;
+ }
+ if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ if (!ISREFCLOCKADR(&peeraddr)) {
+ msyslog(LOG_ERR,
+ "%s is inappropriate address for the fudge command, line ignored",
+ ntoa(&peeraddr));
+ break;
+ }
+
+ memset((void *)&clock_stat, 0, sizeof clock_stat);
+ errflg = 0;
+ for (i = 2; i < ntokens-1; i++) {
+ switch (c = matchkey(tokens[i],
+ fudge_keywords)) {
+ case CONF_FDG_TIME1:
+ if (sscanf(tokens[++i], "%lf",
+ &clock_stat.fudgetime1) != 1) {
+ msyslog(LOG_ERR,
+ "fudge %s time1 value in error",
+ ntoa(&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",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock_stat.haveflags |= CLK_HAVETIME2;
+ break;
+
+
+ case CONF_FDG_STRATUM:
+ /* HMS: the (long *)_ may be trouble */
+ if (!atoint(tokens[++i],
+ (long *)&clock_stat.fudgeval1))
+ {
+ msyslog(LOG_ERR,
+ "fudge %s stratum value in error",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ 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], &lpeerkey)
+ || lpeerkey > 1) {
+ msyslog(LOG_ERR,
+ "fudge %s flag value in error",
+ ntoa(&peeraddr));
+ peerkey = lpeerkey;
+ errflg = i;
+ break;
+ }
+ peerkey = lpeerkey;
+ 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 (peerkey == 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)) {
+ 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);
+ 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,
+ RW |
+ ((((ntokens > 2)
+ && !strcmp(tokens[2],
+ "default")))
+ ? DEF
+ : 0));
+ }
+ break;
+
+ case CONFIG_CLIENTLIMIT:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no value for clientlimit command - line ignored");
+ } else {
+ u_long ui;
+
+ if (!atouint(tokens[1], &ui) || !ui) {
+ msyslog(LOG_ERR,
+ "illegal value for clientlimit command - line ignored");
+ } else {
+ char bp[80];
+
+#ifdef DEBUG
+ if (debug)
+ sprintf(bp, "client_limit=%lu", ui);
+#endif
+ set_sys_var(bp, strlen(bp)+1, RO);
+ client_limit = ui;
+ }
+ }
+ break;
+
+ case CONFIG_CLIENTPERIOD:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "no value for clientperiod command - line ignored");
+ } else {
+ u_long ui;
+
+ if (!atouint(tokens[1], &ui) || ui < 64) {
+ msyslog(LOG_ERR,
+ "illegal value for clientperiod command - line ignored");
+ } else {
+ char bp[80];
+
+ sprintf(bp, "client_limit_period=%ld", ui);
+ set_sys_var(bp, strlen(bp)+1, RO);
+ client_limit_period = ui;
+ }
+ }
+ break;
+
+ case CONFIG_ENABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords);
+ if (flag == CONFIG_UNKNOWN) {
+ msyslog(LOG_ERR,
+ "enable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 1, 0.);
+ }
+ break;
+
+ case CONFIG_DISABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords);
+ if (flag == CONFIG_UNKNOWN) {
+ msyslog(LOG_ERR,
+ "disable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 0, 0.);
+ }
+ 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_PPS:
+ if (ntokens < 2) {
+ msyslog(LOG_ERR,
+ "pps missing device name");
+ break;
+ }
+ (void)strncpy(pps_device, tokens[1], MAXPPS);
+ for (i = 2; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], pps_keywords);
+ switch(flag) {
+ case CONF_PPS_ASSERT:
+ pps_assert = 1;
+ break;
+ case CONF_PPS_CLEAR:
+ pps_assert = 0;
+ break;
+ case CONF_PPS_HARDPPS:
+ pps_hardpps = 1;
+ break;
+ default:
+ msyslog(LOG_ERR,
+ "pps unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ if(errflg)
+ break;
+ }
+ break;
+ }
+ }
+ if (fp) (void)fclose(fp);
+#ifdef HAVE_NETINFO
+ if (config_netinfo) free_netinfo_config(config_netinfo);
+#endif /* HAVE_NETINFO */
+
+ if (res_fp != NULL) {
+ /*
+ * 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);
+ 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
+ )
+{
+ for (;;) {
+ if (keys->keytype == CONFIG_UNKNOWN) {
+ 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_in *addr,
+ int complain
+ )
+{
+ register const char *cp;
+ register char *bp;
+ register int i;
+ register int temp;
+ char buf[80]; /* will core dump on really stupid stuff */
+ u_int32 netnum;
+
+ /* XXX ELIMINATE replace with decodenetnum */
+ cp = num;
+ netnum = 0;
+ for (i = 0; i < 4; i++) {
+ bp = buf;
+ while (isdigit((int)*cp))
+ *bp++ = *cp++;
+ if (bp == buf)
+ break;
+
+ if (i < 3) {
+ if (*cp++ != '.')
+ break;
+ } else if (*cp != '\0')
+ break;
+
+ *bp = '\0';
+ temp = atoi(buf);
+ if (temp > 255)
+ break;
+ netnum <<= 8;
+ netnum += temp;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("getnetnum %s step %d buf %s temp %d netnum %lu\n",
+ num, i, buf, temp, (u_long)netnum);
+#endif
+ }
+
+ if (i < 4) {
+ if (complain)
+ msyslog(LOG_ERR,
+ "getnetnum: \"%s\" invalid host number, line ignored",
+ num);
+#ifdef DEBUG
+ if (debug > 3)
+ printf(
+ "getnetnum: \"%s\" invalid host number, line ignored\n",
+ num);
+#endif
+ return 0;
+ }
+
+ /*
+ * make up socket address. Clear it out for neatness.
+ */
+ memset((void *)addr, 0, sizeof(struct sockaddr_in));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(NTP_PORT);
+ addr->sin_addr.s_addr = htonl(netnum);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("getnetnum given %s, got %s (%lx)\n",
+ num, ntoa(addr), (u_long)netnum);
+#endif
+ return 1;
+}
+
+
+#if !defined(VMS)
+/*
+ * 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,
+ int flags,
+ int ttl,
+ u_long keyid
+ )
+{
+#ifndef SYS_VXWORKS
+ if (res_fp == NULL) {
+#ifndef SYS_WINNT
+ (void) strcpy(res_file, RES_TEMPFILE);
+#else
+ /* no /tmp directory under NT */
+ {
+ DWORD len;
+ if(!(len = 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, "w");
+ }
+#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 %lu\n", name, mode,
+ version, minpoll, maxpoll, flags, ttl, keyid);
+#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 */
+}
+
+
+#define KEY_TYPE_MD5 4
+
+/*
+ * 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,
+ "internal error in do_resolve_internal: res_fp == NULL");
+ exit(1);
+ }
+
+ /* we are done with this now */
+ (void) fclose(res_fp);
+ res_fp = NULL;
+
+#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[8];
+
+ for (i = 0; i < 8; i++)
+ rankey[i] = RANDOM & 0xff;
+ authusekey(req_keyid, KEY_TYPE_MD5, (u_char *)rankey);
+ authtrust(req_keyid, 1);
+ }
+
+ /* save keyid so we will accept config requests with it */
+ info_auth_keyid = req_keyid;
+ 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 ane io associations
+ * shared with the NTP daemon. I currently don't want
+ *
+ * we also block SIGIO (currently no portes 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();
+
+ (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);
+ if (!(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 */
+ 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/contrib/ntp/ntpd/ntp_control.c b/contrib/ntp/ntpd/ntp_control.c
new file mode 100644
index 0000000..e63765f
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_control.c
@@ -0,0 +1,2652 @@
+/*
+ * ntp_control.c - respond to control messages and send async traps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_control.h"
+#include "ntp_stdlib.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)); /* routine to handle request */
+};
+
+/*
+ * Only one flag. Authentication required or not.
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+/*
+ * Request processing routines
+ */
+static void ctl_error P((int));
+static u_short ctlclkstatus P((struct refclockstat *));
+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));
+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_in *, 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, "phase" }, /* 11 */
+ { CS_DRIFT, RO, "frequency" }, /* 12 */
+ { CS_COMPLIANCE, RO, "jitter" }, /* 13 */
+ { CS_CLOCK, RO, "clock" }, /* 14 */
+ { CS_PROCESSOR, RO, "processor" }, /* 15 */
+ { CS_SYSTEM, RO, "system" }, /* 16 */
+ { CS_STABIL, RO, "stability" }, /* 17 */
+ { CS_VARLIST, RO, "sys_var_list" }, /* 18 */
+ { 0, EOV, "" }
+};
+
+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_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_COMPLIANCE,
+ CS_STABIL,
+ 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, "valid" }, /* 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_DISP, PADDING,"" }, /* 36 */
+ { CP_VARLIST, RO, "peer_var_list" }, /* 37 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Peer variables we print by default
+ */
+static u_char def_peer_var[] = {
+ CP_SRCADR,
+ CP_SRCPORT,
+ CP_DSTADR,
+ CP_DSTPORT,
+ CP_KEYID,
+ CP_STRATUM,
+ CP_PRECISION,
+ CP_ROOTDELAY,
+ CP_ROOTDISPERSION,
+ CP_REFID,
+ CP_REFTIME,
+ CP_DELAY,
+ CP_OFFSET,
+ CP_JITTER,
+ CP_DISPERSION,
+ CP_REACH,
+ CP_VALID,
+ CP_HMODE,
+ CP_PMODE,
+ CP_HPOLL,
+ CP_PPOLL,
+ CP_LEAP,
+ CP_FLASH,
+ CP_ORG,
+ CP_REC,
+ CP_XMT,
+ CP_FILTDELAY,
+ CP_FILTOFFSET,
+ CP_FILTERROR,
+ 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, "" }
+};
+
+
+/*
+ * 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. These will change for the gizmo board.
+ */
+#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) */
+};
+
+
+/*
+ * Keyid used for authenticating write requests.
+ */
+u_long 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 u_short res_associd;
+static int res_offset;
+static u_char * datapt;
+static u_char * dataend;
+static int datalinelen;
+static int datanotbinflag;
+static struct sockaddr_in *rmt_addr;
+static struct interface *lcl_inter;
+
+static u_char res_authenticate;
+static u_char res_authokay;
+static u_long 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 > 1)
+ 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 >= 3)
+ printf(
+ "recv_len %d, properlen %d, wants auth with keyid %ld, MAC length=%d\n",
+ rbufp->recv_length, properlen, res_keyid, maclen);
+#endif
+ if (!authistrusted(res_keyid)) {
+#ifdef DEBUG
+ if (debug >= 2)
+ printf("invalid keyid %lu\n", res_keyid);
+#endif
+ } else if (authdecrypt(res_keyid, (u_int32 *)pkt,
+ rbufp->recv_length - maclen, maclen)) {
+#ifdef DEBUG
+ if (debug >= 3)
+ printf("authenticated okay\n");
+#endif
+ res_authokay = 1;
+ } else {
+#ifdef DEBUG
+ if (debug >= 3)
+ 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
+ */
+static u_short
+ctlclkstatus(
+ struct refclockstat *this_clock
+ )
+{
+ return ((u_short)(this_clock->currentstatus) << 8)
+ | (u_short)(this_clock->lastevent);
+}
+
+
+
+/*
+ * 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;
+ u_long 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 a dotted quad IP address into the response
+ */
+static void
+ctl_putadr(
+ const char *tag,
+ u_int32 addr
+ )
+{
+ register char *cp;
+ register const char *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = numtoa(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;
+#ifdef HAVE_UNAME
+ char str[50];
+#endif
+
+ 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)
+ ctl_putadr(sys_var[CS_REFID].text, sys_refid);
+ 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_COMPLIANCE:
+ ctl_putdbl(sys_var[CS_COMPLIANCE].text, sys_error * 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
+ (void)strcpy(str, utsnamebuf.sysname);
+ (void)strcat(str, utsnamebuf.release);
+ ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str));
+#endif /* HAVE_UNAME */
+ 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 8-( - Killer */
+
+ 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;
+ }
+}
+
+
+/*
+ * ctl_putpeer - output a peer variable
+ */
+static void
+ctl_putpeer(
+ int varid,
+ struct peer *peer
+ )
+{
+ 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,
+ peer->srcadr.sin_addr.s_addr);
+ break;
+ case CP_SRCPORT:
+ ctl_putuint(peer_var[CP_SRCPORT].text,
+ ntohs(peer->srcadr.sin_port));
+ break;
+ case CP_DSTADR:
+ ctl_putadr(peer_var[CP_DSTADR].text,
+ peer->processed ?
+ peer->cast_flags & MDF_BCAST ?
+ peer->dstadr->bcast.sin_addr.s_addr:
+ peer->cast_flags ?
+ peer->dstadr->sin.sin_addr.s_addr ?
+ peer->dstadr->sin.sin_addr.s_addr:
+ peer->dstadr->bcast.sin_addr.s_addr:
+ 8 : 12);
+ break;
+ case CP_DSTPORT:
+ ctl_putuint(peer_var[CP_DSTPORT].text,
+ (u_long)(peer->dstadr
+ ? ntohs(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->stratum > 1)
+ {
+ if (peer->flags & FLAG_REFCLOCK)
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->srcadr.sin_addr.s_addr);
+ else
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->refid);
+ }
+ 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_VALID:
+ ctl_putuint(peer_var[CP_VALID].text, peer->valid);
+ 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->variance) * 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 8-( - Killer */
+
+ 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 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);
+ 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 8-( - Killer */
+
+ 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 (cp < reqend)
+ cp++;
+ *tp = '\0';
+ while (isspace((int)(*(tp-1))))
+ *(--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((int)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((int)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((int)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;
+ /*int leapind, leapwarn;*/
+
+ /*
+ * 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());
+
+ /*
+ * Set flags to not-in-sync so we can tell when we get something.
+ */
+ /*
+ leapind = ~0;
+ leapwarn = ~0;
+ */
+
+ /*
+ * 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); /* our fault, really */
+ return;
+ }
+ }
+ }
+
+ /*
+ * If we got anything, do it.
+ */
+ /*
+ 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((int)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_in *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 = 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 clr a trap
+ */
+int
+ctlclrtrap(
+ struct sockaddr_in *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_in *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
+ && NSRCADR(raddr) == NSRCADR(&tp->tr_addr)
+ && NSRCPORT(raddr) == NSRCPORT(&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)) {
+ 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.sin_addr.s_addr);
+ else
+#endif
+ src = ntoa(&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. Maybe more selective later
+ */
+ for (i = 1; i <= CS_MAXCODE; i++)
+ 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++)
+ 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,
+ int 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,
+ int def
+ )
+{
+ register struct ctl_var *k;
+ register const char *s;
+ register const char *t;
+ char *td;
+
+ if (!data || !size)
+ return;
+
+ if ((k = *kv))
+ {
+ 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,
+ int 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/contrib/ntp/ntpd/ntp_filegen.c b/contrib/ntp/ntpd/ntp_filegen.c
new file mode 100644
index 0000000..bcf3f9c
--- /dev/null
+++ b/contrib/ntp/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 loosing 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 -= 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 = type;
+ gen->flag = 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/contrib/ntp/ntpd/ntp_intres.c b/contrib/ntp/ntpd/ntp_intres.c
new file mode 100644
index 0000000..1829914
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_intres.c
@@ -0,0 +1,970 @@
+/*
+ * 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 <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <signal.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_request.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+#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 */
+};
+#define ce_peeraddr ce_config.peeraddr
+#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
+
+/*
+ * 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 NUMTOK 8
+
+#define MAXLINESIZE 512
+
+
+/*
+ * File descriptor for ntp request code.
+ */
+static int sockfd = -1;
+
+
+/* stuff to be filled in by caller */
+
+u_long 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, int, int, u_long));
+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));
+
+/*
+ * assumes: req_key, req_keyid, conffile valid
+ * syslog still open
+ */
+void
+ntp_intres(void)
+{
+ FILE *in;
+#ifdef HAVE_SIGSUSPEND
+ sigset_t set;
+
+ sigemptyset(&set);
+#endif /* NTP_POSIX_SOURCE */
+
+#ifdef DEBUG
+ if (debug) {
+ msyslog(LOG_INFO, "NTP_INTRES running");
+ }
+#endif
+
+ /* check out auth stuff */
+ if (sys_authenticate) {
+ if (!authistrusted(req_keyid)) {
+ msyslog(LOG_ERR, "invalid request keyid %lu",
+ 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
+ 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
+ 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,
+ int flags,
+ int ttl,
+ u_long keyid
+ )
+{
+ register char *cp;
+ register struct conf_entry *ce;
+ unsigned int len;
+
+ 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_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;
+ 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
+ *
+ * The routine sticks the address into the entry's ce_peeraddr if it
+ * gets 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 will still be zero.
+ */
+static int
+findhostaddr(
+ struct conf_entry *entry
+ )
+{
+ struct hostent *hp;
+
+ checkparent(); /* make sure our guy is still running */
+
+ hp = gethostbyname(entry->ce_name);
+
+ if (hp == NULL) {
+#ifndef NODNS
+ /*
+ * If the resolver is in use, see if the failure is
+ * temporary. If so, return success.
+ */
+ if (h_errno == TRY_AGAIN)
+ return (1);
+#endif
+ return (0);
+ }
+
+ /*
+ * Use the first address. We don't have any way to
+ * tell preferences and older gethostbyname() implementations
+ * only return one.
+ */
+ memmove((char *)&(entry->ce_peeraddr),
+ (char *)hp->h_addr,
+ sizeof(struct in_addr));
+ return (1);
+}
+
+
+/*
+ * openntp - open a socket to the ntp server
+ */
+static void
+openntp(void)
+{
+ struct sockaddr_in saddr;
+
+ if (sockfd >= 0)
+ return;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd == -1) {
+ msyslog(LOG_ERR, "socket() failed: %m");
+ exit(1);
+ }
+
+ memset((char *)&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(NTP_PORT); /* trash */
+ saddr.sin_addr.s_addr = htonl(LOCALHOST); /* garbage */
+
+ /*
+ * 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, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
+ msyslog(LOG_ERR, "connect() failed: %m");
+ exit(1);
+ }
+}
+
+
+/*
+ * 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));
+ 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 so 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)
+ {
+ msyslog(LOG_ERR, "select() fails: %m");
+ return 0;
+ }
+ else if (n == 0)
+ {
+ if(debug)
+ msyslog(LOG_DEBUG, "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,
+ "server reports implementation mismatch!!");
+ return 0;
+
+ case INFO_ERR_REQ:
+ msyslog(LOG_ERR,
+ "server claims configuration request is unknown");
+ return 0;
+
+ case INFO_ERR_FMT:
+ msyslog(LOG_ERR,
+ "server indicates a format error occurred(!!)");
+ return 0;
+
+ case INFO_ERR_NODATA:
+ msyslog(LOG_ERR,
+ "server indicates no data available (shouldn't happen)");
+ return 0;
+
+ case INFO_ERR_AUTH:
+ msyslog(LOG_ERR,
+ "server returns a permission denied error");
+ return 0;
+
+ default:
+ msyslog(LOG_ERR,
+ "server 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];
+ 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; 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_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_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]);
+ }
+}
+
+
+/*
+ * doconfigure - attempt to resolve names and configure the server
+ */
+static void
+doconfigure(
+ int dores
+ )
+{
+ register struct conf_entry *ce;
+ register struct conf_entry *ceremove;
+
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO, "doconfigure(%d)", dores);
+#endif
+
+ ce = confentries;
+ while (ce != NULL) {
+#ifdef DEBUG
+ if (debug > 1)
+ msyslog(LOG_INFO, "doconfigure: <%s> has peeraddr %#x",
+ ce->ce_name, ce->ce_peeraddr);
+#endif
+ if (dores && ce->ce_peeraddr == 0) {
+ 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;
+ }
+#ifdef DEBUG
+ if (debug > 1) {
+ msyslog(LOG_INFO,
+ "doconfigure:findhostaddr() worked");
+ }
+#endif
+ }
+
+ if (ce->ce_peeraddr != 0) {
+#ifdef DEBUG
+ if (debug > 1) {
+ msyslog(LOG_INFO,
+ "doconfigure: calling request()");
+ }
+#endif
+ if (request(&ce->ce_config)) {
+#ifdef DEBUG
+ if (debug > 1) {
+ msyslog(LOG_INFO,
+ "doconfigure: request() OK, removing this entry");
+ }
+#endif
+ 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/contrib/ntp/ntpd/ntp_io.c b/contrib/ntp/ntpd/ntp_io.c
new file mode 100644
index 0000000..e920249
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_io.c
@@ -0,0 +1,1754 @@
+/*
+ * 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 <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.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>
+
+#if _BSDI_VERSION >= 199510
+# include <ifaddrs.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"
+
+#if defined(VMS) /* most likely UCX-specific */
+
+#include <UCX$INETDEF.H>
+
+/* "un*x"-compatible names for some items in UCX$INETDEF.H */
+#define ifreq IFREQDEF
+#define ifr_name IFR$T_NAME
+#define ifr_addr IFR$R_DUMMY.IFR$T_ADDR
+#define ifr_broadaddr IFR$R_DUMMY.IFR$T_BROADADDR
+#define ifr_flags IFR$R_DUMMY.IFR$R_DUMMY_1_OVRL.IFR$W_FLAGS
+#define IFF_UP IFR$M_IFF_UP
+#define IFF_BROADCAST IFR$M_IFF_BROADCAST
+#define IFF_LOOPBACK IFR$M_IFF_LOOPBACK
+
+#endif /* VMS */
+
+
+#if defined(VMS) || defined(SYS_WINNT)
+/* structure used in SIOCGIFCONF request (after [KSR] OSF/1) */
+struct ifconf {
+ int ifc_len; /* size of buffer */
+ union {
+ caddr_t ifcu_buf;
+ struct ifreq *ifcu_req;
+ } ifc_ifcu;
+};
+#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
+#define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */
+
+#endif /* VMS */
+
+#if defined(USE_TTY_SIGPOLL) || defined(USE_UDP_SIGPOLL)
+# if defined(SYS_AIX) && defined(_IO) /* XXX Identify AIX some other way */
+# undef _IO
+# endif
+# include <stropts.h>
+#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; /* pointer to default interface */
+struct interface *loopback_interface; /* point to loopback interface */
+static struct interface inter_list[MAXINTERFACES];
+static int ninterfaces;
+
+#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 */
+
+/*
+ * File descriptor masks etc. for call to select
+ */
+fd_set activefds;
+int maxactivefd;
+
+static int create_sockets P((u_int));
+static int open_socket P((struct sockaddr_in *, int, int));
+static void close_socket P((int));
+static void close_file P((int));
+static char * fdbits P((int, fd_set *));
+
+/*
+ * init_io - initialize I/O data structures and call socket creation routine
+ */
+void
+init_io(void)
+{
+#ifdef SYS_WINNT
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ 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 = 0;
+
+#ifdef REFCLOCK
+ refio = 0;
+#endif
+
+#if defined(HAVE_SIGNALED_IO)
+ (void) set_signal();
+#endif
+
+#ifdef SYS_WINNT
+ wVersionRequested = MAKEWORD(1,1);
+ if (WSAStartup(wVersionRequested, &wsaData))
+ {
+ msyslog(LOG_ERR, "No useable winsock.dll: %m");
+ exit(1);
+ }
+#endif /* SYS_WINNT */
+
+ /*
+ * Create the sockets
+ */
+ BLOCKIO();
+ (void) create_sockets(htons(NTP_PORT));
+ UNBLOCKIO();
+
+#ifdef DEBUG
+ if (debug)
+ printf("init_io: maxactivefd %d\n", maxactivefd);
+#endif
+}
+
+/*
+ * 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_int port
+ )
+{
+#if _BSDI_VERSION >= 199510
+ int i, j;
+ struct ifaddrs *ifaddrs, *ifap;
+ struct sockaddr_in resmask;
+#if _BSDI_VERSION < 199701
+ struct ifaddrs *lp;
+ int num_if;
+#endif
+#else /* _BSDI_VERSION >= 199510 */
+# ifdef STREAMS_TLI
+ struct strioctl ioc;
+# endif /* STREAMS_TLI */
+ char buf[MAXINTERFACES*sizeof(struct ifreq)];
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr;
+ int n, i, j, vs, size = 0;
+ struct sockaddr_in resmask;
+#endif /* _BSDI_VERSION >= 199510 */
+
+#ifdef DEBUG
+ if (debug)
+ printf("create_sockets(%d)\n", ntohs( (u_short) port));
+#endif
+
+ /*
+ * create pseudo-interface with wildcard address
+ */
+ inter_list[0].sin.sin_family = AF_INET;
+ inter_list[0].sin.sin_port = port;
+ inter_list[0].sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ (void) strncpy(inter_list[0].name, "wildcard",
+ sizeof(inter_list[0].name));
+ inter_list[0].mask.sin_addr.s_addr = htonl(~ (u_int32)0);
+ inter_list[0].received = 0;
+ inter_list[0].sent = 0;
+ inter_list[0].notsent = 0;
+ inter_list[0].flags = INT_BROADCAST;
+
+#if _BSDI_VERSION >= 199510
+#if _BSDI_VERSION >= 199701
+ if (getifaddrs(&ifaddrs) < 0)
+ {
+ msyslog(LOG_ERR, "getifaddrs: %m");
+ exit(1);
+ }
+ i = 1;
+ for (ifap = ifaddrs; ifap != NULL; ifap = ifap->ifa_next)
+#else
+ if (getifaddrs(&ifaddrs, &num_if) < 0)
+ {
+ msyslog(LOG_ERR, "create_sockets: getifaddrs() failed: %m");
+ exit(1);
+ }
+
+ i = 1;
+
+ for (ifap = ifaddrs, lp = ifap + num_if; ifap < lp; ifap++)
+#endif
+ {
+ struct sockaddr_in *sin;
+
+ if (!ifap->ifa_addr)
+ continue;
+
+ if (ifap->ifa_addr->sa_family != AF_INET)
+ continue;
+
+ if ((ifap->ifa_flags & IFF_UP) == 0)
+ continue;
+
+ if (ifap->ifa_flags & IFF_LOOPBACK)
+ {
+ sin = (struct sockaddr_in *)ifap->ifa_addr;
+ if (ntohl(sin->sin_addr.s_addr) != 0x7f000001)
+ {
+ continue;
+ }
+ }
+
+ inter_list[i].flags = 0;
+ if (ifap->ifa_flags & IFF_BROADCAST)
+ inter_list[i].flags |= INT_BROADCAST;
+
+ (void)strcpy(inter_list[i].name, ifap->ifa_name);
+
+ sin = (struct sockaddr_in *)ifap->ifa_addr;
+ inter_list[i].sin = *sin;
+ inter_list[i].sin.sin_port = port;
+
+ if (ifap->ifa_flags & IFF_LOOPBACK)
+ {
+ inter_list[i].flags = INT_LOOPBACK;
+ if (loopback_interface == NULL
+ || ntohl(sin->sin_addr.s_addr) != 0x7f000001)
+ loopback_interface = &inter_list[i];
+ }
+
+ if (inter_list[i].flags & INT_BROADCAST)
+ {
+ sin = (struct sockaddr_in *)ifap->ifa_broadaddr;
+ inter_list[i].bcast = *sin;
+ inter_list[i].bcast.sin_port = port;
+ }
+
+ if (ifap->ifa_flags & (IFF_LOOPBACK|IFF_POINTOPOINT))
+ {
+ inter_list[i].mask.sin_addr.s_addr = 0xffffffff;
+ }
+ else
+ {
+ sin = (struct sockaddr_in *)ifap->ifa_netmask;
+ inter_list[i].mask = *sin;
+ }
+ inter_list[i].mask.sin_family = AF_INET;
+ inter_list[i].mask.sin_len = sizeof *sin;
+
+ /*
+ * look for an already existing source interface address. If
+ * the machine has multiple point to point interfaces, then
+ * the local address may appear more than once.
+ *
+ * A second problem exists if we have two addresses on
+ * the same network (via "ifconfig alias ..."). Don't
+ * make two xntp interfaces for the two aliases on the
+ * one physical interface. -wsr
+ */
+ for (j=0; j < i; j++)
+ if (inter_list[j].sin.sin_addr.s_addr &
+ inter_list[j].mask.sin_addr.s_addr ==
+ inter_list[i].sin.sin_addr.s_addr &
+ inter_list[i].mask.sin_addr.s_addr)
+ {
+ if (inter_list[j].flags & INT_LOOPBACK)
+ inter_list[j] = inter_list[i];
+ break;
+ }
+ if (j == i)
+ i++;
+ if (i > MAXINTERFACES)
+ break;
+ }
+ free(ifaddrs);
+#else /* _BSDI_VERSION >= 199510 */
+# ifdef USE_STREAMS_DEVICE_FOR_IF_CONFIG
+ if ((vs = open("/dev/ip", O_RDONLY)) < 0)
+ {
+ msyslog(LOG_ERR, "create_sockets: open(/dev/ip) failed: %m");
+ exit(1);
+ }
+# else /* not USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+ if (
+ (vs = socket(AF_INET, SOCK_DGRAM, 0))
+# ifndef SYS_WINNT
+ < 0
+# else /* SYS_WINNT */
+ == INVALID_SOCKET
+# endif /* SYS_WINNT */
+ ) {
+ msyslog(LOG_ERR, "create_sockets: socket(AF_INET, SOCK_DGRAM) failed: %m");
+ exit(1);
+ }
+# endif /* not USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+
+ i = 1;
+# if !defined(SYS_WINNT)
+ ifc.ifc_len = sizeof(buf);
+# endif
+# ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFCONF;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)buf;
+ ioc.ic_len = sizeof(buf);
+ if(ioctl(vs, I_STR, &ioc) < 0 ||
+ ioc.ic_len < sizeof(struct ifreq))
+ {
+ msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFCONF) failed: %m - exiting");
+ exit(1);
+ }
+# ifdef SIZE_RETURNED_IN_BUFFER
+ ifc.ifc_len = ioc.ic_len - sizeof(int);
+ ifc.ifc_buf = buf + sizeof(int);
+# else /* not SIZE_RETURNED_IN_BUFFER */
+ ifc.ifc_len = ioc.ic_len;
+ ifc.ifc_buf = buf;
+# endif /* not SIZE_RETURNED_IN_BUFFER */
+
+# else /* not STREAMS_TLI */
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+# ifndef SYS_WINNT
+ if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0)
+# else
+ if (WSAIoctl(vs, SIO_GET_INTERFACE_LIST, 0, 0, ifc.ifc_buf, ifc.ifc_len, &ifc.ifc_len, 0, 0) == SOCKET_ERROR)
+# endif /* SYS_WINNT */
+{
+ msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFCONF) failed: %m - exiting");
+ exit(1);
+}
+
+# endif /* not STREAMS_TLI */
+
+ for(n = ifc.ifc_len, ifr = ifc.ifc_req; n > 0;
+ ifr = (struct ifreq *)((char *)ifr + size))
+ {
+ extern int listen_to_virtual_ips;
+
+ size = sizeof(*ifr);
+
+# ifdef HAVE_SA_LEN_IN_STRUCT_SOCKADDR
+ if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr))
+ size += ifr->ifr_addr.sa_len - sizeof(struct sockaddr);
+# endif
+ n -= size;
+
+# if !defined(SYS_WINNT)
+ /* Exclude logical interfaces (indicated by ':' in the interface name) */
+ if (debug)
+ printf("interface <%s> ", ifr->ifr_name);
+ if ((listen_to_virtual_ips == 0)
+ && (strchr(ifr->ifr_name, (int)':') != NULL)) {
+ if (debug)
+ printf("ignored\n");
+ continue;
+ }
+ if (debug)
+ printf("OK\n");
+
+ if (
+# ifdef VMS /* VMS+UCX */
+ (((struct sockaddr *)&(ifr->ifr_addr))->sa_family != AF_INET)
+# else
+ (ifr->ifr_addr.sa_family != AF_INET)
+# endif /* VMS+UCX */
+ ) {
+ if (debug)
+ printf("ignoring %s - not AF_INET\n",
+ ifr->ifr_name);
+ continue;
+ }
+# endif /* SYS_WINNT */
+ ifreq = *ifr;
+ inter_list[i].flags = 0;
+ /* is it broadcast capable? */
+# ifndef SYS_WINNT
+# ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFFLAGS;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+ msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFFLAGS) failed: %m");
+ continue;
+ }
+# else /* not STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ if (errno != ENXIO)
+ msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFFLAGS) failed: %m");
+ continue;
+ }
+# endif /* not STREAMS_TLI */
+ if ((ifreq.ifr_flags & IFF_UP) == 0) {
+ if (debug)
+ printf("ignoring %s - interface not UP\n",
+ ifr->ifr_name);
+ continue;
+ }
+ inter_list[i].flags = 0;
+ if (ifreq.ifr_flags & IFF_BROADCAST)
+ inter_list[i].flags |= INT_BROADCAST;
+# endif /* not SYS_WINNT */
+# if !defined(SUN_3_3_STINKS)
+ if (
+# if defined(IFF_LOCAL_LOOPBACK) /* defined(SYS_HPUX) && (SYS_HPUX < 8) */
+ (ifreq.ifr_flags & IFF_LOCAL_LOOPBACK)
+# elif defined(IFF_LOOPBACK)
+ (ifreq.ifr_flags & IFF_LOOPBACK)
+# else /* not IFF_LOCAL_LOOPBACK and not IFF_LOOPBACK */
+ /* test against 127.0.0.1 (yuck!!) */
+ (inter_list[i].sin.sin_addr.s_addr == inet_addr("127.0.0.1"))
+# endif /* not IFF_LOCAL_LOOPBACK and not IFF_LOOPBACK */
+ )
+ {
+# ifndef SYS_WINNT
+ inter_list[i].flags |= INT_LOOPBACK;
+# endif /* not SYS_WINNT */
+ if (loopback_interface == 0)
+ {
+ loopback_interface = &inter_list[i];
+ }
+ }
+# endif /* not SUN_3_3_STINKS */
+
+#if 0
+# ifndef SYS_WINNT
+# ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFADDR;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if (ioctl(vs, I_STR, &ioc))
+ {
+ msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFADDR) failed: %m");
+ continue;
+ }
+# else /* not STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFADDR, (char *)&ifreq) < 0)
+ {
+ if (errno != ENXIO)
+ msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFADDR) failed: %m");
+ continue;
+ }
+# endif /* not STREAMS_TLI */
+# endif /* not SYS_WINNT */
+#endif /* 0 */
+# if defined(SYS_WINNT)
+ {int TODO_FillInTheNameWithSomeThingReasonble;}
+# else
+ (void)strncpy(inter_list[i].name, ifreq.ifr_name,
+ sizeof(inter_list[i].name));
+# endif
+ inter_list[i].sin = *(struct sockaddr_in *)&ifr->ifr_addr;
+ inter_list[i].sin.sin_family = AF_INET;
+ inter_list[i].sin.sin_port = port;
+
+# if defined(SUN_3_3_STINKS)
+ /*
+ * Oh, barf! I'm too disgusted to even explain this
+ */
+ if (SRCADR(&inter_list[i].sin) == 0x7f000001)
+ {
+ inter_list[i].flags |= INT_LOOPBACK;
+ if (loopback_interface == 0)
+ loopback_interface = &inter_list[i];
+ }
+# endif /* SUN_3_3_STINKS */
+# if !defined SYS_WINNT && !defined SYS_CYGWIN32 /* no interface flags on NT */
+ if (inter_list[i].flags & INT_BROADCAST) {
+# ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFBRDADDR;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+ msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFBRDADDR) failed: %m");
+ exit(1);
+ }
+# else /* not STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFBRDADDR) failed: %m");
+ exit(1);
+ }
+# endif /* not STREAMS_TLI */
+
+# ifndef ifr_broadaddr
+ inter_list[i].bcast =
+ *(struct sockaddr_in *)&ifreq.ifr_addr;
+# else
+ inter_list[i].bcast =
+ *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
+# endif /* ifr_broadaddr */
+ inter_list[i].bcast.sin_family = AF_INET;
+ inter_list[i].bcast.sin_port = port;
+ }
+
+# ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFNETMASK;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+ msyslog(LOG_ERR, "create_sockets: ioctl(I_STR:SIOCGIFNETMASK) failed: %m");
+ exit(1);
+ }
+# else /* not STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+ msyslog(LOG_ERR, "create_sockets: ioctl(SIOCGIFNETMASK) failed: %m");
+ exit(1);
+ }
+# endif /* not STREAMS_TLI */
+ inter_list[i].mask = *(struct sockaddr_in *)&ifreq.ifr_addr;
+# else
+ /* winnt here */
+ inter_list[i].bcast = ifreq.ifr_broadaddr;
+ inter_list[i].bcast.sin_family = AF_INET;
+ inter_list[i].bcast.sin_port = port;
+ inter_list[i].mask = ifreq.ifr_mask;
+# endif /* not SYS_WINNT */
+
+ /*
+ * look for an already existing source interface address. If
+ * the machine has multiple point to point interfaces, then
+ * the local address may appear more than once.
+ */
+ for (j=0; j < i; j++)
+ if (inter_list[j].sin.sin_addr.s_addr ==
+ inter_list[i].sin.sin_addr.s_addr) {
+ break;
+ }
+ if (j == i)
+ i++;
+ if (i > MAXINTERFACES)
+ break;
+ }
+ closesocket(vs);
+#endif /* _BSDI_VERSION >= 199510 */
+
+ ninterfaces = i;
+ maxactivefd = 0;
+ FD_ZERO(&activefds);
+ for (i = 0; i < ninterfaces; i++) {
+ inter_list[i].fd =
+ open_socket(&inter_list[i].sin,
+ inter_list[i].flags & INT_BROADCAST, 0);
+ }
+
+ /*
+ * Now that we have opened all the sockets, turn off the reuse flag for
+ * security.
+ */
+ for (i = 0; i < ninterfaces; i++) {
+ int off = 0;
+
+ /*
+ * if inter_list[ n ].fd is -1, we might have a adapter
+ * configured but not present
+ */
+ if ( inter_list[ i ].fd != -1 ) {
+ if (setsockopt(inter_list[i].fd, SOL_SOCKET,
+ SO_REUSEADDR, (char *)&off,
+ sizeof(off))) {
+ msyslog(LOG_ERR, "create_sockets: setsockopt(SO_REUSEADDR,off) failed: %m");
+ }
+ }
+ }
+
+#if defined(MCAST)
+ /*
+ * enable possible multicast reception on the broadcast socket
+ */
+ inter_list[0].bcast.sin_addr.s_addr = htonl(INADDR_ANY);
+ inter_list[0].bcast.sin_family = AF_INET;
+ inter_list[0].bcast.sin_port = port;
+#endif /* MCAST */
+
+ /*
+ * Blacklist all bound interface addresses
+ */
+ resmask.sin_addr.s_addr = ~ (u_int32)0;
+ for (i = 1; i < ninterfaces; i++)
+ hack_restrict(RESTRICT_FLAGS, &inter_list[i].sin, &resmask,
+ RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE);
+
+ any_interface = &inter_list[0];
+#ifdef DEBUG
+ if (debug > 2) {
+ 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",
+ inet_ntoa((inter_list[i].sin.sin_addr)));
+ if (inter_list[i].flags & INT_BROADCAST)
+ printf(" bcast=%s,",
+ inet_ntoa((inter_list[i].bcast.sin_addr)));
+ printf(" mask=%s\n",
+ inet_ntoa((inter_list[i].mask.sin_addr)));
+ }
+ }
+#endif
+#if defined (HAVE_IO_COMPLETION_PORT)
+ for (i = 0; i < ninterfaces; i++) {
+ io_completion_port_add_socket(&inter_list[i]);
+ }
+#endif
+ return ninterfaces;
+}
+
+/*
+ * io_setbclient - open the broadcast client sockets
+ */
+void
+io_setbclient(void)
+{
+ int i;
+
+ for (i = 1; i < ninterfaces; i++)
+ {
+ if (!(inter_list[i].flags & INT_BROADCAST))
+ continue;
+ 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);
+ inter_list[i].flags |= INT_BCASTOPEN;
+#endif
+ }
+}
+
+
+/*
+ * io_multicast_add() - add multicast group address
+ */
+void
+io_multicast_add(
+ u_int32 addr
+ )
+{
+#ifdef MCAST
+ struct ip_mreq mreq;
+ int i = ninterfaces; /* Use the next interface */
+ u_int32 haddr = ntohl(addr);
+ struct in_addr iaddr;
+ int s;
+ struct sockaddr_in *sinp;
+
+ iaddr.s_addr = addr;
+
+ if (!IN_CLASSD(haddr))
+ {
+ msyslog(LOG_ERR,
+ "cannot add multicast address %s as it is not class D",
+ inet_ntoa(iaddr));
+ return;
+ }
+
+ for (i = 0; i < ninterfaces; i++)
+ {
+ /* Already have this address */
+ if (inter_list[i].sin.sin_addr.s_addr == addr) return;
+ /* found a free slot */
+ if (inter_list[i].sin.sin_addr.s_addr == 0 &&
+ inter_list[i].fd <= 0 && inter_list[i].bfd <= 0 &&
+ inter_list[i].flags == 0) break;
+ }
+ sinp = &(inter_list[i].sin);
+
+ memset((char *)&mreq, 0, sizeof(mreq));
+ memset((char *)&inter_list[i], 0, sizeof inter_list[0]);
+ sinp->sin_family = AF_INET;
+ sinp->sin_addr = iaddr;
+ sinp->sin_port = htons(123);
+
+ s = open_socket(sinp, 0, 1);
+ /* Try opening a socket for the specified class D address */
+ /* This works under SunOS 4.x, but not OSF1 .. :-( */
+ if (s < 0)
+ {
+ memset((char *)&inter_list[i], 0, sizeof inter_list[0]);
+ i = 0;
+ /* HACK ! -- stuff in an address */
+ inter_list[i].bcast.sin_addr.s_addr = addr;
+ msyslog(LOG_ERR, "...multicast address %s using wildcard socket",
+ inet_ntoa(iaddr));
+ }
+ else
+ {
+ inter_list[i].fd = s;
+ inter_list[i].bfd = -1;
+ (void) strncpy(inter_list[i].name, "multicast",
+ sizeof(inter_list[i].name));
+ inter_list[i].mask.sin_addr.s_addr = htonl(~(u_int32)0);
+ }
+
+ /*
+ * 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)
+ msyslog(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;
+ if (i >= ninterfaces) ninterfaces = i+1;
+#else /* MCAST */
+ struct in_addr iaddr;
+
+ iaddr.s_addr = addr;
+ msyslog(LOG_ERR, "cannot add multicast address %s as no MCAST support",
+ inet_ntoa(iaddr));
+#endif /* MCAST */
+}
+
+/*
+ * io_unsetbclient - close the broadcast client sockets
+ */
+void
+io_unsetbclient(void)
+{
+ int i;
+
+ for (i = 1; i < ninterfaces; i++)
+ {
+ if (!(inter_list[i].flags & INT_BCASTOPEN))
+ continue;
+ close_socket(inter_list[i].bfd);
+ inter_list[i].bfd = -1;
+ inter_list[i].flags &= ~INT_BCASTOPEN;
+ }
+}
+
+
+/*
+ * io_multicast_del() - delete multicast group address
+ */
+void
+io_multicast_del(
+ u_int32 addr
+ )
+{
+#ifdef MCAST
+ int i;
+ struct ip_mreq mreq;
+ u_int32 haddr = ntohl(addr);
+ struct sockaddr_in sinaddr;
+
+ if (!IN_CLASSD(haddr))
+ {
+ sinaddr.sin_addr.s_addr = addr;
+ msyslog(LOG_ERR,
+ "invalid multicast address %s", ntoa(&sinaddr));
+ return;
+ }
+
+ /*
+ * Disable reception of multicast packets
+ */
+ mreq.imr_multiaddr.s_addr = addr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ for (i = 0; i < ninterfaces; i++)
+ {
+ if (!(inter_list[i].flags & INT_MULTICAST))
+ continue;
+ if (!(inter_list[i].fd < 0))
+ continue;
+ if (addr != inter_list[i].sin.sin_addr.s_addr)
+ continue;
+ if (i != 0)
+ {
+ /* we have an explicit fd, so we can close it */
+ close_socket(inter_list[i].fd);
+ memset((char *)&inter_list[i], 0, sizeof inter_list[0]);
+ inter_list[i].fd = -1;
+ inter_list[i].bfd = -1;
+ }
+ 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)
+ msyslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails: %m");
+ /* This is **WRONG** -- there may be others ! */
+ /* There should be a count of users ... */
+ inter_list[i].flags &= ~INT_MULTICAST;
+ }
+ }
+#else /* not MCAST */
+ msyslog(LOG_ERR, "this function requires multicast kernel");
+#endif /* not MCAST */
+}
+
+
+/*
+ * open_socket - open a socket, returning the file descriptor
+ */
+static int
+open_socket(
+ struct sockaddr_in *addr,
+ int flags,
+ int turn_off_reuse
+ )
+{
+ int fd;
+ int on = 1, off = 0;
+
+ /* create a datagram (UDP) socket */
+ if ( (fd = socket(AF_INET, SOCK_DGRAM, 0))
+#ifndef SYS_WINNT
+ < 0
+#else
+ == INVALID_SOCKET
+#endif /* SYS_WINNT */
+ )
+ {
+ msyslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ /* 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)))
+ {
+ msyslog(LOG_ERR, "setsockopt SO_REUSEADDR on fails: %m");
+ }
+
+ /*
+ * bind the local address.
+ */
+ if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
+ char buff[160];
+ sprintf(buff,
+ "bind() fd %d, family %d, port %d, addr %s, in_classd=%d flags=%d fails: %%m",
+ fd, addr->sin_family, (int)ntohs(addr->sin_port),
+ ntoa(addr),
+ IN_CLASSD(ntohl(addr->sin_addr.s_addr)), flags);
+ msyslog(LOG_ERR, buff);
+ closesocket(fd);
+
+ /*
+ * soft fail if opening a class D address
+ */
+ if (IN_CLASSD(ntohl(addr->sin_addr.s_addr)))
+ return -1;
+#if 0
+ exit(1);
+#else
+ return -1;
+#endif
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("bind() fd %d, family %d, port %d, addr %s, flags=%d\n",
+ fd,
+ addr->sin_family,
+ (int)ntohs(addr->sin_port),
+ ntoa(addr),
+ flags);
+#endif
+ if (fd > maxactivefd)
+ maxactivefd = fd;
+ FD_SET(fd, &activefds);
+
+ /*
+ * 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)
+ {
+ msyslog(LOG_ERR, "fcntl(O_NONBLOCK) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(FNDELAY)
+ if (fcntl(fd, F_SETFL, FNDELAY) < 0)
+ {
+ msyslog(LOG_ERR, "fcntl(FNDELAY) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(O_NDELAY) /* generally the same as FNDELAY */
+ if (fcntl(fd, F_SETFL, O_NDELAY) < 0)
+ {
+ msyslog(LOG_ERR, "fcntl(O_NDELAY) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(FIONBIO)
+ if (
+# if defined(VMS)
+ (ioctl(fd,FIONBIO,&1) < 0)
+# elif defined(SYS_WINNT)
+ (ioctlsocket(fd,FIONBIO,(u_long *) &on) == SOCKET_ERROR)
+# else
+ (ioctl(fd,FIONBIO,&on) < 0)
+# endif
+ )
+ {
+ msyslog(LOG_ERR, "ioctl(FIONBIO) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#elif defined(FIOSNBIO)
+ if (ioctl(fd,FIOSNBIO,&on) < 0)
+ {
+ msyslog(LOG_ERR, "ioctl(FIOSNBIO) fails: %m");
+ 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)))
+ {
+ msyslog(LOG_ERR, "setsockopt SO_REUSEADDR off fails: %m");
+ }
+
+#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)))
+ {
+ msyslog(LOG_ERR, "setsockopt(SO_BROADCAST): %m");
+ }
+ }
+#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(
+ int fd
+ )
+{
+ int i, newmax;
+
+ (void) closesocket(fd);
+ 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;
+ }
+}
+
+
+/*
+ * close_file - close a file and remove from the activefd list
+ * added 1/31/1997 Greg Schueman for Windows NT portability
+ */
+static void
+close_file(
+ int fd
+ )
+{
+ int i, newmax;
+
+ (void) close(fd);
+ 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;
+ }
+}
+
+
+/*
+ * findbcastinter - find broadcast interface corresponding to address
+ */
+struct interface *
+findbcastinter(
+ struct sockaddr_in *addr
+ )
+{
+#if defined(SIOCGIFCONF) || defined(SYS_WINNT)
+ register int i;
+ register u_int32 netnum;
+
+ netnum = NSRCADR(addr);
+ for (i = 1; i < ninterfaces; i++)
+ {
+ if (!(inter_list[i].flags & INT_BROADCAST))
+ continue;
+ if (NSRCADR(&inter_list[i].bcast) == netnum)
+ return &inter_list[i];
+ if ((NSRCADR(&inter_list[i].sin) & NSRCADR(&inter_list[i].mask))
+ == (netnum & NSRCADR(&inter_list[i].mask)))
+ return &inter_list[i];
+ }
+#endif /* SIOCGIFCONF */
+ return any_interface;
+}
+
+
+/* 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_in *dest,
+ struct interface *inter,
+ int ttl,
+ struct pkt *pkt,
+ int len
+ )
+{
+ int cc, slot;
+#ifdef SYS_WINNT
+ DWORD err;
+#endif /* SYS_WINNT */
+
+ /*
+ * Send error cache. Empty slots have port == 0
+ * Set ERRORCACHESIZE to 0 to disable
+ */
+ struct cache {
+ u_short port;
+ struct in_addr addr;
+ };
+
+#ifndef ERRORCACHESIZE
+#define ERRORCACHESIZE 8
+#endif
+#if ERRORCACHESIZE > 0
+ static struct cache badaddrs[ERRORCACHESIZE];
+#else
+#define badaddrs ((struct cache *)0) /* Only used in empty loops! */
+#endif
+
+ /*
+ * check if the source address is a multicast address - replace
+ * interface with any-interface if so.
+ */
+ if (IN_MULTICAST(ntohl(inter->sin.sin_addr.s_addr)))
+ inter = any_interface;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("%ssendpkt(fd=%d dst=%s, src=%s, ttl=%d, len=%d)\n",
+ (ttl >= 0) ? "\tMCAST\t*****" : "",
+ inter->fd, ntoa(dest),
+ ntoa(&inter->sin), ttl, len);
+#endif
+
+#ifdef MCAST
+ /* for the moment we use the bcast option to set multicast ttl */
+ if (ttl >= 0 && ttl != inter->last_ttl)
+ {
+ char mttl = ttl;
+
+ /* set the multicast ttl for outgoing packets */
+ if (setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &mttl, sizeof(mttl)) == -1)
+ {
+ msyslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails: %m");
+ }
+ else inter->last_ttl = ttl;
+ }
+#endif /* MCAST */
+
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs[slot].port == dest->sin_port &&
+ badaddrs[slot].addr.s_addr == dest->sin_addr.s_addr)
+ break;
+
+#if defined(HAVE_IO_COMPLETION_PORT)
+ err = io_completion_port_sendto(inter, pkt, len, dest);
+ if (err != ERROR_SUCCESS)
+#else
+ cc = sendto(inter->fd, (char *)pkt, len, 0, (struct sockaddr *)dest,
+ sizeof(struct sockaddr_in));
+ if (cc == -1)
+#endif
+ {
+ inter->notsent++;
+ packets_notsent++;
+#if defined(HAVE_IO_COMPLETION_PORT)
+ if (err != WSAEWOULDBLOCK && err != WSAENOBUFS && slot < 0)
+#else
+ if (errno != EWOULDBLOCK && errno != ENOBUFS && slot < 0)
+#endif
+ {
+ /*
+ * Remember this, if there's an empty slot
+ */
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs[slot].port == 0)
+ {
+ badaddrs[slot].port = dest->sin_port;
+ badaddrs[slot].addr = dest->sin_addr;
+ break;
+ }
+ msyslog(LOG_ERR, "sendto(%s): %m", ntoa(dest));
+ }
+ }
+ else
+ {
+ inter->sent++;
+ packets_sent++;
+ /*
+ * He's not bad any more
+ */
+ if (slot >= 0)
+ {
+ msyslog(LOG_INFO, "Connection re-established to %s", ntoa(dest));
+ badaddrs[slot].port = 0;
+ }
+ }
+}
+
+#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
+ */
+extern void
+input_handler(
+ l_fp *cts
+ )
+{
+ register int i, n;
+ register struct recvbuf *rb;
+ register int doing;
+ register int 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];
+
+#ifndef SYS_WINNT
+ (void) read(fd, buf, sizeof buf);
+#else
+ (void) ReadFile((HANDLE)fd, buf, (DWORD)sizeof buf, NULL, NULL);
+#endif /* SYS_WINNT */
+ 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;
+#ifndef SYS_WINNT
+ rb->recv_length =
+ read(fd, (char *)&rb->recv_space, (unsigned)i)
+#else /* SYS_WINNT */
+ ReadFile((HANDLE)fd, (char *)&rb->recv_space, (DWORD)i,
+ (LPDWORD)&(rb->recv_length), NULL)
+#endif /* SYS_WINNT */
+ ;
+
+ if (rb->recv_length == -1)
+ {
+ msyslog(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 == 0) || (free_recvbuffs() == 0))
+#endif
+ )
+ {
+ char buf[RX_BUFF_SIZE];
+ struct sockaddr from;
+
+ fromlen = sizeof from;
+ (void) recvfrom(fd, buf, sizeof(buf), 0, &from, &fromlen);
+#ifdef DEBUG
+ if (debug)
+ printf("%s on %d(%lu) fd=%d from %s\n",
+ (i) ? "drop" : "ignore",
+ i, free_recvbuffs(), fd,
+ inet_ntoa(((struct sockaddr_in *) &from)->sin_addr));
+#endif
+ if (i == 0)
+ packets_ignored++;
+ else
+ packets_dropped++;
+ goto select_again;
+ }
+
+ rb = get_free_recv_buffer();
+
+ fromlen = sizeof(struct sockaddr_in);
+ 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)
+ {
+ msyslog(LOG_ERR, "recvfrom() fd=%d: %m", fd);
+#ifdef DEBUG
+ if (debug)
+ printf("input_handler: fd=%d dropped (bad recvfrom)\n", fd);
+#endif
+ freerecvbuf(rb);
+ continue;
+ }
+#ifdef DEBUG
+ if (debug > 2)
+ printf("input_handler: fd=%d length %d from %08lx %s\n",
+ fd, rb->recv_length,
+ (u_long)ntohl(rb->recv_srcadr.sin_addr.s_addr) &
+ 0x00000000ffffffff,
+ inet_ntoa(rb->recv_srcadr.sin_addr));
+#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)
+ msyslog(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)
+ msyslog(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)
+ {
+#ifndef SYS_WINNT
+ int err = errno;
+#else
+ DWORD err = WSAGetLastError();
+#endif /* SYS_WINNT */
+
+ /*
+ * extended FAU debugging output
+ */
+ msyslog(LOG_ERR, "select(%d, %s, 0L, 0L, &0.000000) error: %m",
+ maxactivefd+1, fdbits(maxactivefd, &activefds));
+ if (
+#ifndef SYS_WINNT
+ (err == EBADF)
+#else
+ (err == WSAEBADF)
+#endif /* SYS_WINNT */
+ )
+ {
+ int j, b;
+
+ fds = activefds;
+ for (j = 0; j <= maxactivefd; j++)
+ if (
+#ifndef SYS_WINNT
+ (FD_ISSET(j, &fds) && (read(j, &b, 0) == -1))
+#else
+ (FD_ISSET(j, &fds) && (!ReadFile((HANDLE)j, &b, 0, NULL, NULL)))
+#endif /* SYS_WINNT */
+ )
+ msyslog(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 - utility used by other modules to find an interface
+ * given an address.
+ */
+struct interface *
+findinterface(
+ struct sockaddr_in *addr
+ )
+{
+ register int i;
+ register u_int32 saddr;
+
+ /*
+ * Just match the address portion.
+ */
+ saddr = addr->sin_addr.s_addr;
+ for (i = 0; i < ninterfaces; i++)
+ {
+ if (inter_list[i].sin.sin_addr.s_addr == saddr)
+ return &inter_list[i];
+ }
+ return (struct interface *)0;
+}
+
+
+/*
+ * 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;
+
+ if (rio->fd > maxactivefd)
+ maxactivefd = rio->fd;
+ FD_SET(rio->fd, &activefds);
+ 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))
+ {
+ refio = rio->next;
+ UNBLOCKIO();
+ return 0;
+ }
+# endif
+
+ if (rio->fd > maxactivefd)
+ maxactivefd = rio->fd;
+ FD_SET(rio->fd, &activefds);
+
+ 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 */
+
+void
+kill_asyncio(void)
+{
+ int i;
+
+ BLOCKIO();
+ for (i = 0; i <= maxactivefd; i++)
+ (void)close_socket(i);
+}
diff --git a/contrib/ntp/ntpd/ntp_loopfilter.c b/contrib/ntp/ntpd/ntp_loopfilter.c
new file mode 100644
index 0000000..6e0e134
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_loopfilter.c
@@ -0,0 +1,847 @@
+/*
+ * ntp_loopfilter.c - implements the NTP loop filter algorithm
+ *
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+
+#include <signal.h>
+#include <setjmp.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.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 max offset (s) */
+#define CLOCK_PANIC 1000. /* default panic offset (s) */
+#define CLOCK_MAXSTAB 2e-6 /* max frequency stability */
+#define CLOCK_MAXERR 1e-2 /* max phase jitter (s) */
+#define SHIFT_PLL 4 /* PLL loop gain (shift) */
+#define CLOCK_AVG 4. /* FLL loop gain */
+#define CLOCK_MINSEC 256. /* min FLL update interval (s) */
+#define CLOCK_MINSTEP 900. /* step-change timeout (s) */
+#define CLOCK_DAY 86400. /* one day of seconds */
+#define CLOCK_LIMIT 30 /* poll-adjust threshold */
+#define CLOCK_PGATE 4. /* poll-adjust gate */
+#define CLOCK_ALLAN 1024. /* min Allan intercept (s) */
+#define CLOCK_ADF 1e11 /* Allan deviation factor */
+
+/*
+ * Clock discipline state machine. This is used to control the
+ * synchronization behavior during initialization and following a
+ * timewarp.
+ */
+#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_update 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_update 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.
+ */
+#define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */
+
+/*
+ * Program variables
+ */
+static double clock_offset; /* clock offset adjustment (s) */
+double drift_comp; /* clock frequency (ppm) */
+double clock_stability; /* clock stability (ppm) */
+double clock_max = CLOCK_MAX; /* max offset allowed before step (s) */
+static double clock_panic = CLOCK_PANIC; /* max offset allowed before panic */
+u_long pps_control; /* last pps sample time */
+static void rstclock P((int)); /* state transition function */
+
+#ifdef KERNEL_PLL
+int pll_status; /* status bits for kernel pll */
+#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 ext_enable; /* external clock enabled */
+int pps_update; /* pps update valid */
+int allow_set_backward = TRUE; /* step corrections allowed */
+int correct_any = FALSE; /* corrections > 1000 s allowed */
+
+#ifdef STA_NANO
+int pll_nano; /* nanosecond kernel switch */
+#endif /* STA_NANO */
+
+/*
+ * Clock state machine variables
+ */
+u_char sys_poll; /* log2 of system poll interval */
+int state; /* clock discipline state */
+int tc_counter; /* poll-adjust counter */
+u_long last_time; /* time of last clock update (s) */
+double last_offset; /* last clock offset (s) */
+double allan_xpt; /* Allan intercept (s) */
+double sys_error; /* system standard error (s) */
+
+#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)
+static void pll_trap P((int)); /* configuration trap */
+#ifdef SIGSYS
+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);
+}
+
+/*
+ * 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.
+ */
+int
+local_clock(
+ struct peer *peer, /* synch source peer structure */
+ double fp_offset, /* clock offset (s) */
+ double epsil /* jittter (square s*s) */
+ )
+{
+ double 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 defined(KERNEL_PLL)
+ struct timex ntv; /* kernel interface structure */
+#endif /* KERNEL_PLL */
+
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: offset %.6f jitter %.6f state %d\n",
+ fp_offset, SQRT(epsil), state);
+#endif
+ if (!ntp_enable)
+ return(0);
+
+ /*
+ * If the clock is way off, don't tempt fate by correcting it.
+ */
+#ifndef SYS_WINNT
+ if (fabs(fp_offset) >= clock_panic && !correct_any) {
+ msyslog(LOG_ERR,
+ "time error %.0f over %d seconds; set clock manually)",
+ fp_offset, (int)clock_panic);
+ return (-1);
+ }
+#endif
+ /*
+ * 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) {
+ step_systime(fp_offset);
+ NLOG(NLOG_SYNCEVENT|NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE, "time set %.6f s", fp_offset);
+ rstclock(S_TSET);
+ rstclock(S_FREQ);
+ return (1);
+ }
+
+ /*
+ * Update the jitter estimate.
+ */
+ oerror = sys_error;
+ dtemp = SQUARE(sys_error);
+ sys_error = SQRT(dtemp + (epsil - dtemp) / CLOCK_AVG);
+
+ /*
+ * 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
+ * phase error exceeds the maximum allowed for ordinary tracking
+ * and otherwise when it does not.
+ */
+ retval = 0;
+ clock_frequency = flladj = plladj = 0;
+ mu = current_time - last_time;
+ if (fabs(fp_offset) > clock_max) {
+ 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:
+ rstclock(S_FREQ);
+ last_offset = clock_offset = fp_offset;
+ return (0);
+
+ /*
+ * In S_SYNC state we ignore outlyers. At the first
+ * outlyer after CLOCK_MINSTEP (900 s), switch to S_SPIK
+ * state.
+ */
+ case S_SYNC:
+ if (mu < CLOCK_MINSTEP)
+ return (0);
+ rstclock(S_SPIK);
+ return (0);
+
+ /*
+ * In S_FREQ state we ignore outlyers. At the first
+ * outlyer after CLOCK_MINSTEP (900 s), compute the
+ * apparent phase and frequency correction.
+ */
+ case S_FREQ:
+ if (mu < CLOCK_MINSTEP)
+ return (0);
+ clock_frequency = (fp_offset - clock_offset) /
+ mu;
+ /* fall through to default */
+
+ /*
+ * 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:
+ if (allow_set_backward) {
+ step_systime(fp_offset);
+ NLOG(NLOG_SYNCEVENT|NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE, "time reset %.6f s",
+ fp_offset);
+ rstclock(S_TSET);
+ retval = 1;
+ } else {
+ NLOG(NLOG_SYNCEVENT|NLOG_SYSEVENT)
+ msyslog(LOG_NOTICE, "time slew %.6f s",
+ fp_offset);
+ rstclock(S_FREQ);
+ last_offset = clock_offset = fp_offset;
+ return (0);
+ }
+ break;
+ }
+ } else {
+ switch (state) {
+
+ /*
+ * If this is the first update, initialize the
+ * discipline parameters and pretend we had just set the
+ * clock. We don't want to step the clock unless we have
+ * to.
+ */
+ case S_FSET:
+ rstclock(S_TSET);
+ last_offset = clock_offset = fp_offset;
+ return (0);
+
+ /*
+ * In S_FREQ state we ignore updates until CLOCK_MINSTEP
+ * (900 s). 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;
+ clock_offset = fp_offset;
+ rstclock(S_SYNC);
+ 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:
+ rstclock(S_SYNC);
+ /* fall through to default */
+
+ /*
+ * We come here in the normal case for linear phase and
+ * frequency adjustments. If the offset exceeds the
+ * previous time error estimate by CLOCK_SGATE and the
+ * interval since the last update is less than twice the
+ * poll interval, consider the update a popcorn spike
+ * and ignore it.
+ */
+ default:
+ if (fabs(fp_offset - last_offset) >
+ CLOCK_SGATE * oerror && mu <
+ ULOGTOD(sys_poll + 1)) {
+#ifdef DEBUG
+ if (debug)
+ printf("local_clock: popcorn %.6f %.6f\n",
+ fp_offset, last_offset);
+#endif
+ last_offset = fp_offset;
+ return (0);
+ }
+
+ /*
+ * Compute the FLL and PLL frequency adjustments
+ * conditioned on two weighting factors, one
+ * which limits the time constant determined
+ * from the Allan intercept, the other which
+ * limits the gain factor as a function of
+ * update interval. The net effect is to favor
+ * the PLL adjustments at the smaller update
+ * intervals and the FLL adjustments at the
+ * larger ones.
+ */
+ dtemp = max(mu, allan_xpt);
+ etemp = min(max(0, mu - CLOCK_MINSEC) /
+ CLOCK_ALLAN, 1.);
+ flladj = fp_offset * etemp / (dtemp *
+ CLOCK_AVG);
+ dtemp = ULOGTOD(SHIFT_PLL + 2 + sys_poll);
+ plladj = fp_offset * mu / (dtemp * dtemp);
+ clock_offset = fp_offset;
+ break;
+ }
+ }
+
+ /*
+ * 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 defined(KERNEL_PLL)
+ 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((char *)&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;
+#ifdef STA_NANO
+ if (pll_nano)
+ ntv.offset = (int32)(clock_offset *
+ 1e9 + dtemp);
+ else
+#endif /* STA_NANO */
+ ntv.offset = (int32)(clock_offset *
+ 1e6 + dtemp);
+ if (clock_frequency != 0) {
+ ntv.modes |= MOD_FREQUENCY;
+ ntv.freq = (int32)((clock_frequency +
+ drift_comp) * 65536e6);
+ }
+#ifdef STA_NANO
+ ntv.constant = sys_poll;
+#else
+ ntv.constant = sys_poll - 4;
+#endif /* STA_NANO */
+ ntv.esterror = (u_int32)(sys_error * 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;
+ }
+
+ /*
+ * Wiggle the PPS bits according to the health of the
+ * prefer peer.
+ */
+ if (pll_status & STA_PPSSIGNAL)
+ ntv.status |= STA_PPSFREQ;
+ if (pll_status & STA_PPSFREQ && pps_update)
+ ntv.status |= STA_PPSTIME;
+
+ /*
+ * Update the offset and frequency from the kernel
+ * variables.
+ */
+ if (ntp_adjtime(&ntv) == TIME_ERROR) {
+ if (ntv.status != pll_status)
+ msyslog(LOG_ERR,
+ "kernel pll status change %x",
+ ntv.status);
+ }
+ pll_status = ntv.status;
+#ifdef STA_NANO
+ if (pll_nano)
+ clock_offset = ntv.offset / 1e9;
+ else
+#endif /* STA_NANO */
+ clock_offset = ntv.offset / 1e6;
+#ifdef STA_NANO
+ sys_poll = ntv.constant;
+#else
+ sys_poll = ntv.constant + 4;
+#endif /* STA_NANO */
+ clock_frequency = ntv.freq / 65536e6 - drift_comp;
+ flladj = plladj = 0;
+
+ /*
+ * If the kernel pps discipline is working, monitor its
+ * performance.
+ */
+ if (ntv.status & STA_PPSTIME) {
+ if (!pps_control)
+ NLOG(NLOG_SYSEVENT)msyslog(LOG_INFO,
+ "pps sync enabled");
+ pps_control = current_time;
+#ifdef STA_NANO
+ if (pll_nano)
+ record_peer_stats(
+ &loopback_interface->sin,
+ ctlsysstatus(), ntv.offset / 1e9,
+ 0., ntv.jitter / 1e9, 0.);
+ else
+#endif /* STA_NANO */
+ record_peer_stats(
+ &loopback_interface->sin,
+ ctlsysstatus(), ntv.offset / 1e6,
+ 0., ntv.jitter / 1e6, 0.);
+ }
+ }
+#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.
+ */
+ etemp = clock_frequency + flladj + plladj;
+ drift_comp += etemp;
+ if (drift_comp > sys_maxfreq)
+ drift_comp = sys_maxfreq;
+ else if (drift_comp <= -sys_maxfreq)
+ drift_comp = -sys_maxfreq;
+ dtemp = SQUARE(clock_stability);
+ etemp = SQUARE(etemp) - dtemp;
+ clock_stability = SQRT(dtemp + etemp / CLOCK_AVG);
+ allan_xpt = max(CLOCK_ALLAN, clock_stability * CLOCK_ADF);
+
+ /*
+ * In SYNC state, adjust the poll interval.
+ */
+ if (state == S_SYNC) {
+ if (clock_stability < CLOCK_MAXSTAB &&
+ fabs(clock_offset) < CLOCK_PGATE * sys_error) {
+ 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.
+ */
+ last_time = current_time;
+ last_offset = clock_offset;
+ dtemp = peer->disp + SQRT(peer->variance + SQUARE(sys_error));
+ if ((peer->flags & FLAG_REFCLOCK) == 0 && dtemp < MINDISPERSE)
+ dtemp = MINDISPERSE;
+ sys_rootdispersion = peer->rootdispersion + dtemp;
+ (void)record_loop_stats();
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: mu %.0f allan %.0f fadj %.3f fll %.3f pll %.3f\n",
+ mu, allan_xpt, clock_frequency * 1e6, flladj * 1e6,
+ plladj * 1e6);
+#endif /* DEBUG */
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "local_clock: jitter %.6f freq %.3f stab %.3f poll %d count %d\n",
+ sys_error, drift_comp * 1e6, clock_stability * 1e6,
+ sys_poll, tc_counter);
+#endif /* DEBUG */
+ return (retval);
+}
+
+
+/*
+ * adj_host_clock - Called once every second to update the local clock.
+ */
+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;
+
+ /*
+ * 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_SYSEVENT) /* conditional if clause */
+ msyslog(LOG_INFO, "pps sync disabled");
+ pps_control = 0;
+ }
+ if (!ntp_enable)
+ return;
+
+ /*
+ * If the phase-lock loop is implemented in the kernel, we
+ * have no business going further.
+ */
+ if (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;
+ }
+ adjustment = clock_offset / ULOGTOD(SHIFT_PLL + sys_poll);
+ clock_offset -= adjustment;
+ adj_systime(adjustment + drift_comp);
+}
+
+
+/*
+ * Clock state machine. Enter new state and set state variables.
+ */
+static void
+rstclock(
+ int trans /* new state */
+ )
+{
+ state = trans;
+ switch (state) {
+
+ /*
+ * Frequency mode. The clock has ben set, but the frequency has
+ * not yet been determined. Note that the Allan intercept is set
+ * insure the clock filter considers only the most recent
+ * measurements.
+ */
+ case S_FREQ:
+ sys_poll = NTP_MINDPOLL;
+ allan_xpt = CLOCK_ALLAN;
+ last_time = current_time;
+ break;
+
+ /*
+ * Synchronized mode. Discipline the poll interval.
+ */
+ case S_SYNC:
+ sys_poll = NTP_MINDPOLL;
+ allan_xpt = CLOCK_ALLAN;
+ tc_counter = 0;
+ break;
+
+ /*
+ * Don't do anything in S_SPIK state; just continue from S_SYNC
+ * state.
+ */
+ case S_SPIK:
+ break;
+
+ /*
+ * S_NSET, S_FSET and S_TSET states. These transient states set
+ * the time reference for future frequency updates.
+ */
+ default:
+ sys_poll = NTP_MINDPOLL;
+ allan_xpt = CLOCK_ALLAN;
+ last_time = current_time;
+ last_offset = clock_offset = 0;
+ break;
+ }
+}
+
+
+/*
+ * loop_config - configure the loop filter
+ */
+void
+loop_config(
+ int item,
+ double freq
+ )
+{
+#if defined(KERNEL_PLL)
+ struct timex ntv;
+#endif /* KERNEL_PLL */
+
+#ifdef DEBUG
+ if (debug)
+ printf("loop_config: state %d freq %.3f\n", item, freq *
+ 1e6);
+#endif
+ switch (item) {
+
+ case LOOP_DRIFTINIT:
+ case LOOP_DRIFTCOMP:
+
+ /*
+ * The drift file is present and the initial frequency
+ * is available, so set the state to S_FSET
+ */
+ rstclock(S_FSET);
+ drift_comp = freq;
+ if (drift_comp > sys_maxfreq)
+ drift_comp = sys_maxfreq;
+ if (drift_comp < -sys_maxfreq)
+ drift_comp = -sys_maxfreq;
+#ifdef KERNEL_PLL
+ /*
+ * If the phase-lock code is implemented in the kernel,
+ * give the time_constant and saved frequency offset to
+ * the kernel. If not, no harm is done. Note the initial
+ * time constant is zero, but the first clock update
+ * will fix that.
+ */
+ memset((char *)&ntv, 0, sizeof ntv);
+ pll_control = 1;
+#ifdef MOD_NANO
+ ntv.modes = MOD_NANO;
+#endif /* MOD_NANO */
+#ifdef SIGSYS
+ 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;
+ return;
+ }
+
+ /*
+ * Use sigsetjmp() to save state and then call
+ * ntp_adjtime(); if it fails, then siglongjmp() is used
+ * to return control
+ */
+ if (sigsetjmp(env, 1) == 0)
+ (void)ntp_adjtime(&ntv);
+ if ((sigaction(SIGSYS, &sigsys,
+ (struct sigaction *)NULL))) {
+ msyslog(LOG_ERR,
+ "sigaction() fails to restore SIGSYS trap: %m");
+ pll_control = 0;
+ return;
+ }
+#else /* SIGSYS */
+ if (ntp_adjtime(&ntv) < 0) {
+ msyslog(LOG_ERR,
+ "loop_config: ntp_adjtime() failed: %m");
+ pll_control = 0;
+ }
+#endif /* SIGSYS */
+
+ /*
+ * If the kernel support is available and enabled,
+ * initialize the parameters, but only if the external
+ * clock is not present.
+ */
+ if (pll_control && kern_enable) {
+ msyslog(LOG_NOTICE,
+ "using kernel phase-lock loop %04x",
+ ntv.status);
+#ifdef STA_NANO
+ if (ntv.status & STA_NANO)
+ pll_nano = 1;
+#endif /* STA_NANO */
+#ifdef STA_CLK
+
+ if (ntv.status & STA_CLK) {
+ ext_enable = 1;
+ } else {
+ ntv.modes = MOD_BITS | MOD_FREQUENCY;
+ ntv.freq = (int32)(drift_comp *
+ 65536e6);
+ ntv.maxerror = MAXDISPERSE;
+ ntv.esterror = MAXDISPERSE;
+ ntv.status = STA_UNSYNC | STA_PLL;
+ (void)ntp_adjtime(&ntv);
+ }
+#else
+ ntv.modes = MOD_BITS | MOD_FREQUENCY;
+ ntv.freq = (int32)(drift_comp * 65536e6);
+ ntv.maxerror = MAXDISPERSE;
+ ntv.esterror = MAXDISPERSE;
+ ntv.status = STA_UNSYNC | STA_PLL;
+ (void)ntp_adjtime(&ntv);
+#endif /* STA_CLK */
+ }
+#endif /* KERNEL_PLL */
+ }
+}
+
+
+#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/contrib/ntp/ntpd/ntp_monitor.c b/contrib/ntp/ntpd/ntp_monitor.c
new file mode 100644
index 0000000..64dfb3e
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_monitor.c
@@ -0,0 +1,354 @@
+/*
+ * ntp_monitor.c - monitor who is using the ntpd server
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif
+# include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+/*
+ * 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) ((int)(ntohl((addr)) & MON_HASH_MASK))
+
+/*
+ * 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]; /* array of list ptrs */
+struct mon_data mon_mru_list;
+struct mon_data mon_fifo_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; /* the free list or null if none */
+
+static int mon_total_mem; /* total number of structures allocated */
+static int mon_mem_increments; /* number of times we've 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;
+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((char *)&mon_hash[0], 0, sizeof mon_hash);
+ memset((char *)&mon_mru_list, 0, sizeof mon_mru_list);
+ memset((char *)&mon_fifo_list, 0, sizeof mon_fifo_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; /* Ooops.. */
+
+ 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_fifo_list.fifo_next = &mon_fifo_list;
+ mon_fifo_list.fifo_prev = &mon_fifo_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;
+
+ mon_fifo_list.fifo_next = &mon_fifo_list;
+ mon_fifo_list.fifo_prev = &mon_fifo_list;
+}
+
+
+/*
+ * ntp_monitor - record stats about this packet
+ */
+void
+ntp_monitor(
+ struct recvbuf *rbufp
+ )
+{
+ register struct pkt *pkt;
+ register struct mon_data *md;
+ register u_long netnum;
+ register int hash;
+ register int mode;
+
+ if (mon_enabled == MON_OFF)
+ return;
+
+ pkt = &rbufp->recv_pkt;
+ netnum = NSRCADR(&rbufp->recv_srcadr);
+ hash = MON_HASH(netnum);
+ mode = PKT_MODE(pkt->li_vn_mode);
+
+ md = mon_hash[hash];
+ while (md != NULL) {
+ if (md->rmtadr == netnum &&
+ /* ?? md->interface == rbufp->dstadr && ?? */
+ md->mode == (u_char)mode) {
+ md->lasttime = current_time;
+ md->count++;
+ md->version = PKT_VERSION(pkt->li_vn_mode);
+ md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
+
+ /*
+ * Shuffle him to the head of the
+ * mru list. What a crock.
+ */
+ 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) {
+ /*
+ * Get it from MRU list
+ */
+ md = mon_mru_list.mru_prev;
+ md->mru_prev->mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = md->mru_prev;
+
+ remove_from_hash(md);
+
+ /*
+ * Get it from FIFO list
+ */
+ md->fifo_prev->fifo_next = md->fifo_next;
+ md->fifo_next->fifo_prev = md->fifo_prev;
+
+ } else {
+ if (mon_free == NULL) /* if free list empty */
+ mon_getmoremem(); /* then get more */
+ md = mon_free;
+ mon_free = md->hash_next;
+ }
+
+ /*
+ * Got one, initialize it
+ */
+ md->lasttime = md->firsttime = current_time;
+ md->lastdrop = 0;
+ md->count = 1;
+ md->rmtadr = netnum;
+ 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 = ((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
+ * and at bottom of FIFO 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;
+
+ md->fifo_prev = mon_fifo_list.fifo_prev;
+ md->fifo_next = &mon_fifo_list;
+ mon_fifo_list.fifo_prev->fifo_next = md;
+ mon_fifo_list.fifo_prev = 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/contrib/ntp/ntpd/ntp_peer.c b/contrib/ntp/ntpd/ntp_peer.c
new file mode 100644
index 0000000..90646ab
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_peer.c
@@ -0,0 +1,795 @@
+/*
+ * 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"
+
+/*
+ * 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
+ * MCLIENT | e 0 0 0 0 0
+ *
+ * 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},
+
+/*MCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}
+};
+
+#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 corresponding peer data
+ * 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.
+ */
+
+/*
+ * The peer hash table (imported by the protocol module).
+ */
+struct peer *peer_hash[HASH_SIZE];
+int peer_hash_count[HASH_SIZE]; /* count of peers in each bucket */
+
+/*
+ * The association ID hash table. Used for lookups by association ID
+ */
+struct peer *assoc_hash[HASH_SIZE];
+int assoc_hash_count[HASH_SIZE];
+
+/*
+ * The free list. Clean structures only, please.
+ */
+static struct peer *peer_free;
+int peer_free_count;
+
+/*
+ * Association ID. We initialize this value randomly, the assign a new
+ * value every time the peer structure is incremented.
+ */
+static u_short current_association_ID;
+
+/*
+ * Memory allocation watermarks.
+ */
+#define INIT_PEER_ALLOC 15 /* initialize space for 15 peers */
+#define INC_PEER_ALLOC 5 /* when we run out, add 5 more */
+
+/*
+ * Miscellaneous statistic counters which may be queried.
+ */
+u_long peer_timereset; /* time stat counters were zeroed */
+u_long findpeer_calls; /* number of calls to findpeer */
+u_long assocpeer_calls; /* number of calls to findpeerbyassoc */
+u_long peer_allocations; /* number of allocations from the free list */
+u_long peer_demobilizations; /* number of structs freed to free list */
+int total_peer_structs; /* number of peer structs in circulation */
+int peer_associations; /* number of active associations */
+
+/*
+ * Our initial allocation of peer space
+ */
+static struct peer init_peer_alloc[INIT_PEER_ALLOC];
+
+/*
+ * Initialization data. When configuring peers at initialization time,
+ * we try to get their poll update timers initialized to different values
+ * to prevent us from sending big clumps of data all at once.
+ */
+/* static u_long init_peer_starttime; */
+
+static void getmorepeermem P((void));
+static void key_expire P((struct peer *));
+
+/*
+ * 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;
+
+ /*
+ * Initialization counter.
+ */
+ /* init_peer_starttime = 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 = (u_short)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_in *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 (NSRCADR(addr) == NSRCADR(&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_in *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 != 0; peer = peer->next) {
+ if (NSRCADR(srcadr) == NSRCADR(&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->cast_flags & FLAG_MCAST1)) {
+ *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;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("pkt_mode %d action %d\n", pkt_mode, *action);
+#endif
+ /* if no matching association is found */
+ if (peer == 0) {
+ *action = MATCH_ASSOC(NO_PEER, pkt_mode);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("pkt_mode %d action %d\n", pkt_mode, *action);
+#endif
+ return (struct peer *)0;
+ }
+
+ /* reset the default interface to something more meaningful */
+ if ((peer->dstadr == any_interface))
+ peer->dstadr = dstadr;
+ return peer;
+}
+
+/*
+ * findpeerbyassocid - find and return a peer using his association ID
+ */
+struct peer *
+findpeerbyassoc(
+ 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 ((u_short)assoc == peer->associd)
+ return peer; /* got it! */
+ }
+
+ /*
+ * Out of luck. Return 0.
+ */
+ return (struct peer *)0;
+}
+
+/*
+ * findmanycastpeer - find and return an manycast peer if it exists
+ *
+ *
+ * the current implementation loops across all hash-buckets
+ *
+ * *** THERE IS AN URGENT NEED TO CHANGE THIS ***
+ */
+struct peer *
+findmanycastpeer(
+ l_fp *p_org
+ )
+{
+ register struct peer *peer;
+ register struct peer *manycast_peer = 0;
+ int i = 0;
+
+ 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->flags & FLAG_CONFIG) {
+ if (L_ISEQU(&peer->xmt, p_org))
+ return peer; /* got it */
+ else
+ manycast_peer = peer;
+ }
+ }
+ }
+
+ /*
+ * Out of luck. Return the manycastpeer for what it is worth.
+ */
+ return manycast_peer;
+}
+
+/*
+ * key_expire - garbage collect keys
+ */
+static void
+key_expire(
+ struct peer *peer
+ )
+{
+ int i;
+
+ if (peer->keylist != 0) {
+ for (i = 0; i <= peer->keynumber; i++)
+ authtrust(peer->keylist[i], 0);
+ free(peer->keylist);
+ peer->keylist = 0;
+ }
+ if (peer->keyid > NTP_MAXKEY) {
+ authtrust(peer->keyid, 0);
+ peer->keyid = 0;
+ }
+}
+
+/*
+ * key_rekey - expire all keys and roll a new private value. Note the
+ * 32-bit mask is necessary for 64-bit u_longs.
+ */
+void
+key_expire_all(
+ )
+{
+ struct peer *peer, *next_peer;
+ int n;
+
+ for (n = 0; n < HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
+ next_peer = peer->next;
+ key_expire(peer);
+ }
+ }
+ sys_private = (u_long)RANDOM & 0xffffffff;
+#ifdef DEBUG
+ if (debug)
+ printf("key_expire_all: at %lu private %08lx\n",
+ current_time, sys_private);
+#endif
+}
+/*
+ * unpeer - remove peer structure from hash table and free structure
+ */
+void
+unpeer(
+ struct peer *peer_to_remove
+ )
+{
+ int hash;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("demobilize %u\n", peer_to_remove->associd);
+#endif
+ key_expire(peer_to_remove);
+ hash = HASH_ADDR(&peer_to_remove->srcadr);
+ peer_hash_count[hash]--;
+ peer_demobilizations++;
+ peer_associations--;
+
+#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!",
+ ntoa(&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!",
+ ntoa(&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_config - configure a new peer
+ */
+struct peer *
+peer_config(
+ struct sockaddr_in *srcadr,
+ struct interface *dstadr,
+ int hmode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ int flags,
+ int ttl,
+ u_long key
+ )
+{
+ register struct peer *peer;
+
+ /*
+ * See if we have this guy in the tables already. If
+ * so just mark him configured.
+ */
+ peer = findexistingpeer(srcadr, (struct peer *)0, hmode);
+ if (dstadr != 0) {
+ while (peer != 0) {
+ if (peer->dstadr == dstadr)
+ break;
+ peer = findexistingpeer(srcadr, peer, hmode);
+ }
+ }
+
+ /*
+ * If we found one, just change his mode and mark him configured.
+ */
+ if (peer != 0) {
+ peer->hmode = (u_char)hmode;
+ peer->version = (u_char)version;
+ peer->minpoll = (u_char)minpoll;
+ peer->maxpoll = (u_char)maxpoll;
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->minpoll;
+ peer->flags = flags | FLAG_CONFIG |
+ (peer->flags & FLAG_REFCLOCK);
+ peer->cast_flags = (hmode == MODE_BROADCAST) ?
+ IN_CLASSD(ntohl(srcadr->sin_addr.s_addr)) ? MDF_MCAST : MDF_BCAST : MDF_UCAST;
+ peer->ttl = (u_char)ttl;
+ peer->keyid = key;
+ peer->keynumber = 0;
+ return peer;
+ }
+
+ /*
+ * If we're here this guy is unknown to us. Make a new peer
+ * structure for him.
+ */
+ peer = newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll,
+ ttl, key);
+ if (peer != 0) {
+ peer->flags |= flags | FLAG_CONFIG;
+#ifdef DEBUG
+ if (debug)
+ printf("peer_config: %s mode %d vers %d min %d max %d flags 0x%04x ttl %d key %lu\n",
+ ntoa(&peer->srcadr), peer->hmode, peer->version,
+ peer->minpoll, peer->maxpoll, peer->flags,
+ peer->ttl, peer->keyid);
+#endif
+ }
+ return peer;
+}
+
+
+/*
+ * newpeer - initialize a new peer association
+ */
+struct peer *
+newpeer(
+ struct sockaddr_in *srcadr,
+ struct interface *dstadr,
+ int hmode,
+ int version,
+ int minpoll,
+ int maxpoll,
+ int ttl,
+ u_long key
+ )
+{
+ register struct peer *peer;
+ register int i;
+
+ /*
+ * Some dirt here. 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++;
+
+ /*
+ * Initialize the structure. This stuff is sort of part of
+ * the receive procedure and part of the clear procedure rolled
+ * into one.
+ *
+ * Zero the whole thing for now. We might be pickier later.
+ */
+ memset((char *)peer, 0, sizeof(struct peer));
+
+ peer->srcadr = *srcadr;
+ if (dstadr != 0)
+ peer->dstadr = dstadr;
+ else if (hmode == MODE_BROADCAST)
+ peer->dstadr = findbcastinter(srcadr);
+ else
+ peer->dstadr = any_interface;
+ peer->cast_flags = (hmode == MODE_BROADCAST) ?
+ (IN_CLASSD(ntohl(srcadr->sin_addr.s_addr))) ? MDF_MCAST :
+ MDF_BCAST : (hmode == MODE_BCLIENT || hmode == MODE_MCLIENT) ?
+ (peer->dstadr->flags & INT_MULTICAST) ? MDF_MCAST : MDF_BCAST :
+ MDF_UCAST;
+ /* Set manycast flags if appropriate */
+ if (IN_CLASSD(ntohl(srcadr->sin_addr.s_addr)) && hmode == MODE_CLIENT)
+ peer->cast_flags = MDF_ACAST;
+ peer->hmode = (u_char)hmode;
+ peer->keyid = key;
+ peer->version = (u_char)version;
+ peer->minpoll = (u_char)minpoll;
+ peer->maxpoll = (u_char)maxpoll;
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->minpoll;
+ peer->ttl = ttl;
+ peer->leap = LEAP_NOTINSYNC;
+ peer->precision = sys_precision;
+ peer->variance = MAXDISPERSE;
+ peer->epoch = current_time;
+ peer->stratum = STRATUM_UNSPEC;
+ peer_clear(peer);
+ peer->update = peer->outdate = current_time;
+ peer->nextdate = peer->outdate + RANDPOLL(NTP_MINPOLL);
+ if (peer->flags & FLAG_BURST)
+ peer->burst = NTP_SHIFT;
+
+ /*
+ * Assign him an association ID and increment the system variable
+ */
+ peer->associd = current_association_ID;
+ if (++current_association_ID == 0)
+ ++current_association_ID;
+
+ /*
+ * 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 0;
+ }
+ }
+#endif
+
+ /*
+ * Put him 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 DEBUG
+ if (debug > 1)
+ printf("mobilize %u next %lu\n", peer->associd,
+ peer->nextdate - peer->outdate);
+#endif
+ return peer;
+}
+
+
+/*
+ * peer_unconfig - remove the configuration bit from a peer
+ */
+int
+peer_unconfig(
+ struct sockaddr_in *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_copy_manycast - copy manycast peer variables to new association
+ * (right now it simply copies the transmit timestamp)
+ */
+void
+peer_config_manycast(
+ struct peer *peer1,
+ struct peer *peer2
+ )
+{
+ peer2->cast_flags = MDF_ACAST;
+ peer2->xmt = peer1->xmt;
+}
+
+/*
+ * 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->seltooold = 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);
+}
diff --git a/contrib/ntp/ntpd/ntp_proto.c b/contrib/ntp/ntpd/ntp_proto.c
new file mode 100644
index 0000000..6d54291
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_proto.c
@@ -0,0 +1,2165 @@
+/*
+ * ntp_proto.c - NTP version 4 protocol machinery
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+#include "ntp_control.h"
+#include "ntp_string.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; /* distance to current sync source */
+double sys_rootdispersion; /* dispersion of system clock */
+u_int32 sys_refid; /* reference source for local clock */
+static double sys_offset; /* current local clock offset */
+l_fp sys_reftime; /* time we were last updated */
+struct peer *sys_peer; /* our current peer */
+u_long sys_automax; /* maximum session key lifetime */
+
+/*
+ * Nonspecified system state variables.
+ */
+int sys_bclient; /* we set our time to broadcasts */
+double sys_bdelay; /* broadcast client default delay */
+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_maxd; /* select error (squares) */
+static double sys_epsil; /* system error (squares) */
+u_long sys_private; /* private value for session seed */
+int sys_manycastserver; /* 1 => respond to manycast client pkts */
+
+/*
+ * Statistics counters
+ */
+u_long sys_stattime; /* time when we started recording */
+u_long sys_badstratum; /* packets with invalid stratum */
+u_long sys_oldversionpkt; /* old version packets received */
+u_long sys_newversionpkt; /* new version packets received */
+u_long sys_unknownversion; /* don't know version packets */
+u_long sys_badlength; /* packets with bad length */
+u_long sys_processed; /* packets processed */
+u_long sys_badauth; /* packets dropped because of auth */
+u_long sys_limitrejected; /* pkts rejected due to client count per net */
+
+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, u_long));
+static void clock_update P((void));
+#ifdef MD5
+static void make_keylist P((struct peer *));
+#endif /* MD5 */
+
+/*
+ * transmit - Transmit Procedure. See Section 3.4.2 of the
+ * specification.
+ */
+void
+transmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int hpoll;
+
+ hpoll = peer->hpoll;
+ if (peer->burst == 0) {
+ u_char oreach;
+
+ /*
+ * Determine reachability and diddle things if we
+ * haven't heard from the host for a while. If the peer
+ * is not configured and not likely to stay around,
+ * we exhaust it.
+ */
+ oreach = peer->reach;
+ if (oreach & 0x01)
+ peer->valid++;
+ if (oreach & 0x80)
+ peer->valid--;
+ if (!(peer->flags & FLAG_CONFIG) &&
+ peer->valid > NTP_SHIFT / 2 && (peer->reach & 0x80) &&
+ peer->status < CTL_PST_SEL_SYNCCAND)
+ peer->reach = 0;
+ peer->reach <<= 1;
+ if (peer->reach == 0) {
+
+ /*
+ * If this is an uncofigured association and
+ * has become unreachable, demobilize it.
+ */
+ if (oreach != 0) {
+ report_event(EVNT_UNREACH, peer);
+ peer->timereachable = current_time;
+ peer_clear(peer);
+ if (!(peer->flags & FLAG_CONFIG)) {
+ unpeer(peer);
+ return;
+ }
+ }
+
+ /*
+ * We would like to respond quickly when the
+ * peer comes back to life. If the probes since
+ * becoming unreachable are less than
+ * NTP_UNREACH, clamp the poll interval to the
+ * minimum. In order to minimize the network
+ * traffic, the interval gradually ramps up the
+ * the maximum after that.
+ */
+ peer->ppoll = peer->maxpoll;
+ if (peer->unreach < NTP_UNREACH) {
+ if (peer->hmode == MODE_CLIENT)
+ peer->unreach++;
+ hpoll = peer->minpoll;
+ } else {
+ hpoll++;
+ }
+ if (peer->flags & FLAG_BURST)
+ peer->burst = 2;
+
+ } else {
+
+ /*
+ * Here the peer is reachable. If there is no
+ * system peer or if the stratum of the system
+ * peer is greater than this peer, clamp the
+ * poll interval to the minimum. If less than
+ * two samples are in the reachability register,
+ * reduce the interval; if more than six samples
+ * are in the register, increase the interval.
+ */
+ peer->unreach = 0;
+ if (sys_peer == 0)
+ hpoll = peer->minpoll;
+ else if (sys_peer->stratum > peer->stratum)
+ hpoll = peer->minpoll;
+ if ((peer->reach & 0x03) == 0) {
+ clock_filter(peer, 0., 0., MAXDISPERSE);
+ clock_select();
+ }
+ if (peer->valid <= 2)
+ hpoll--;
+ else if (peer->valid >= NTP_SHIFT - 2)
+ hpoll++;
+ if (peer->flags & FLAG_BURST)
+ peer->burst = NTP_SHIFT;
+ }
+ } else {
+ peer->burst--;
+ if (peer->burst == 0) {
+ if (peer->flags & FLAG_MCAST2) {
+ peer->flags &= ~FLAG_BURST;
+ peer->hmode = MODE_BCLIENT;
+ }
+ clock_select();
+ poll_update(peer, hpoll);
+ return;
+ }
+ }
+
+ /*
+ * We need to be very careful about honking uncivilized time. If
+ * not operating in broadcast mode, honk in all except broadcast
+ * client mode. If operating in broadcast mode and synchronized
+ * to a real source, honk except when the peer is the local-
+ * clock driver and the prefer flag is not set. In other words,
+ * in broadcast mode we never honk unless known to be
+ * synchronized to real time.
+ */
+ if (peer->hmode != MODE_BROADCAST) {
+ if (peer->hmode != MODE_BCLIENT)
+ peer_xmit(peer);
+ } else if (sys_peer != 0 && sys_leap != LEAP_NOTINSYNC) {
+ if (!(sys_peer->refclktype == REFCLK_LOCALCLOCK &&
+ !(sys_peer->flags & FLAG_PREFER)))
+ peer_xmit(peer);
+ }
+ peer->outdate = current_time;
+ poll_update(peer, hpoll);
+}
+
+/*
+ * receive - Receive Procedure. See section 3.4.3 in the specification.
+ */
+void
+receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct peer *peer;
+ register struct pkt *pkt;
+ int hismode;
+ int oflags;
+ int restrict_mask;
+ int has_mac; /* has MAC field */
+ int authlen; /* length of MAC field */
+ int is_authentic; /* cryptosum ok */
+ int is_mystic; /* session key exists */
+ int is_error; /* parse error */
+/* u_long pkeyid; */
+ u_long skeyid, tkeyid;
+ struct peer *peer2;
+ int retcode = AM_NOMATCH;
+
+ /*
+ * Monitor the packet and get restrictions
+ */
+ ntp_monitor(rbufp);
+ restrict_mask = restrictions(&rbufp->recv_srcadr);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("receive: from %s restrict %02x\n",
+ ntoa(&rbufp->recv_srcadr), restrict_mask);
+#endif
+ if (restrict_mask & RES_IGNORE)
+ return;
+
+ /*
+ * Discard packets with invalid version number.
+ */
+ pkt = &rbufp->recv_pkt;
+ if (PKT_VERSION(pkt->li_vn_mode) >= NTP_VERSION)
+ sys_newversionpkt++;
+ else if (PKT_VERSION(pkt->li_vn_mode) >= NTP_OLDVERSION)
+ sys_oldversionpkt++;
+ else {
+ sys_unknownversion++;
+ return;
+ }
+
+ /*
+ * Restrict control/private mode packets. Note that packet
+ * length has to be checked in the control/private mode protocol
+ * module.
+ */
+ if (PKT_MODE(pkt->li_vn_mode) == MODE_PRIVATE) {
+ if (restrict_mask & RES_NOQUERY)
+ return;
+ process_private(rbufp, ((restrict_mask & RES_NOMODIFY) ==
+ 0));
+ return;
+ }
+ if (PKT_MODE(pkt->li_vn_mode) == MODE_CONTROL) {
+ if (restrict_mask & RES_NOQUERY)
+ return;
+ process_control(rbufp, restrict_mask);
+ return;
+ }
+
+ /*
+ * Restrict revenue packets.
+ */
+ if (restrict_mask & RES_DONTSERVE)
+ return;
+
+ /*
+ * See if we only accept limited number of clients from the net
+ * this guy is from. Note: the flag is determined dynamically
+ * within restrictions()
+ */
+ if (restrict_mask & RES_LIMITED) {
+ sys_limitrejected++;
+ return;
+ }
+
+ /*
+ * If we are not a broadcast client, ignore broadcast packets.
+ */
+ if ((PKT_MODE(pkt->li_vn_mode) == MODE_BROADCAST && !sys_bclient))
+ return;
+
+ /*
+ * This is really awful ugly. We figure out whether an extension
+ * field is present and then measure the MAC size. If the number
+ * of words following the packet header is less than or equal to
+ * 5, no extension field is present and these words constitute the
+ * MAC. If the number of words is greater than 5, an extension
+ * field is present and the first word contains the length of
+ * the extension field and the MAC follows that.
+ */
+ has_mac = 0;
+/* pkeyid = 0; */
+ skeyid = tkeyid = 0;
+ authlen = LEN_PKT_NOMAC;
+ has_mac = rbufp->recv_length - authlen;
+ if (has_mac <= 5 * sizeof(u_int32)) {
+ skeyid = (u_long)ntohl(pkt->keyid1) & 0xffffffff;
+ } else {
+ authlen += (u_long)ntohl(pkt->keyid1) & 0xffffffff;
+ has_mac = rbufp->recv_length - authlen;
+ if (authlen <= 0) {
+ sys_badlength++;
+ return;
+ }
+
+ /*
+ * Note that keyid3 is actually the key ident of the
+ * MAC itself.
+ */
+/* pkeyid = (u_long)ntohl(pkt->keyid2) & 0xffffffff; */
+ skeyid = tkeyid = (u_long)ntohl(pkt->keyid3) & 0xffffffff;
+ }
+
+ /*
+ * Figure out his mode and validate it.
+ */
+ hismode = (int)PKT_MODE(pkt->li_vn_mode);
+ if (PKT_VERSION(pkt->li_vn_mode) == NTP_OLDVERSION && hismode ==
+ 0) {
+ /*
+ * Easy. If it is from the NTP port it is
+ * a sym act, else client.
+ */
+ if (SRCPORT(&rbufp->recv_srcadr) == NTP_PORT)
+ hismode = MODE_ACTIVE;
+ else
+ hismode = MODE_CLIENT;
+ } else {
+ if (hismode != MODE_ACTIVE && hismode != MODE_PASSIVE &&
+ hismode != MODE_SERVER && hismode != MODE_CLIENT &&
+ hismode != MODE_BROADCAST)
+ return;
+ }
+
+ /*
+ * If he included a mac field, decrypt it to see if it is
+ * authentic.
+ */
+ is_authentic = is_mystic = 0;
+ if (has_mac == 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("receive: at %ld from %s mode %d\n",
+ current_time, ntoa(&rbufp->recv_srcadr),
+ hismode);
+#endif
+ } else {
+ is_mystic = authistrusted(skeyid);
+#ifdef MD5
+ if (skeyid > NTP_MAXKEY && !is_mystic) {
+
+ /*
+ * For multicast mode, generate the session key
+ * and install in the key cache. For client mode,
+ * generate the session key for the unicast
+ * address. For server mode, the session key should
+ * already be in the key cache, since it was
+ * generated when the last request was sent.
+ */
+ if (hismode == MODE_BROADCAST) {
+ tkeyid = session_key(
+ ntohl((&rbufp->recv_srcadr)->sin_addr.s_addr),
+ ntohl(rbufp->dstadr->bcast.sin_addr.s_addr),
+ skeyid, (u_long)(4 * (1 << pkt->ppoll)));
+ } else if (hismode != MODE_SERVER) {
+ tkeyid = session_key(
+ ntohl((&rbufp->recv_srcadr)->sin_addr.s_addr),
+ ntohl(rbufp->dstadr->sin.sin_addr.s_addr),
+ skeyid, (u_long)(4 * (1 << pkt->ppoll)));
+ }
+
+ }
+#endif /* MD5 */
+
+ /*
+ * Compute the cryptosum. Note a clogging attack may
+ * succceed in bloating the key cache.
+ */
+ if (authdecrypt(skeyid, (u_int32 *)pkt, authlen, has_mac))
+ is_authentic = 1;
+ else
+ sys_badauth++;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "receive: at %ld %s mode %d keyid %08lx mac %d auth %d\n",
+ current_time, ntoa(&rbufp->recv_srcadr),
+ hismode, skeyid, has_mac, is_authentic);
+#endif
+ }
+
+ /*
+ * Find the peer. This will return a null if this guy isn't in
+ * the database.
+ */
+ peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, rbufp->fd,
+ hismode, &retcode);
+ /*
+ * The new association matching rules are driven by a table specified
+ * in ntp.h. We have replaced the *default* behaviour of replying
+ * to bogus packets in server mode in this version.
+ * A packet must now match an association in order to be processed.
+ * In the event that no association exists, then an association is
+ * mobilized if need be. Two different associations can be mobilized
+ * a) passive associations
+ * b) client associations due to broadcasts or manycasts.
+ */
+ is_error = 0;
+ switch (retcode) {
+ case AM_FXMIT:
+ /*
+ * If the client is configured purely as a broadcast client and
+ * not as an manycast server, it has no business being a server.
+ * Simply go home. Otherwise, send a MODE_SERVER response and go
+ * home. Note that we don't do a authentication check here,
+ * since we can't set the system clock; but, we do set the
+ * key ID to zero to tell the caller about this.
+ */
+ if (!sys_bclient || sys_manycastserver) {
+ if (is_authentic)
+ fast_xmit(rbufp, MODE_SERVER, skeyid);
+ else
+ fast_xmit(rbufp, MODE_SERVER, 0);
+ }
+
+ /*
+ * We can't get here if an association is mobilized, so just
+ * toss the key, if appropriate.
+ */
+ if (!is_mystic && skeyid > NTP_MAXKEY)
+ authtrust(skeyid, 0);
+ return;
+
+ case AM_MANYCAST:
+ /*
+ * This could be in response to a multicast packet sent by
+ * the "manycast" mode association. Find peer based on the
+ * originate timestamp in the packet. Note that we don't
+ * mobilize a new association, unless the packet is properly
+ * authenticated. The response must be properly authenticated
+ * and it's darn funny of the manycaster isn't around now.
+ */
+ if ((sys_authenticate && !is_authentic)) {
+ is_error = 1;
+ break;
+ }
+ peer2 = (struct peer *)findmanycastpeer(&pkt->org);
+ if (peer2 == 0) {
+ is_error = 1;
+ break;
+ }
+
+ /*
+ * Create a new association and copy the peer variables to it.
+ * If something goes wrong, carefully pry the new association
+ * away and return its marbles to the candy store.
+ */
+ peer = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, MODE_CLIENT, PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXDPOLL, 0, skeyid);
+ if (peer == 0) {
+ is_error = 1;
+ break;
+ }
+ peer_config_manycast(peer2, peer);
+ break;
+
+ case AM_ERR:
+ /*
+ * Something bad happened. Dirty floor will be mopped by the
+ * code at the end of this adventure.
+ */
+ is_error = 1;
+ break;
+
+ case AM_NEWPASS:
+ /*
+ * Okay, we're going to keep him around. Allocate him some
+ * memory. But, don't do that unless the packet is properly
+ * authenticated.
+ */
+ if ((sys_authenticate && !is_authentic)) {
+ is_error = 1;
+ break;
+ }
+ peer = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, MODE_PASSIVE, PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXDPOLL, 0, skeyid);
+ break;
+
+ case AM_NEWBCL:
+ /*
+ * Broadcast client being set up now. Do this only if the
+ * packet is properly authenticated.
+ */
+ if ((restrict_mask & RES_NOPEER) || !sys_bclient ||
+ (sys_authenticate && !is_authentic)) {
+ is_error = 1;
+ break;
+ }
+ peer = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, MODE_MCLIENT, PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXDPOLL, 0, skeyid);
+ if (peer == 0)
+ break;
+ peer->flags |= FLAG_MCAST1 | FLAG_MCAST2 | FLAG_BURST;
+ peer->hmode = MODE_CLIENT;
+ break;
+
+ case AM_POSSBCL:
+ case AM_PROCPKT:
+ /*
+ * It seems like it is okay to process the packet now
+ */
+ break;
+
+ default:
+ /*
+ * shouldn't be getting here, but simply return anyway!
+ */
+ is_error = 1;
+ }
+ if (is_error) {
+
+ /*
+ * Error stub. If we get here, something broke. We scuttle
+ * the autokey if necessary and sink the ship. This can
+ * occur only upon mobilization, so we can throw the
+ * structure away without fear of breaking anything.
+ */
+ if (!is_mystic && skeyid > NTP_MAXKEY)
+ authtrust(skeyid, 0);
+ if (peer != 0)
+ if (!(peer->flags & FLAG_CONFIG))
+ unpeer(peer);
+#ifdef DEBUG
+ if (debug)
+ printf("match error code %d assoc %d\n", retcode,
+ peer_associations);
+#endif
+ return;
+ }
+
+ /*
+ * If the peer isn't configured, set his keyid and authenable
+ * status based on the packet.
+ */
+ oflags = peer->flags;
+ peer->timereceived = current_time;
+ if (!(peer->flags & FLAG_CONFIG) && has_mac) {
+ peer->flags |= FLAG_AUTHENABLE;
+ if (skeyid > NTP_MAXKEY) {
+ if (peer->flags & FLAG_MCAST2)
+ peer->keyid = skeyid;
+ else
+ peer->flags |= FLAG_SKEY;
+ }
+ }
+
+ /*
+ * Determine if this guy is basically trustable. If not, flush
+ * the bugger. If this is the first packet that is authenticated,
+ * flush the clock filter. This is to foil clogging attacks that
+ * might starve the poor dear.
+ */
+ peer->flash = 0;
+ if (is_authentic)
+ peer->flags |= FLAG_AUTHENTIC;
+ else
+ peer->flags &= ~FLAG_AUTHENTIC;
+ if (peer->hmode == MODE_BROADCAST && (restrict_mask & RES_DONTTRUST))
+ peer->flash |= TEST10; /* access denied */
+ if (peer->flags & FLAG_AUTHENABLE) {
+ if (!(peer->flags & FLAG_AUTHENTIC))
+ peer->flash |= TEST5; /* authentication failed */
+ else if (skeyid == 0)
+ peer->flash |= TEST9; /* peer not authenticated */
+ else if (!(oflags & FLAG_AUTHENABLE)) {
+ peer_clear(peer);
+ report_event(EVNT_PEERAUTH, peer);
+ }
+ }
+ if ((peer->flash & ~(u_int)TEST9) != 0) {
+
+ /*
+ * The packet is bogus, so we throw it away before becoming
+ * a denial-of-service hazard. We don't throw the current
+ * association away if it is configured or if it has prior
+ * reachable friends.
+ */
+ if (!is_mystic && skeyid > NTP_MAXKEY)
+ authtrust(skeyid, 0);
+ if (!(peer->flags & FLAG_CONFIG) && peer->reach == 0)
+ unpeer(peer);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "invalid packet 0x%02x code %d assoc %d\n",
+ peer->flash, retcode, peer_associations);
+#endif
+ return;
+ }
+
+#ifdef MD5
+ /*
+ * The autokey dance. The cha-cha requires that the hash of the
+ * current session key matches the previous key identifier. Heaps
+ * of trouble if the steps falter.
+ */
+ if (skeyid > NTP_MAXKEY) {
+ int i;
+
+ /*
+ * In the case of a new autokey, verify the hash matches
+ * one of the previous four hashes. If not, raise the
+ * authentication flasher and hope the next one works.
+ */
+ if (hismode == MODE_SERVER) {
+ peer->pkeyid = peer->keyid;
+ } else if (peer->flags & FLAG_MCAST2) {
+ if (peer->pkeyid > NTP_MAXKEY)
+ authtrust(peer->pkeyid, 0);
+ for (i = 0; i < 4 && tkeyid != peer->pkeyid; i++) {
+ tkeyid = session_key(
+ ntohl((&rbufp->recv_srcadr)->sin_addr.s_addr),
+ ntohl(rbufp->dstadr->bcast.sin_addr.s_addr),
+ tkeyid, 0);
+ }
+ } else {
+ if (peer->pkeyid > NTP_MAXKEY)
+ authtrust(peer->pkeyid, 0);
+ for (i = 0; i < 4 && tkeyid != peer->pkeyid; i++) {
+ tkeyid = session_key(
+ ntohl((&rbufp->recv_srcadr)->sin_addr.s_addr),
+ ntohl(rbufp->dstadr->sin.sin_addr.s_addr),
+ tkeyid, 0);
+ }
+ }
+#ifdef XXX /* temp until certificate code is mplemented */
+ if (tkeyid != peer->pkeyid)
+ peer->flash |= TEST9; /* peer not authentic */
+#endif
+ peer->pkeyid = skeyid;
+ }
+#endif /* MD5 */
+
+ /*
+ * Gawdz, it's come to this. Process the dang packet. If something
+ * breaks and the association doesn't deserve to live, toss it.
+ * Be careful in active mode and return a packet anyway.
+ */
+ process_packet(peer, pkt, &(rbufp->recv_time));
+ if (!(peer->flags & FLAG_CONFIG) && peer->reach == 0) {
+ if (peer->hmode == MODE_PASSIVE) {
+ if (is_authentic)
+ fast_xmit(rbufp, MODE_PASSIVE, skeyid);
+ else
+ fast_xmit(rbufp, MODE_PASSIVE, 0);
+ }
+ unpeer(peer);
+ }
+}
+
+
+/*
+ * 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.
+ */
+int
+process_packet(
+ register struct peer *peer,
+ register struct pkt *pkt,
+ l_fp *recv_ts
+ )
+{
+ l_fp t10, t23;
+ double p_offset, p_del, p_disp;
+ double dtemp;
+ l_fp p_rec, p_xmt, p_org, p_reftime;
+ l_fp ci;
+ int pmode;
+
+ /*
+ * Swap header fields and keep the books.
+ */
+ 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);
+ if (PKT_MODE(pkt->li_vn_mode) != MODE_BROADCAST)
+ NTOHL_FP(&pkt->org, &p_org);
+ else
+ p_org = peer->rec;
+ peer->rec = *recv_ts;
+ peer->ppoll = pkt->ppoll;
+ pmode = PKT_MODE(pkt->li_vn_mode);
+
+ /*
+ * Test for old or duplicate packets (tests 1 through 3).
+ */
+ if (L_ISHIS(&peer->org, &p_xmt)) /* count old packets */
+ peer->oldpkt++;
+ if (L_ISEQU(&peer->org, &p_xmt)) /* test 1 */
+ peer->flash |= TEST1; /* duplicate packet */
+ if (PKT_MODE(pkt->li_vn_mode) != MODE_BROADCAST) {
+ if (!L_ISEQU(&peer->xmt, &p_org)) { /* test 2 */
+ peer->bogusorg++;
+ peer->flash |= TEST2; /* bogus packet */
+ }
+ if (L_ISZERO(&p_rec) || L_ISZERO(&p_org))
+ peer->flash |= TEST3; /* unsynchronized */
+ } else {
+ if (L_ISZERO(&p_org))
+ peer->flash |= TEST3; /* unsynchronized */
+ }
+ peer->org = p_xmt;
+
+ /*
+ * Test for valid header (tests 5 through 10)
+ */
+ ci = p_xmt;
+ L_SUB(&ci, &p_reftime);
+ LFPTOD(&ci, dtemp);
+ if (PKT_LEAP(pkt->li_vn_mode) == LEAP_NOTINSYNC || /* test 6 */
+ PKT_TO_STRATUM(pkt->stratum) >= NTP_MAXSTRATUM ||
+ dtemp < 0)
+ peer->flash |= TEST6; /* peer clock unsynchronized */
+ if (!(peer->flags & FLAG_CONFIG) && sys_peer != 0) { /* test 7 */
+ if (PKT_TO_STRATUM(pkt->stratum) > sys_stratum) {
+ peer->flash |= TEST7; /* peer stratum too high */
+ sys_badstratum++;
+ }
+ }
+ if (fabs(p_del) >= MAXDISPERSE /* test 8 */
+ || p_disp >= MAXDISPERSE)
+ peer->flash |= TEST8; /* delay/dispersion too high */
+
+ /*
+ * If the packet header is invalid (tests 5 through 10), exit.
+ * XXX we let TEST9 sneak by until the certificate code is
+ * implemented, but only to mobilize the association.
+ */
+ if (peer->flash & (TEST5 | TEST6 | TEST7 | TEST8 | TEST10)) {
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "invalid packet header 0x%02x mode %d\n",
+ peer->flash, pmode);
+#endif
+ return (0);
+ }
+
+ /*
+ * Valid header; update our state.
+ */
+ record_raw_stats(&peer->srcadr, &peer->dstadr->sin,
+ &p_org, &p_rec, &p_xmt, &peer->rec);
+
+ peer->leap = PKT_LEAP(pkt->li_vn_mode);
+ peer->pmode = pmode; /* unspec */
+ peer->stratum = PKT_TO_STRATUM(pkt->stratum);
+ peer->precision = pkt->precision;
+ peer->rootdelay = p_del;
+ peer->rootdispersion = p_disp;
+ peer->refid = pkt->refid;
+ peer->reftime = p_reftime;
+ if (peer->reach == 0) {
+ report_event(EVNT_REACH, peer);
+ peer->timereachable = current_time;
+ }
+ peer->reach |= 1;
+ poll_update(peer, peer->hpoll);
+
+ /*
+ * 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.
+ *
+ * c = ((t2 - t3) + (t1 - t0)) / 2
+ * d = (t2 - t3) - (t1 - t0)
+ * e = (org - rec) (seconds only)
+ */
+ t10 = p_xmt; /* compute t1 - t0 */
+ L_SUB(&t10, &peer->rec);
+ t23 = p_rec; /* compute t2 - t3 */
+ L_SUB(&t23, &p_org);
+ ci = t10;
+ p_disp = CLOCK_PHI * (peer->rec.l_ui - p_org.l_ui);
+
+ /*
+ * 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 client/server mode,
+ * calculate the clock offset, using the engineered refinement
+ * algorithms, while also receiving broadcasts. When a broadcast
+ * is received in client/server mode, we calculate a correction
+ * factor to use after switching back to broadcast mode. We know
+ * NTP_SKEWFACTOR == 16, which accounts for the simplified ei
+ * calculation.
+ *
+ * If FLAG_MCAST2 is set, we are a broadcast/multicast client.
+ * If FLAG_MCAST1 is set, we haven't calculated the propagation
+ * delay. If hmode is MODE_CLIENT, we haven't set the local
+ * clock in client/server mode. Initially, we come up
+ * MODE_CLIENT. When the clock is first updated and FLAG_MCAST2
+ * is set, we switch from MODE_CLIENT to MODE_BCLIENT.
+ */
+ if (pmode == MODE_BROADCAST) {
+ if (peer->flags & FLAG_MCAST1) {
+ if (peer->hmode == MODE_BCLIENT)
+ peer->flags &= ~FLAG_MCAST1;
+ LFPTOD(&ci, p_offset);
+ peer->estbdelay = peer->offset - p_offset;
+ return (1);
+
+ }
+ DTOLFP(peer->estbdelay, &t10);
+ L_ADD(&ci, &t10);
+ p_del = peer->delay;
+ } else {
+ L_ADD(&ci, &t23);
+ L_RSHIFT(&ci);
+ L_SUB(&t23, &t10);
+ LFPTOD(&t23, p_del);
+ }
+ LFPTOD(&ci, p_offset);
+ if (fabs(p_del) >= MAXDISPERSE || p_disp >= MAXDISPERSE) /* test 4 */
+ peer->flash |= TEST4; /* delay/dispersion too big */
+
+ /*
+ * If the packet data are invalid (tests 1 through 4), exit.
+ */
+ if (peer->flash) {
+#ifdef DEBUG
+ if (debug)
+ printf("invalid packet data 0x%02x mode %d\n",
+ peer->flash, pmode);
+#endif
+ return(1);
+ }
+
+
+ /*
+ * This one is valid. Mark it so, give it to clock_filter().
+ */
+ clock_filter(peer, p_offset, p_del, fabs(p_disp));
+ clock_select();
+ record_peer_stats(&peer->srcadr, ctlpeerstatus(peer),
+ peer->offset, peer->delay, peer->disp, SQRT(peer->variance));
+ return(1);
+}
+
+
+/*
+ * clock_update - Called at system process update intervals.
+ */
+static void
+clock_update(void)
+{
+ u_char oleap;
+ u_char ostratum;
+ int i;
+ struct peer *peer;
+
+ /*
+ * Reset/adjust the system clock. Do this only if there is a
+ * system peer and we haven't seen that peer lately. Watch for
+ * timewarps here.
+ */
+ if (sys_peer == 0)
+ return;
+ if (sys_peer->pollsw == FALSE || sys_peer->burst > 0)
+ return;
+ sys_peer->pollsw = FALSE;
+#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_epsil)) {
+
+ case -1:
+ /*
+ * Clock is too screwed up. Just exit for now.
+ */
+ report_event(EVNT_SYSFAULT, (struct peer *)0);
+ exit(1);
+ /*NOTREACHED*/
+
+ case 1:
+ /*
+ * Clock was stepped. Clear filter registers
+ * of all peers.
+ */
+ for (i = 0; i < HASH_SIZE; i++) {
+ for (peer = peer_hash[i]; peer != 0;
+ peer =peer->next)
+ peer_clear(peer);
+ }
+ NLOG(NLOG_SYNCSTATUS)
+ msyslog(LOG_INFO, "synchronisation lost");
+ sys_peer = 0;
+ sys_stratum = STRATUM_UNSPEC;
+ report_event(EVNT_CLOCKRESET, (struct peer *)0);
+ break;
+
+ default:
+ /*
+ * Update the system stratum, leap bits, root delay,
+ * root dispersion, reference ID and reference time. We
+ * also update select dispersion and max frequency
+ * error.
+ */
+ sys_stratum = sys_peer->stratum + 1;
+ if (sys_stratum == 1)
+ sys_refid = sys_peer->refid;
+ else
+ sys_refid = sys_peer->srcadr.sin_addr.s_addr;
+ sys_reftime = sys_peer->rec;
+ sys_rootdelay = sys_peer->rootdelay + fabs(sys_peer->delay);
+ sys_leap = leap_consensus;
+ }
+ if (oleap != sys_leap)
+ report_event(EVNT_SYNCCHG, (struct peer *)0);
+ if (ostratum != sys_stratum)
+ report_event(EVNT_PEERSTCHG, (struct peer *)0);
+}
+
+
+/*
+ * poll_update - update peer poll interval. See Section 3.4.9 of the
+ * spec.
+ */
+void
+poll_update(
+ struct peer *peer,
+ int hpoll
+ )
+{
+ long update;
+
+ /*
+ * The wiggle-the-poll-interval dance. Broadcasters dance only
+ * the minpoll beat. Reference clock partners sit this one out.
+ * Dancers surviving the clustering algorithm beat to the system
+ * clock. Broadcast clients are usually lead by their broadcast
+ * partner, but faster in the initial mating dance.
+ */
+ if (peer->hmode == MODE_BROADCAST) {
+ peer->hpoll = peer->minpoll;
+ } else if (peer->flags & FLAG_SYSPEER) {
+ peer->hpoll = sys_poll;
+ } else {
+ if (hpoll > peer->maxpoll)
+ peer->hpoll = peer->maxpoll;
+ else if (hpoll < peer->minpoll)
+ peer->hpoll = peer->minpoll;
+ else
+ peer->hpoll = hpoll;
+ }
+ if (peer->burst > 0) {
+ if (peer->nextdate != current_time)
+ return;
+ if (peer->flags & FLAG_REFCLOCK)
+ peer->nextdate++;
+ else if (peer->reach & 0x1)
+ peer->nextdate += RANDPOLL(BURST_INTERVAL2);
+ else
+ peer->nextdate += RANDPOLL(BURST_INTERVAL1);
+ } else {
+ update = max(min(peer->ppoll, peer->hpoll), peer->minpoll);
+ peer->nextdate = peer->outdate + RANDPOLL(update);
+ }
+#ifdef DEBUG
+ if (debug > 1)
+ printf("poll_update: at %lu %s poll %d burst %d last %lu next %lu\n",
+ current_time, ntoa(&peer->srcadr), hpoll, peer->burst,
+ peer->outdate, peer->nextdate);
+#endif
+}
+
+
+/*
+ * clear - clear peer filter registers. See Section 3.4.8 of the spec.
+ */
+void
+peer_clear(
+ register struct peer *peer
+ )
+{
+ register int i;
+
+ memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO);
+ peer->estbdelay = sys_bdelay;
+ peer->hpoll = peer->minpoll;
+ peer->pollsw = FALSE;
+ peer->variance = MAXDISPERSE;
+ peer->epoch = current_time;
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_order[i] = i;
+ peer->filter_disp[i] = MAXDISPERSE;
+ peer->filter_epoch[i] = current_time;
+ }
+ poll_update(peer, peer->minpoll);
+
+ /*
+ * Since we have a chance to correct possible funniness in
+ * our selection of interfaces on a multihomed host, do so
+ * by setting us to no particular interface.
+ * WARNING: do so only in non-broadcast mode!
+ */
+ if (peer->hmode != MODE_BROADCAST)
+ peer->dstadr = any_interface;
+}
+
+
+/*
+ * clock_filter - add incoming clock sample to filter register and run
+ * the filter procedure to find the best sample.
+ */
+void
+clock_filter(
+ register struct peer *peer,
+ double sample_offset,
+ double sample_delay,
+ double sample_disp
+ )
+{
+ register int i, j, k, n = 0;
+ register u_char *ord;
+ double distance[NTP_SHIFT];
+ double x, y, z, off;
+
+ /*
+ * Update error bounds and calculate distances. Also initialize
+ * sort index vector.
+ */
+ x = CLOCK_PHI * (current_time - peer->update);
+ peer->update = current_time;
+ ord = peer->filter_order;
+ j = peer->filter_nextpt;
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_disp[j] += x;
+ if (peer->filter_disp[j] > MAXDISPERSE)
+ peer->filter_disp[j] = MAXDISPERSE;
+ distance[i] = fabs(peer->filter_delay[j]) / 2 +
+ peer->filter_disp[j];
+ ord[i] = j;
+ if (--j < 0)
+ j += NTP_SHIFT;
+ }
+
+ /*
+ * Insert the new sample at the beginning of the register.
+ */
+ peer->filter_offset[peer->filter_nextpt] = sample_offset;
+ peer->filter_delay[peer->filter_nextpt] = sample_delay;
+ x = LOGTOD(peer->precision) + LOGTOD(sys_precision) + sample_disp;
+ peer->filter_disp[peer->filter_nextpt] = min(x, MAXDISPERSE);
+ peer->filter_epoch[peer->filter_nextpt] = current_time;
+ distance[0] = min(x + fabs(sample_delay) / 2, MAXDISTANCE);
+ peer->filter_nextpt++;
+ if (peer->filter_nextpt >= NTP_SHIFT)
+ peer->filter_nextpt = 0;
+
+ /*
+ * Sort the samples in the register by distance. The winning
+ * sample will be in ord[0]. Sort the samples only if they
+ * are younger than the Allen intercept.
+ */
+ y = min(allan_xpt, NTP_SHIFT * ULOGTOD(sys_poll));
+ for (n = 0; n < NTP_SHIFT && current_time -
+ peer->filter_epoch[ord[n]] <= y; n++) {
+ for (j = 0; j < n; j++) {
+ if (distance[j] > distance[n]) {
+ x = distance[j];
+ k = ord[j];
+ distance[j] = distance[n];
+ ord[j] = ord[n];
+ distance[n] = x;
+ ord[n] = k;
+ }
+ }
+ }
+
+ /*
+ * Compute the error bound and standard error.
+ */
+ x = y = z = off = 0.;
+ for (i = NTP_SHIFT - 1; i >= 0; i--) {
+ x = NTP_FWEIGHT * (x + peer->filter_disp[ord[i]]);
+ if (i < n) {
+ z += 1. / distance[i];
+ off += peer->filter_offset[ord[i]] / distance[i];
+ y += DIFF(peer->filter_offset[ord[i]],
+ peer->filter_offset[ord[0]]);
+ }
+ }
+ peer->delay = peer->filter_delay[ord[0]];
+ peer->variance = min(y / n, MAXDISPERSE);
+ peer->disp = min(x, MAXDISPERSE);
+ peer->epoch = current_time;
+ x = peer->offset;
+ if (peer->flags & FLAG_BURST)
+ peer->offset = off / z;
+ else
+ peer->offset = peer->filter_offset[ord[0]];
+
+ /*
+ * A new sample is useful only if it is younger than the last
+ * one used.
+ */
+ if (peer->filter_epoch[ord[0]] > peer->epoch) {
+#ifdef DEBUG
+ if (debug)
+ printf("clock_filter: discard %lu\n",
+ peer->filter_epoch[ord[0]] - peer->epoch);
+#endif
+ return;
+ }
+
+ /*
+ * If the offset exceeds the dispersion 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 (fabs(x - peer->offset) > CLOCK_SGATE &&
+ peer->filter_epoch[ord[0]] - peer->epoch < (1 <<
+ (sys_poll + 1))) {
+#ifdef DEBUG
+ if (debug)
+ printf("clock_filter: popcorn spike %.6f\n", x);
+#endif
+ return;
+ }
+ peer->epoch = peer->filter_epoch[ord[0]];
+ peer->pollsw = TRUE;
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "clock_filter: offset %.6f delay %.6f disp %.6f std %.6f, age %lu\n",
+ peer->offset, peer->delay, peer->disp,
+ SQRT(peer->variance), current_time - peer->epoch);
+#endif
+}
+
+
+/*
+ * clock_select - find the pick-of-the-litter clock
+ */
+void
+clock_select(void)
+{
+ register struct peer *peer;
+ int i;
+ int nlist, nl3;
+ double d, e, f;
+ int j;
+ int n;
+ int allow, found, k;
+ double high, low;
+ double synch[NTP_MAXCLOCK], error[NTP_MAXCLOCK];
+ struct peer *osys_peer;
+ struct peer *typeacts = 0;
+ struct peer *typelocal = 0;
+ struct peer *typepps = 0;
+ struct peer *typeprefer = 0;
+ struct peer *typesystem = 0;
+
+ static int list_alloc = 0;
+ static struct endpoint *endpoint = NULL;
+ static int *index = NULL;
+ static struct peer **peer_list = NULL;
+ static u_int endpoint_size = 0;
+ static u_int index_size = 0;
+ static u_int peer_list_size = 0;
+
+ /*
+ * Initialize. If a prefer peer does not survive this thing,
+ * the pps_update switch will remain zero.
+ */
+ pps_update = 0;
+ nlist = 0;
+ low = 1e9;
+ high = -1e9;
+ for (n = 0; n < HASH_SIZE; n++)
+ nlist += peer_hash_count[n];
+ if (nlist > list_alloc) {
+ if (list_alloc > 0) {
+ free(endpoint);
+ free(index);
+ free(peer_list);
+ }
+ while (list_alloc < nlist) {
+ list_alloc += 5;
+ endpoint_size += 5 * 3 * sizeof *endpoint;
+ index_size += 5 * 3 * sizeof *index;
+ peer_list_size += 5 * sizeof *peer_list;
+ }
+ endpoint = (struct endpoint *)emalloc(endpoint_size);
+ index = (int *)emalloc(index_size);
+ peer_list = (struct peer **)emalloc(peer_list_size);
+ }
+
+ /*
+ * This first chunk of code is supposed to go through all
+ * peers we know about to find the peers which are most likely
+ * to succeed. We run through the list doing the sanity checks
+ * and trying to insert anyone who looks okay.
+ */
+ nlist = nl3 = 0; /* none yet */
+ for (n = 0; n < HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = peer->next) {
+ peer->flags &= ~FLAG_SYSPEER;
+ peer->status = CTL_PST_SEL_REJECT;
+ if (peer->flags & FLAG_NOSELECT)
+ continue; /* noselect (survey only) */
+ if (peer->reach == 0)
+ continue; /* unreachable */
+ if (peer->stratum > 1 && peer->refid ==
+ peer->dstadr->sin.sin_addr.s_addr)
+ continue; /* sync loop */
+ if (root_distance(peer) >= MAXDISTANCE + 2 *
+ CLOCK_PHI * ULOGTOD(sys_poll)) {
+ peer->seldisptoolarge++;
+ continue; /* too noisy or broken */
+ }
+
+ /*
+ * Don't allow the local-clock or acts drivers
+ * in the kitchen at this point, unless the
+ * prefer peer. Do that later, but only if
+ * nobody else is around.
+ */
+ if (peer->refclktype == REFCLK_LOCALCLOCK
+#if defined(VMS) && defined(VMS_LOCALUNIT)
+ /* wjm: local unit VMS_LOCALUNIT taken seriously */
+ && REFCLOCKUNIT(&peer->srcadr) != VMS_LOCALUNIT
+#endif /* VMS && VMS_LOCALUNIT */
+ ) {
+ typelocal = peer;
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no local clock */
+ }
+ if (peer->sstclktype == CTL_SST_TS_TELEPHONE) {
+ typeacts = peer;
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no acts */
+ }
+
+ /*
+ * If we get this far, we assume the peer is
+ * acceptable.
+ */
+ 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[index[i]].val)
+ break;
+ index[i + 3] = index[i];
+ }
+ index[i + 3] = nl3;
+ endpoint[nl3].type = 1;
+ endpoint[nl3++].val = e;
+
+ e = e - f; /* Center point */
+ for ( ; i >= 0; i--) {
+ if (e >= endpoint[index[i]].val)
+ break;
+ index[i + 2] = index[i];
+ }
+ index[i + 2] = nl3;
+ endpoint[nl3].type = 0;
+ endpoint[nl3++].val = e;
+
+ e = e - f; /* Lower end */
+ for ( ; i >= 0; i--) {
+ if (e >= endpoint[index[i]].val)
+ break;
+ index[i + 1] = index[i];
+ }
+ index[i + 1] = nl3;
+ endpoint[nl3].type = -1;
+ endpoint[nl3++].val = e;
+ }
+ }
+#ifdef DEBUG
+ if (debug > 1)
+ for (i = 0; i < nl3; i++)
+ printf("select: endpoint %2d %.6f\n",
+ endpoint[index[i]].type, endpoint[index[i]].val);
+#endif
+ i = 0;
+ j = nl3 - 1;
+ allow = nlist; /* falsetickers assumed */
+ found = 0;
+ while (allow > 0) {
+ allow--;
+ for (n = 0; i <= j; i++) {
+ n += endpoint[index[i]].type;
+ if (n < 0)
+ break;
+ if (endpoint[index[i]].type == 0)
+ found++;
+ }
+ for (n = 0; i <= j; j--) {
+ n += endpoint[index[j]].type;
+ if (n > 0)
+ break;
+ if (endpoint[index[j]].type == 0)
+ found++;
+ }
+ if (found > allow)
+ break;
+ low = endpoint[index[i++]].val;
+ high = endpoint[index[j--]].val;
+ }
+
+ /*
+ * If no survivors remain at this point, check if the acts or
+ * local clock drivers have been found. If so, nominate one of
+ * them as the only survivor. Otherwise, give up and declare us
+ * unsynchronized.
+ */
+ if ((allow << 1) >= nlist) {
+ 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 (sys_peer != 0) {
+ report_event(EVNT_PEERSTCHG,
+ (struct peer *)0);
+ NLOG(NLOG_SYNCSTATUS)
+ msyslog(LOG_INFO, "synchronisation lost");
+ }
+ sys_peer = 0;
+ return;
+ }
+ }
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: low %.6f high %.6f\n", low, high);
+#endif
+
+ /*
+ * Clustering algorithm. Process intersection list to discard
+ * outlyers. Construct candidate list in cluster order
+ * determined by the sum of peer synchronization distance plus
+ * scaled stratum. We must find at least one peer.
+ */
+ j = 0;
+ for (i = 0; i < nlist; i++) {
+ peer = peer_list[i];
+ if (nlist > 1 && (low >= peer->offset ||
+ peer->offset >= high))
+ continue;
+ peer->status = CTL_PST_SEL_CORRECT;
+ d = root_distance(peer) + peer->stratum * MAXDISPERSE;
+ if (j >= NTP_MAXCLOCK) {
+ if (d >= synch[j - 1])
+ continue;
+ else
+ j--;
+ }
+ for (k = j; k > 0; k--) {
+ if (d >= synch[k - 1])
+ break;
+ synch[k] = synch[k - 1];
+ peer_list[k] = peer_list[k - 1];
+ }
+ peer_list[k] = peer;
+ synch[k] = d;
+ j++;
+ }
+ nlist = j;
+
+#ifdef DEBUG
+ if (debug > 1)
+ for (i = 0; i < nlist; i++)
+ printf("select: %s distance %.6f\n",
+ ntoa(&peer_list[i]->srcadr), synch[i]);
+#endif
+
+ /*
+ * Now, prune outlyers by root dispersion. Continue as long as
+ * there are more than NTP_MINCLOCK survivors and the minimum
+ * select dispersion is greater than the maximum peer
+ * dispersion. Stop if we are about to discard a prefer peer.
+ */
+ for (i = 0; i < nlist; i++) {
+ peer = peer_list[i];
+ error[i] = peer->variance;
+ if (i < NTP_CANCLOCK)
+ peer->status = CTL_PST_SEL_SELCAND;
+ else
+ peer->status = CTL_PST_SEL_DISTSYSPEER;
+ }
+ while (1) {
+ sys_maxd = 0;
+ d = error[0];
+ for (k = i = nlist - 1; i >= 0; i--) {
+ double sdisp = 0;
+
+ for (j = nlist - 1; j > 0; j--) {
+ sdisp = NTP_SWEIGHT * (sdisp +
+ DIFF(peer_list[i]->offset,
+ peer_list[j]->offset));
+ }
+ if (sdisp > sys_maxd) {
+ sys_maxd = sdisp;
+ k = i;
+ }
+ if (error[i] < d)
+ d = error[i];
+ }
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "select: survivors %d select %.6f peer %.6f\n",
+ nlist, SQRT(sys_maxd), SQRT(d));
+#endif
+ if (nlist <= NTP_MINCLOCK || sys_maxd <= d ||
+ peer_list[k]->flags & FLAG_PREFER)
+ break;
+ for (j = k + 1; j < nlist; j++) {
+ peer_list[j - 1] = peer_list[j];
+ error[j - 1] = error[j];
+ }
+ nlist--;
+ }
+#ifdef DEBUG
+ if (debug > 1) {
+ for (i = 0; i < nlist; i++)
+ printf(
+ "select: %s offset %.6f, distance %.6f poll %d\n",
+ ntoa(&peer_list[i]->srcadr), peer_list[i]->offset,
+ synch[i], peer_list[i]->pollsw);
+ }
+#endif
+
+ /*
+ * What remains is a list of not greater than NTP_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 the peers
+ * at the lowest stratum. Of these, OR the leap bits on the
+ * assumption that, if some of them honk nonzero bits, they must
+ * know what they are doing. Also, check for prefer and pps
+ * peers. If a prefer peer is found within clock_max, update the
+ * pps switch. Of the other peers not at the lowest stratum,
+ * check if the system peer is among them and, if found, zap
+ * him. We note that the head of the list is at the lowest
+ * stratum and that unsynchronized peers cannot survive this
+ * far.
+ */
+ leap_consensus = 0;
+ for (i = nlist - 1; i >= 0; i--) {
+ peer_list[i]->status = CTL_PST_SEL_SYNCCAND;
+ peer_list[i]->flags |= FLAG_SYSPEER;
+ poll_update(peer_list[i], peer_list[i]->hpoll);
+ if (peer_list[i]->stratum == peer_list[0]->stratum) {
+ leap_consensus |= peer_list[i]->leap;
+ if (peer_list[i]->refclktype == REFCLK_ATOM_PPS)
+ typepps = peer_list[i];
+ if (peer_list[i] == sys_peer)
+ typesystem = peer_list[i];
+ if (peer_list[i]->flags & FLAG_PREFER) {
+ typeprefer = peer_list[i];
+ if (fabs(typeprefer->offset) < clock_max)
+ pps_update = 1;
+ }
+ } else {
+ if (peer_list[i] == sys_peer)
+ sys_peer = 0;
+ }
+ }
+
+ /*
+ * 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.
+ */
+ osys_peer = sys_peer;
+ if (typeprefer && (typeprefer->refclktype == REFCLK_LOCALCLOCK ||
+ typeprefer->sstclktype == CTL_SST_TS_TELEPHONE || !typepps)) {
+ sys_peer = typeprefer;
+ sys_peer->status = CTL_PST_SEL_SYSPEER;
+ sys_offset = sys_peer->offset;
+ sys_epsil = sys_peer->variance;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: prefer offset %.6f\n", sys_offset);
+#endif
+ } else if (typepps && pps_update) {
+ sys_peer = typepps;
+ sys_peer->status = CTL_PST_SEL_PPS;
+ sys_offset = sys_peer->offset;
+ sys_epsil = sys_peer->variance;
+ if (!pps_control)
+ NLOG(NLOG_SYSEVENT) /* conditional syslog */
+ 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 = peer_list[0];
+ sys_peer->status = CTL_PST_SEL_SYSPEER;
+ sys_offset = clock_combine(peer_list, nlist);
+ sys_epsil = sys_peer->variance + sys_maxd;
+#ifdef DEBUG
+ if (debug > 1)
+ printf("select: combine offset %.6f\n",
+ sys_offset);
+#endif
+ }
+ if (osys_peer != sys_peer)
+ report_event(EVNT_PEERSTCHG, (struct peer *)0);
+ 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
+ )
+{
+ return ((fabs(peer->delay) + peer->rootdelay) / 2 +
+ peer->rootdispersion + peer->disp +
+ SQRT(peer->variance) + CLOCK_PHI * (current_time -
+ peer->update));
+}
+
+/*
+ * peer_xmit - send packet for persistent association.
+ */
+static void
+peer_xmit(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct pkt xpkt;
+ int find_rtt = (peer->cast_flags & MDF_MCAST) &&
+ peer->hmode != MODE_BROADCAST;
+ int sendlen;
+
+ /*
+ * Initialize protocol 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 +
+ LOGTOD(sys_precision)));
+ xpkt.refid = sys_refid;
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ HTONL_FP(&peer->org, &xpkt.org);
+ HTONL_FP(&peer->rec, &xpkt.rec);
+
+ /*
+ * Authenticate the packet if enabled and either configured or
+ * the previous packet was authenticated. If for some reason the
+ * key associated with the key identifier is not in the key
+ * cache, then honk key zero.
+ */
+ sendlen = LEN_PKT_NOMAC;
+ if (peer->flags & FLAG_AUTHENABLE) {
+ u_long xkeyid;
+ l_fp xmt_tx;
+
+ /*
+ * Transmit encrypted packet compensated for the
+ * encryption delay.
+ */
+#ifdef MD5
+ if (peer->flags & FLAG_SKEY) {
+
+ /*
+ * In SKEY mode, allocate and initialize a key list 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 we have to wait until the receive side of the
+ * socket is bound and the server address confirmed.
+ */
+ if (ntohl(peer->dstadr->sin.sin_addr.s_addr) == 0 &&
+ ntohl(peer->dstadr->bcast.sin_addr.s_addr) == 0)
+ peer->keyid = 0;
+ else {
+ if (peer->keylist == 0) {
+ make_keylist(peer);
+ } else {
+ authtrust(peer->keylist[peer->keynumber], 0);
+ if (peer->keynumber == 0)
+ make_keylist(peer);
+ else {
+ peer->keynumber--;
+ xkeyid = peer->keylist[peer->keynumber];
+ if (!authistrusted(xkeyid))
+ make_keylist(peer);
+ }
+ }
+ peer->keyid = peer->keylist[peer->keynumber];
+ xpkt.keyid1 = htonl(2 * sizeof(u_int32));
+ xpkt.keyid2 = htonl(sys_private);
+ sendlen += 2 * sizeof(u_int32);
+ }
+ }
+#endif /* MD5 */
+ xkeyid = peer->keyid;
+ get_systime(&peer->xmt);
+ L_ADD(&peer->xmt, &sys_authdelay);
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ sendlen += authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+ get_systime(&xmt_tx);
+ sendpkt(&peer->srcadr, find_rtt ? any_interface :
+ peer->dstadr,
+ ((peer->cast_flags & MDF_MCAST) && !find_rtt) ?
+ ((peer->cast_flags & MDF_ACAST) ? -7 : peer->ttl) : -7,
+ &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 DEBUG
+ if (debug)
+ printf(
+ "transmit: at %ld to %s mode %d keyid %08lx index %d\n",
+ current_time, ntoa(&peer->srcadr),
+ peer->hmode, xkeyid, peer->keynumber);
+#endif
+ } else {
+ /*
+ * Transmit non-authenticated packet.
+ */
+ get_systime(&(peer->xmt));
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ sendpkt(&(peer->srcadr), find_rtt ? any_interface :
+ peer->dstadr,
+ ((peer->cast_flags & MDF_MCAST) && !find_rtt) ?
+ ((peer->cast_flags & MDF_ACAST) ? -7 : peer->ttl) : -8,
+ &xpkt, sendlen);
+ peer->sent++;
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld to %s mode %d\n",
+ current_time, ntoa(&peer->srcadr),
+ peer->hmode);
+#endif
+ }
+}
+
+/*
+ * fast_xmit - Send packet for nonpersistent association.
+ */
+static void
+fast_xmit(
+ struct recvbuf *rbufp, /* receive packet pointer */
+ int xmode, /* transmit mode */
+ u_long xkeyid /* transmit key ID */
+ )
+{
+ struct pkt xpkt;
+ struct pkt *rpkt;
+ int sendlen;
+ l_fp xmt_ts;
+
+ /*
+ * Initialize transmit packet header fields in the receive
+ * buffer provided. We leave some fields intact as received.
+ */
+ rpkt = &rbufp->recv_pkt;
+ 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.ppoll = rpkt->ppoll;
+ xpkt.precision = sys_precision;
+ xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay));
+ xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion +
+ LOGTOD(sys_precision)));
+ xpkt.refid = sys_refid;
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ xpkt.org = rpkt->xmt;
+ HTONL_FP(&rbufp->recv_time, &xpkt.rec);
+ sendlen = LEN_PKT_NOMAC;
+ if (rbufp->recv_length > sendlen) {
+ l_fp xmt_tx;
+
+ /*
+ * Transmit encrypted packet compensated for the
+ * encryption delay.
+ */
+ if (xkeyid > NTP_MAXKEY) {
+ xpkt.keyid1 = htonl(2 * sizeof(u_int32));
+ xpkt.keyid2 = htonl(sys_private);
+ sendlen += 2 * sizeof(u_int32);
+ }
+ get_systime(&xmt_ts);
+ L_ADD(&xmt_ts, &sys_authdelay);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ sendlen += authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+ get_systime(&xmt_tx);
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -9, &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 to %s mode %d keyid %08lx\n",
+ current_time, ntoa(&rbufp->recv_srcadr),
+ xmode, xkeyid);
+#endif
+ } else {
+
+ /*
+ * Transmit non-authenticated packet.
+ */
+ get_systime(&xmt_ts);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -10, &xpkt,
+ sendlen);
+#ifdef DEBUG
+ if (debug)
+ printf("transmit: at %ld to %s mode %d\n",
+ current_time, ntoa(&rbufp->recv_srcadr),
+ xmode);
+#endif
+ }
+}
+
+#ifdef MD5
+/*
+ * Compute key list
+ */
+static void
+make_keylist(
+ struct peer *peer
+ )
+{
+ int i;
+ u_long keyid;
+ u_long ltemp;
+
+ /*
+ * Allocate the key list if necessary.
+ */
+ if (peer->keylist == 0)
+ peer->keylist = (u_long *)emalloc(sizeof(u_long) *
+ 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.
+ */
+ ltemp = sys_automax;
+ for (i = 0; i < NTP_MAXSESSION; i++) {
+ peer->keylist[i] = keyid;
+ peer->keynumber = i;
+ keyid = session_key(
+ ntohl(peer->dstadr->sin.sin_addr.s_addr),
+ (peer->hmode == MODE_BROADCAST || (peer->flags &
+ FLAG_MCAST2)) ?
+ ntohl(peer->dstadr->bcast.sin_addr.s_addr):
+ ntohl(peer->srcadr.sin_addr.s_addr),
+ keyid, ltemp);
+ ltemp -= 1 << peer->hpoll;
+ if (auth_havekey(keyid) || keyid <= NTP_MAXKEY ||
+ ltemp <= (1 << (peer->hpoll + 1)))
+ break;
+ }
+}
+#endif /* MD5 */
+
+/*
+ * Find the precision of this particular machine
+ */
+#define DUSECS 1000000 /* us in a s */
+#define HUSECS (1 << 20) /* approx DUSECS for shifting etc */
+#define MINSTEP 5 /* minimum clock increment (us) */
+#define MAXSTEP 20000 /* maximum clock increment (us) */
+#define MINLOOPS 5 /* minimum number of step samples */
+
+/*
+ * This routine calculates the differences between successive calls to
+ * gettimeofday(). If a difference is less than zero, the us field
+ * has rolled over to the next second, so we add a second in us. If
+ * the difference is greater than zero and less than MINSTEP, the
+ * clock has been advanced by a small amount to avoid standing still.
+ * If the clock has advanced by a greater amount, then a timer interrupt
+ * has occurred and this amount represents the precision of the clock.
+ * In order to guard against spurious values, which could occur if we
+ * happen to hit a fat interrupt, we do this for MINLOOPS times and
+ * keep the minimum value obtained.
+ */
+int
+default_get_precision(void)
+{
+ struct timeval tp;
+#if !defined(SYS_WINNT) && !defined(VMS) && !defined(_SEQUENT_)
+ struct timezone tzp;
+#elif defined(VMS) || defined(_SEQUENT_)
+ struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+ } tzp;
+#endif /* defined(VMS) || defined(_SEQUENT_) */
+ long last;
+ int i;
+ long diff;
+ long val;
+ long usec;
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ u_long freq;
+ int j;
+
+ /* Try to see if we can find the frequency of of the counter
+ * which drives our timekeeping
+ */
+ j = sizeof freq;
+ i = sysctlbyname("kern.timecounter.frequency",
+ &freq, &j , 0, 0);
+ if (i)
+ i = sysctlbyname("machdep.tsc_freq",
+ &freq, &j , 0, 0);
+ if (i)
+ i = sysctlbyname("machdep.i586_freq",
+ &freq, &j , 0, 0);
+ if (i)
+ i = sysctlbyname("machdep.i8254_freq",
+ &freq, &j , 0, 0);
+ if (!i) {
+ for (i = 1; freq ; i--)
+ freq >>= 1;
+ return (i);
+ }
+#endif
+ usec = 0;
+ val = MAXSTEP;
+#ifdef HAVE_GETCLOCK
+ (void) getclock(TIMEOFDAY, &ts);
+ tp.tv_sec = ts.tv_sec;
+ tp.tv_usec = ts.tv_nsec / 1000;
+#else /* not HAVE_GETCLOCK */
+ GETTIMEOFDAY(&tp, &tzp);
+#endif /* not HAVE_GETCLOCK */
+ last = tp.tv_usec;
+ for (i = 0; i < MINLOOPS && usec < HUSECS;) {
+#ifdef HAVE_GETCLOCK
+ (void) getclock(TIMEOFDAY, &ts);
+ tp.tv_sec = ts.tv_sec;
+ tp.tv_usec = ts.tv_nsec / 1000;
+#else /* not HAVE_GETCLOCK */
+ GETTIMEOFDAY(&tp, &tzp);
+#endif /* not HAVE_GETCLOCK */
+ diff = tp.tv_usec - last;
+ last = tp.tv_usec;
+ if (diff < 0)
+ diff += DUSECS;
+ usec += diff;
+ if (diff > MINSTEP) {
+ i++;
+ if (diff < val)
+ val = diff;
+ }
+ }
+ NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "precision = %ld usec", val);
+ if (usec >= HUSECS)
+ val = MINSTEP; /* val <= MINSTEP; fast machine */
+ diff = HUSECS;
+ for (i = 0; diff > val; i--)
+ diff >>= 1;
+ return (i);
+}
+
+/*
+ * init_proto - initialize the protocol module's data
+ */
+void
+init_proto(void)
+{
+ l_fp dummy;
+
+ /*
+ * Fill in the sys_* stuff. Default is don't listen to
+ * broadcasting, authenticate.
+ */
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ sys_precision = (s_char)default_get_precision();
+ sys_rootdelay = 0;
+ sys_rootdispersion = 0;
+ sys_refid = 0;
+ L_CLR(&sys_reftime);
+ sys_peer = 0;
+ get_systime(&dummy);
+ sys_bclient = 0;
+ sys_bdelay = DEFBROADDELAY;
+#if defined(DES) || defined(MD5)
+ sys_authenticate = 1;
+#else
+ sys_authenticate = 0;
+#endif
+ L_CLR(&sys_authdelay);
+ sys_authdly[0] = sys_authdly[1] = 0;
+ sys_stattime = 0;
+ sys_badstratum = 0;
+ sys_oldversionpkt = 0;
+ sys_newversionpkt = 0;
+ sys_badlength = 0;
+ sys_unknownversion = 0;
+ sys_processed = 0;
+ sys_badauth = 0;
+ sys_manycastserver = 0;
+ sys_automax = 1 << NTP_AUTOMAX;
+
+ /*
+ * Default these to enable
+ */
+ ntp_enable = 1;
+#ifndef KERNEL_FLL_BUG
+ kern_enable = 1;
+#endif
+ msyslog(LOG_DEBUG, "kern_enable is %d", kern_enable);
+ stats_control = 1;
+
+ /*
+ * Some system clocks should only be adjusted in 10ms increments.
+ */
+#if defined RELIANTUNIX_CLOCK
+ systime_10ms_ticks = 1; /* Reliant UNIX */
+#elif defined SCO5_CLOCK
+ if (sys_precision >= (s_char)-10) /* pre- SCO OpenServer 5.0.6 */
+ systime_10ms_ticks = 1;
+#endif
+ if (systime_10ms_ticks)
+ msyslog(LOG_INFO, "using 10ms tick adjustments");
+}
+
+
+/*
+ * proto_config - configure the protocol module
+ */
+void
+proto_config(
+ int item,
+ u_long value,
+ double dvalue
+ )
+{
+ /*
+ * Figure out what he wants to change, then do it
+ */
+ switch (item) {
+ case PROTO_KERNEL:
+ /*
+ * Turn on/off kernel discipline
+ */
+ kern_enable = (int)value;
+ break;
+
+ case PROTO_NTP:
+ /*
+ * Turn on/off clock discipline
+ */
+ ntp_enable = (int)value;
+ break;
+
+ case PROTO_MONITOR:
+ /*
+ * Turn on/off monitoring
+ */
+ if (value)
+ mon_start(MON_ON);
+ else
+ mon_stop(MON_ON);
+ break;
+
+ case PROTO_FILEGEN:
+ /*
+ * Turn on/off statistics
+ */
+ stats_control = (int)value;
+ break;
+
+ case PROTO_BROADCLIENT:
+ /*
+ * Turn on/off facility to listen to broadcasts
+ */
+ sys_bclient = (int)value;
+ if (value)
+ io_setbclient();
+ else
+ io_unsetbclient();
+ break;
+
+ case PROTO_MULTICAST_ADD:
+ /*
+ * Add muliticast group address
+ */
+ io_multicast_add(value);
+ break;
+
+ case PROTO_MULTICAST_DEL:
+ /*
+ * Delete multicast group address
+ */
+ io_multicast_del(value);
+ break;
+
+ case PROTO_BROADDELAY:
+ /*
+ * Set default broadcast delay
+ */
+ sys_bdelay = dvalue;
+ break;
+
+ case PROTO_AUTHENTICATE:
+ /*
+ * Specify the use of authenticated data
+ */
+ sys_authenticate = (int)value;
+ break;
+
+ default:
+ /*
+ * Log this error
+ */
+ msyslog(LOG_ERR, "proto_config: illegal item %d, value %ld",
+ item, value);
+ break;
+ }
+}
+
+
+/*
+ * proto_clr_stats - clear protocol stat counters
+ */
+void
+proto_clr_stats(void)
+{
+ sys_badstratum = 0;
+ sys_oldversionpkt = 0;
+ sys_newversionpkt = 0;
+ sys_unknownversion = 0;
+ sys_badlength = 0;
+ sys_processed = 0;
+ sys_badauth = 0;
+ sys_stattime = current_time;
+ sys_limitrejected = 0;
+}
diff --git a/contrib/ntp/ntpd/ntp_refclock.c b/contrib/ntp/ntpd/ntp_refclock.c
new file mode 100644
index 0000000..c7b9166
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_refclock.c
@@ -0,0 +1,1322 @@
+/*
+ * ntp_refclock - processing support for reference clocks
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef REFCLOCK
+
+#ifdef TTYCLK
+# ifdef SCO5_CLOCK
+# include <sys/sio.h>
+# else
+# include <sys/clkdefs.h>
+# endif
+#endif /* TTYCLK */
+
+#ifdef HAVE_PPSCLOCK_H
+#include <sys/ppsclock.h>
+#endif /* HAVE_PPSCLOCK_H */
+
+#ifdef HAVE_PPSAPI
+#include <sys/timepps.h>
+#endif /* HAVE_PPSAPI */
+
+/*
+ * 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, 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.
+ *
+ * The routines include support for the 1-pps signal provided by some
+ * radios and connected via a level converted described in the gadget
+ * directory. The signal is captured using a serial port and one of
+ * three STREAMS modules described in the refclock_atom.c file. For the
+ * highest precision, the signal is captured using the carrier-detect
+ * line of a serial port and either the ppsclock or ppsapi streams
+ * module or some devilish ioctl() folks keep slipping in as a patch. Be
+ * advised ALL support for other than the duly standardized ppsapi
+ * interface will eventually be withdrawn.
+ */
+#define MAXUNIT 4 /* max units */
+
+#if defined(PPS) || defined(HAVE_PPSAPI)
+int fdpps; /* pps file descriptor */
+#endif /* PPS HAVE_PPSAPI */
+
+#define FUDGEFAC .1 /* fudge correction factor */
+
+/*
+ * 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 *));
+
+#ifdef HAVE_PPSAPI
+extern int pps_assert; /* capture edge 1:assert, 0:clear */
+extern int pps_hardpps; /* PPS kernel 1:on, 0:off */
+#endif /* HAVE_PPSAPI */
+
+/*
+ * 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;
+
+ if (!(pp = peer->procptr))
+ 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 = code;
+ pp->lastevent = code;
+ if (code == CEVNT_FAULT)
+ msyslog(LOG_ERR,
+ "clock %s event '%s' (0x%02x)",
+ refnumtoa(peer->srcadr.sin_addr.s_addr),
+ ceventstr(code), code);
+ else {
+ NLOG(NLOG_CLOCKEVENT)
+ msyslog(LOG_INFO,
+ "clock %s event '%s' (0x%02x)",
+ refnumtoa(peer->srcadr.sin_addr.s_addr),
+ ceventstr(code), code);
+ }
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("clock %s event '%s' (0x%02x)\n",
+ refnumtoa(peer->srcadr.sin_addr.s_addr),
+ 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 (!ISREFCLOCKADR(&peer->srcadr)) {
+ msyslog(LOG_ERR,
+ "refclock_newpeer: clock address %s invalid",
+ ntoa(&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);
+ }
+ refclock_unpeer(peer);
+
+ /*
+ * Allocate and initialize interface structure
+ */
+ if (!(pp = (struct refclockproc *)emalloc(sizeof(struct refclockproc))))
+ return (0);
+ memset((char *)pp, 0, sizeof(struct refclockproc));
+ typeunit[clktype][unit] = peer;
+ peer->procptr = pp;
+
+ /*
+ * Initialize structures
+ */
+ peer->refclktype = clktype;
+ peer->refclkunit = unit;
+ peer->flags |= FLAG_REFCLOCK;
+ peer->stratum = STRATUM_REFCLOCK;
+ peer->refid = peer->srcadr.sin_addr.s_addr;
+ peer->maxpoll = peer->minpoll;
+
+ pp->type = clktype;
+ pp->timestarted = current_time;
+
+ /*
+ * If the interface has been set to any_interface, set it to the
+ * loopback address if we have one. This is so that peers which
+ * are unreachable are easy to see in the peer display.
+ */
+ if (peer->dstadr == any_interface && loopback_interface != 0)
+ peer->dstadr = loopback_interface;
+
+ /*
+ * 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))) {
+ free(pp);
+ return (0);
+ }
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->maxpoll;
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ else
+ peer->refid = peer->srcadr.sin_addr.s_addr;
+ 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;
+ int hpoll;
+ 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.
+ */
+ hpoll = peer->hpoll;
+ next = peer->outdate;
+ if (peer->burst == 0) {
+ u_char oreach;
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_transmit: at %ld %s\n",
+ current_time, ntoa(&(peer->srcadr)));
+#endif
+
+ /*
+ * Update reachability and poll variables like the
+ * network code.
+ */
+ oreach = peer->reach;
+ if (oreach & 0x01)
+ peer->valid++;
+ if (oreach & 0x80)
+ peer->valid--;
+ peer->reach <<= 1;
+ if (peer->reach == 0) {
+ if (oreach != 0) {
+ report_event(EVNT_UNREACH, peer);
+ peer->timereachable = current_time;
+ peer_clear(peer);
+ }
+ } else {
+ if ((oreach & 0x03) == 0) {
+ clock_filter(peer, 0., 0., MAXDISPERSE);
+ clock_select();
+ }
+ if (peer->valid <= 2) {
+ hpoll--;
+ } else if (peer->valid > NTP_SHIFT - 2)
+ hpoll++;
+ 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;
+ poll_update(peer, hpoll);
+ if (peer->burst > 0)
+ peer->burst--;
+ poll_update(peer, hpoll);
+}
+
+
+/*
+ * 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,
+ l_fp offset,
+ l_fp lastrec,
+ double fudge
+ )
+{
+ double doffset;
+
+ pp->lastref = offset;
+ pp->lastrec = lastrec;
+ pp->variance = 0;
+ L_SUB(&offset, &lastrec);
+ LFPTOD(&offset, 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
+ )
+{
+ l_fp offset;
+
+ /*
+ * 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);
+ if (pp->usec) {
+ TVUTOTSF(pp->usec, offset.l_uf);
+ } else {
+ MSUTOTSF(pp->msec, offset.l_uf);
+ }
+ 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 mean-square variance. 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 0.
+ */
+static int
+refclock_sample(
+ struct refclockproc *pp
+ )
+{
+ int i, j, k, n;
+ double offset, disp;
+ double off[MAXSTAGE];
+
+ /*
+ * Copy the raw offsets and sort into ascending order. Don't do
+ * anything if the buffer is empty.
+ */
+ if (pp->codeproc == pp->coderecv)
+ return (0);
+ n = 0;
+ while (pp->codeproc != pp->coderecv)
+ off[n++] = pp->filter[pp->codeproc++ % MAXSTAGE];
+ if (n > 1)
+ qsort((char *)off, 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;
+ k = n - (n * 2) / NSTAGE;
+ while ((j - i) > k) {
+ 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 variance.
+ */
+ offset = disp = 0;
+ for (; i < j; i++) {
+ offset += off[i];
+ disp += SQUARE(off[i]);
+ }
+ offset /= k;
+ pp->offset = offset;
+ pp->variance += disp / k - SQUARE(offset);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "refclock_sample: n %d offset %.6f disp %.6f std %.6f\n",
+ n, pp->offset, pp->disp, SQRT(pp->variance));
+#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, ntoa(&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 == 0)
+ report_event(EVNT_REACH, peer);
+ peer->reach |= 1;
+ peer->reftime = peer->org = pp->lastrec;
+ peer->rootdispersion = pp->disp + SQRT(pp->variance);
+ get_systime(&peer->rec);
+ if (!refclock_sample(pp))
+ return;
+ clock_filter(peer, pp->offset, 0., 0.);
+ clock_select();
+ record_peer_stats(&peer->srcadr, ctlpeerstatus(peer),
+ peer->offset, peer->delay, CLOCK_PHI * (current_time -
+ peer->epoch), SQRT(peer->variance));
+ if (pps_control && pp->sloppyclockflag & CLK_FLAG1)
+ pp->fudgetime1 -= pp->offset * 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;
+#ifdef TIOCDCDTIMESTAMP
+ struct timeval dcd_time;
+#endif /* TIOCDCDTIMESTAMP */
+#ifdef HAVE_PPSAPI
+ pps_info_t pi;
+ struct timespec timeout, *tsp;
+ double a;
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * 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_space;
+ dpend = dpt + rbufp->recv_length;
+ trtmp = rbufp->recv_time;
+
+#ifdef HAVE_PPSAPI
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ if ((rbufp->fd == fdpps) &&
+ (time_pps_fetch(fdpps, PPS_TSFMT_TSPEC, &pi, &timeout) >= 0)) {
+ if(pps_assert)
+ tsp = &pi.assert_timestamp;
+ else
+ tsp = &pi.clear_timestamp;
+ a = tsp->tv_nsec;
+ a /= 1e9;
+ tstmp.l_uf = a * 4294967296.0;
+ tstmp.l_ui = tsp->tv_sec;
+ tstmp.l_ui += JAN_1970;
+ L_SUB(&trtmp, &tstmp);
+ if (trtmp.l_ui == 0) {
+#ifdef DEBUG
+ if (debug > 1) {
+ printf(
+ "refclock_gtlin: fd %d time_pps_fetch %s",
+ fdpps, lfptoa(&tstmp, 6));
+ printf(" sigio %s\n", lfptoa(&trtmp, 6));
+ }
+#endif
+ trtmp = tstmp;
+ goto gotit;
+ } else
+ trtmp = rbufp->recv_time;
+ }
+#endif /* HAVE_PPSAPI */
+#ifdef TIOCDCDTIMESTAMP
+ if(ioctl(rbufp->fd, TIOCDCDTIMESTAMP, &dcd_time) != -1) {
+ TVTOTS(&dcd_time, &tstmp);
+ tstmp.l_ui += JAN_1970;
+ L_SUB(&trtmp, &tstmp);
+ if (trtmp.l_ui == 0) {
+#ifdef DEBUG
+ if (debug > 1) {
+ printf(
+ "refclock_gtlin: fd %d DCDTIMESTAMP %s",
+ rbufp->fd, lfptoa(&tstmp, 6));
+ printf(" sigio %s\n", lfptoa(&trtmp, 6));
+ }
+#endif
+ trtmp = tstmp;
+ goto gotit;
+ } else
+ trtmp = rbufp->recv_time;
+ }
+ else
+ /* XXX fallback to old method if kernel refuses TIOCDCDTIMESTAMP */
+#endif /* TIOCDCDTIMESTAMP */
+ 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;
+ }
+ }
+
+#if defined(HAVE_PPSAPI) || defined(TIOCDCDTIMESTAMP)
+gotit:
+#endif
+ /*
+ * 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 = *dpt & 0x7f;
+ if (c >= ' ')
+ *dp++ = c;
+ }
+ i = dp - lineptr;
+ if (i > 0)
+ *dp = '\0';
+#ifdef DEBUG
+ if (debug > 1 && i > 0)
+ printf("refclock_gtlin: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr);
+#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 flags /* line discipline flags */
+ )
+{
+ int fd, i;
+#ifdef HAVE_TERMIOS
+ struct termios ttyb, *ttyp;
+#endif /* HAVE_TERMIOS */
+#ifdef HAVE_SYSV_TTYS
+ struct termio ttyb, *ttyp;
+#endif /* HAVE_SYSV_TTYS */
+#ifdef HAVE_BSD_TTYS
+ struct sgttyb ttyb, *ttyp;
+#endif /* HAVE_BSD_TTYS */
+#ifdef TIOCMGET
+ u_long ltemp;
+#endif /* TIOCMGET */
+
+ /*
+ * Open serial port and set default options
+ */
+#ifdef O_NONBLOCK
+ fd = open(dev, O_RDWR | O_NONBLOCK, 0777);
+#else
+ fd = open(dev, O_RDWR, 0777);
+#endif /* O_NONBLOCK */
+ if (fd == -1) {
+ msyslog(LOG_ERR, "refclock_open: %s: %m", dev);
+ return (0);
+ }
+
+ /*
+ * 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);
+ }
+
+ /*
+ * If this is the PPS device, so say and initialize the thing.
+ */
+ if (strcmp(dev, pps_device) == 0)
+ (void)refclock_ioctl(fd, LDISC_PPS);
+ 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, ppsclock and ppsapi, as well as
+ * their many other variants. 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
+#ifdef HAVE_TERMIOS
+ struct termios ttyb, *ttyp;
+#endif /* HAVE_TERMIOS */
+#ifdef HAVE_SYSV_TTYS
+ struct termio ttyb, *ttyp;
+#endif /* HAVE_SYSV_TTYS */
+#ifdef HAVE_BSD_TTYS
+ struct sgttyb ttyb, *ttyp;
+#endif /* HAVE_BSD_TTYS */
+#endif /* TTYCLK */
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_ioctl: fd %d flags 0x%x\n", fd, flags);
+#endif
+
+ /*
+ * The following sections select optional features, such as
+ * modem control, PPS capture and so forth. Some require
+ * specific operating system support in the form of STREAMS
+ * modules, which can be loaded and unloaded at run time without
+ * rebooting the kernel. The STREAMS modules require System
+ * V STREAMS support. The checking frenzy is attenuated here,
+ * since the device is already open.
+ *
+ * Note that the tty_clk and ppsclock modules are optional; if
+ * configured and unavailable, the dang thing still works, but
+ * the accuracy improvement using them will not be available.
+ * The only known implmentations of these moldules are specific
+ * to SunOS 4.x. Use the ppsclock module ONLY with Sun baseboard
+ * ttya or ttyb. Using it with the SPIF multipexor crashes the
+ * kernel.
+ *
+ * The preferred way to capture PPS timestamps is using the
+ * ppsapi interface, which is machine independent. The SunOS 4.x
+ * and Digital Unix 4.x interfaces use STREAMS modules and
+ * support both the ppsapi specification and ppsclock
+ * functionality, but other systems may vary widely.
+ */
+ 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 */
+
+#if defined(PPS) && !defined(HAVE_PPSAPI)
+ /*
+ * The PPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and System V STREAMS
+ * support. This option has been superseded by the ppsapi
+ * option and may be withdrawn in future.
+ */
+ if (flags & LDISC_PPS) {
+ int rval = 0;
+#ifdef HAVE_TIOCSPPS /* Solaris */
+ int one = 1;
+#endif /* HAVE_TIOCSPPS */
+
+ if (fdpps > 0) {
+ msyslog(LOG_ERR,
+ "refclock_ioctl: PPS already configured");
+ return (0);
+ }
+#ifdef HAVE_TIOCSPPS /* Solaris */
+ if (ioctl(fd, TIOCSPPS, &one) < 0) {
+ msyslog(LOG_NOTICE,
+ "refclock_ioctl: TIOCSPPS failed: %m");
+ return (0);
+ }
+ if (debug)
+ printf("refclock_ioctl: fd %d TIOCSPPS %d\n",
+ fd, rval);
+#else
+ if (ioctl(fd, I_PUSH, "ppsclock") < 0) {
+ msyslog(LOG_NOTICE,
+ "refclock_ioctl: I_PUSH ppsclock failed: %m");
+ return (0);
+ }
+ if (debug)
+ printf("refclock_ioctl: fd %d ppsclock %d\n",
+ fd, rval);
+#endif /* not HAVE_TIOCSPPS */
+ fdpps = fd;
+ }
+#endif /* PPS HAVE_PPSAPI */
+
+#ifdef HAVE_PPSAPI
+ /*
+ * The PPSAPI option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires ppsapi compiled into the kernel on non STREAMS
+ * systems. This is the preferred way to capture PPS timestamps
+ * and is expected to become an IETF cross-platform standard.
+ */
+ if (flags & (LDISC_PPS | LDISC_CLKPPS)) {
+ pps_params_t pp;
+ int mode, temp;
+ pps_handle_t handle;
+
+ memset((char *)&pp, 0, sizeof(pp));
+ if (fdpps > 0) {
+ msyslog(LOG_ERR,
+ "refclock_ioctl: ppsapi already configured");
+ return (0);
+ }
+ if (time_pps_create(fd, &handle) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ioctl: time_pps_create failed: %m");
+ return (0);
+ }
+ if (time_pps_getcap(handle, &mode) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ioctl: time_pps_getcap failed: %m");
+ return (0);
+ }
+ pp.mode = mode & PPS_CAPTUREBOTH;
+ if (time_pps_setparams(handle, &pp) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ioctl: time_pps_setparams failed: %m");
+ return (0);
+ }
+ if (!pps_hardpps)
+ temp = 0;
+ else if (pps_assert)
+ temp = mode & PPS_CAPTUREASSERT;
+ else
+ temp = mode & PPS_CAPTURECLEAR;
+ if (time_pps_kcbind(handle, PPS_KC_HARDPPS, temp,
+ PPS_TSFMT_TSPEC) < 0) {
+ msyslog(LOG_ERR,
+ "refclock_ioctl: time_pps_kcbind failed: %m");
+ return (0);
+ }
+ (void)time_pps_getparams(handle, &pp);
+ fdpps = (int)handle;
+ if (debug)
+ printf(
+ "refclock_ioctl: fd %d ppsapi vers %d mode 0x%x cap 0x%x\n",
+ fdpps, pp.api_version, pp.mode, mode);
+ }
+#endif /* HAVE_PPSAPI */
+#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_in *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 (!ISREFCLOCKADR(srcadr))
+ return;
+ clktype = (u_char)REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit >= MAXUNIT)
+ return;
+ if (!(peer = typeunit[clktype][unit]))
+ 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)
+ peer->stratum = (u_char) in->fudgeval1;
+ if (in->haveflags & CLK_HAVEVAL2)
+ pp->refid = in->fudgeval2;
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ else
+ peer->refid = 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 = peer->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_in *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 (!ISREFCLOCKADR(srcadr))
+ return;
+ clktype = (u_char) REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit >= MAXUNIT)
+ return;
+ if (!(peer = typeunit[clktype][unit]))
+ 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->msec;
+ 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/contrib/ntp/ntpd/ntp_request.c b/contrib/ntp/ntpd/ntp_request.c
new file mode 100644
index 0000000..3743118
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_request.c
@@ -0,0 +1,2334 @@
+/*
+ * ntp_request.c - respond to information requests
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#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 "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)
+
+struct req_proc {
+ short request_code; /* defined request code */
+ short needs_auth; /* true when authentication needed */
+ short sizeofitem; /* size of request data item */
+ void (*handler) P((struct sockaddr_in *, 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_in *, struct interface *, struct req_pkt *, int));
+static char * prepare_pkt P((struct sockaddr_in *, 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_in *, struct interface *, struct req_pkt *));
+static void peer_list_sum P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void peer_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void peer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void sys_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void sys_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void mem_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void io_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void timer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void loop_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_conf P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_unconf P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void set_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void clr_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void setclr_flags P((struct sockaddr_in *, struct interface *, struct req_pkt *, u_long));
+static void list_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_resaddflags P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_ressubflags P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_unrestrict P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static void mon_getlist_0 P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void mon_getlist_1 P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void reset_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void reset_peer P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_key_reread P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void trust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void untrust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_trustkey P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static void get_auth_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void reset_auth_stats P((void));
+static void req_get_traps P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void req_set_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void req_clr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_setclr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static void set_request_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void set_control_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void get_ctl_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#ifdef KERNEL_PLL
+static void get_kernel_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#endif /* KERNEL_PLL */
+#ifdef REFCLOCK
+static void get_clock_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void set_clock_fudge P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+#ifdef REFCLOCK
+static void get_clkbug_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+
+/*
+ * ntpd request codes
+ */
+static struct req_proc ntp_codes[] = {
+ { REQ_PEER_LIST, NOAUTH, 0, peer_list },
+ { REQ_PEER_LIST_SUM, NOAUTH, 0, peer_list_sum },
+ { REQ_PEER_INFO, NOAUTH, sizeof(struct info_peer_list), peer_info },
+ { REQ_PEER_STATS, NOAUTH, sizeof(struct info_peer_list), peer_stats },
+ { REQ_SYS_INFO, NOAUTH, 0, sys_info },
+ { REQ_SYS_STATS, NOAUTH, 0, sys_stats },
+ { REQ_IO_STATS, NOAUTH, 0, io_stats },
+ { REQ_MEM_STATS, NOAUTH, 0, mem_stats },
+ { REQ_LOOP_INFO, NOAUTH, 0, loop_info },
+ { REQ_TIMER_STATS, NOAUTH, 0, timer_stats },
+ { REQ_CONFIG, AUTH, sizeof(struct conf_peer), do_conf },
+ { REQ_UNCONFIG, AUTH, sizeof(struct conf_unpeer), do_unconf },
+ { REQ_SET_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), set_sys_flag },
+ { REQ_CLR_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), clr_sys_flag },
+ { REQ_GET_RESTRICT, NOAUTH, 0, list_restrict },
+ { REQ_RESADDFLAGS, AUTH, sizeof(struct conf_restrict), do_resaddflags },
+ { REQ_RESSUBFLAGS, AUTH, sizeof(struct conf_restrict), do_ressubflags },
+ { REQ_UNRESTRICT, AUTH, sizeof(struct conf_restrict), do_unrestrict },
+ { REQ_MON_GETLIST, NOAUTH, 0, mon_getlist_0 },
+ { REQ_MON_GETLIST_1, NOAUTH, 0, mon_getlist_1 },
+ { REQ_RESET_STATS, AUTH, sizeof(struct reset_flags), reset_stats },
+ { REQ_RESET_PEER, AUTH, sizeof(struct conf_unpeer), reset_peer },
+ { REQ_REREAD_KEYS, AUTH, 0, do_key_reread },
+ { REQ_TRUSTKEY, AUTH, sizeof(u_long), trust_key },
+ { REQ_UNTRUSTKEY, AUTH, sizeof(u_long), untrust_key },
+ { REQ_AUTHINFO, NOAUTH, 0, get_auth_info },
+ { REQ_TRAPS, NOAUTH, 0, req_get_traps },
+ { REQ_ADD_TRAP, AUTH, sizeof(struct conf_trap), req_set_trap },
+ { REQ_CLR_TRAP, AUTH, sizeof(struct conf_trap), req_clr_trap },
+ { REQ_REQUEST_KEY, AUTH, sizeof(u_long), set_request_keyid },
+ { REQ_CONTROL_KEY, AUTH, sizeof(u_long), set_control_keyid },
+ { REQ_GET_CTLSTATS, NOAUTH, 0, get_ctl_stats },
+#ifdef KERNEL_PLL
+ { REQ_GET_KERNEL, NOAUTH, 0, get_kernel_info },
+#endif
+#ifdef REFCLOCK
+ { REQ_GET_CLOCKINFO, NOAUTH, sizeof(u_int32), get_clock_info },
+ { REQ_SET_CLKFUDGE, AUTH, sizeof(struct conf_fudge), set_clock_fudge },
+ { REQ_GET_CLKBUGINFO, NOAUTH, sizeof(u_int32), get_clkbug_info },
+#endif
+ { NO_REQUEST, NOAUTH, 0, 0 }
+};
+
+
+/*
+ * Authentication keyid used to authenticate requests. Zero means we
+ * don't allow writing anything.
+ */
+u_long 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_in *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_in *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_in *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, reqest 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 sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_proc *proc;
+
+ /*
+ * Initialize pointers, for convenience
+ */
+ inpkt = (struct req_pkt *)&rbufp->recv_pkt;
+ srcadr = &rbufp->recv_srcadr;
+ inter = rbufp->dstadr;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("prepare_pkt: impl %d req %d\n",
+ inpkt->implementation, inpkt->request);
+#endif
+
+ /*
+ * Do some sanity checks on the packet. Return a format
+ * error if it fails.
+ */
+ if (ISRESPONSE(inpkt->rm_vn_mode)
+ || ISMORE(inpkt->rm_vn_mode)
+ || INFO_VERSION(inpkt->rm_vn_mode) > NTP_VERSION
+ || INFO_VERSION(inpkt->rm_vn_mode) < NTP_OLDVERSION
+ || INFO_SEQ(inpkt->auth_seq) != 0
+ || INFO_ERR(inpkt->err_nitems) != 0
+ || INFO_MBZ(inpkt->mbz_itemsize) != 0
+ || rbufp->recv_length > REQ_LEN_MAC
+ || rbufp->recv_length < REQ_LEN_NOMAC) {
+ 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)
+ 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 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 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(inpkt->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(inpkt->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
+ 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);
+#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(&inpkt->tstamp, &ftmp);
+ L_SUB(&ftmp, &rbufp->recv_time);
+ LFPTOD(&ftmp, dtemp);
+ if (fabs(dtemp) >= INFO_TS_MAXSKEW) {
+ /*
+ * He's a loser. Tell him.
+ */
+ 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,
+ REQ_LEN_NOMAC, (int)(rbufp->recv_length - REQ_LEN_NOMAC))) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ }
+
+ /*
+ * If we need data, check to see if we have some. If we
+ * don't, check to see that there is none (picky, picky).
+ */
+ if (INFO_ITEMSIZE(inpkt->mbz_itemsize) != proc->sizeofitem) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if (proc->sizeofitem != 0)
+ if (proc->sizeofitem*INFO_NITEMS(inpkt->err_nitems)
+ > sizeof(inpkt->data)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ 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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_list *ip;
+ register struct peer *pp;
+ register int i;
+
+ ip = (struct info_peer_list *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer_list));
+ for (i = 0; i < HASH_SIZE && ip != 0; i++) {
+ pp = peer_hash[i];
+ while (pp != 0 && ip != 0) {
+ ip->address = pp->srcadr.sin_addr.s_addr;
+ ip->port = pp->srcadr.sin_port;
+ 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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_peer_summary *ips;
+ register struct peer *pp;
+ register int i;
+ l_fp ltmp;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants peer list summary\n");
+#endif
+
+ ips = (struct info_peer_summary *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(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
+ ips->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ pp->dstadr->bcast.sin_addr.s_addr:
+ pp->cast_flags ?
+ pp->dstadr->sin.sin_addr.s_addr ?
+ pp->dstadr->sin.sin_addr.s_addr:
+ pp->dstadr->bcast.sin_addr.s_addr:
+ 1 : 5;
+ ips->srcadr = pp->srcadr.sin_addr.s_addr;
+ ips->srcport = pp->srcadr.sin_port;
+ 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_in *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_in addr;
+ extern struct peer *sys_peer;
+ l_fp ltmp;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+ ip = (struct info_peer *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer));
+ while (items-- > 0 && ip != 0) {
+ addr.sin_port = ipl->port;
+ addr.sin_addr.s_addr = ipl->address;
+ ipl++;
+ if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0)
+ continue;
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ pp->dstadr->bcast.sin_addr.s_addr:
+ pp->cast_flags ?
+ pp->dstadr->sin.sin_addr.s_addr ?
+ pp->dstadr->sin.sin_addr.s_addr:
+ pp->dstadr->bcast.sin_addr.s_addr:
+ 2 : 6;
+ ip->srcadr = NSRCADR(&pp->srcadr);
+ 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->valid = pp->valid;
+ ip->reach = pp->reach;
+ ip->unreach = pp->unreach;
+ ip->flash = (u_char)pp->flash;
+ ip->flash2 = 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] = (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->variance)));
+ ip = (struct info_peer *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_stats - send statistics for one or more peers
+ */
+static void
+peer_stats (
+ struct sockaddr_in *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_in addr;
+ extern struct peer *sys_peer;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+ ip = (struct info_peer_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer_stats));
+ while (items-- > 0 && ip != 0) {
+ addr.sin_port = ipl->port;
+ addr.sin_addr.s_addr = ipl->address;
+ ipl++;
+ if ((pp = findexistingpeer(&addr, (struct peer *)0, -1)) == 0)
+ continue;
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ pp->dstadr->bcast.sin_addr.s_addr:
+ pp->cast_flags ?
+ pp->dstadr->sin.sin_addr.s_addr ?
+ pp->dstadr->sin.sin_addr.s_addr:
+ pp->dstadr->bcast.sin_addr.s_addr:
+ 3 : 7;
+ ip->srcadr = NSRCADR(&pp->srcadr);
+ 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_in *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_error;
+
+ is = (struct info_sys *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_sys));
+
+ if (sys_peer != 0) {
+ is->peer = NSRCADR(&sys_peer->srcadr);
+ is->peer_mode = sys_peer->hmode;
+ } else {
+ is->peer = 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_error));
+ 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_bclient)
+ is->flags |= INFO_FLAG_BCLIENT;
+ if (sys_authenticate)
+ is->flags |= INFO_FLAG_AUTHENTICATE;
+ if (kern_enable)
+ is->flags |= INFO_FLAG_KERNEL;
+ if (ntp_enable)
+ is->flags |= INFO_FLAG_NTP;
+ if (pll_control)
+ is->flags |= INFO_FLAG_PLL_SYNC;
+ if (pps_control)
+ is->flags |= INFO_FLAG_PPS_SYNC;
+ if (mon_enabled != MON_OFF)
+ is->flags |= INFO_FLAG_MONITOR;
+ 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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_sys_stats *ss;
+
+ /*
+ * Importations from the protocol module
+ */
+ extern u_long sys_stattime;
+ extern u_long sys_badstratum;
+ extern u_long sys_oldversionpkt;
+ extern u_long sys_newversionpkt;
+ extern u_long sys_unknownversion;
+ extern u_long sys_badlength;
+ extern u_long sys_processed;
+ extern u_long sys_badauth;
+ extern u_long sys_limitrejected;
+
+ 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->badstratum = htonl((u_int32)sys_badstratum);
+ 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);
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * mem_stats - return memory statistics
+ */
+static void
+mem_stats(
+ struct sockaddr_in *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_in *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_in *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_in *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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_peer *cp;
+ register int items;
+ struct sockaddr_in peeraddr;
+ int fl;
+
+ /*
+ * 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;
+
+ fl = 0;
+ while (items-- > 0 && !fl) {
+ if (((cp->version) > NTP_VERSION)
+ || ((cp->version) < NTP_OLDVERSION))
+ fl = 1;
+ if (cp->hmode != MODE_ACTIVE
+ && cp->hmode != MODE_CLIENT
+ && cp->hmode != MODE_BROADCAST)
+ fl = 1;
+ if (cp->flags & ~(CONF_FLAG_AUTHENABLE | CONF_FLAG_PREFER
+ | CONF_FLAG_NOSELECT | CONF_FLAG_BURST | CONF_FLAG_SKEY))
+ fl = 1;
+ cp++;
+ }
+
+ 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;
+ 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)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ while (items-- > 0) {
+ fl = 0;
+ if (cp->flags & CONF_FLAG_AUTHENABLE)
+ fl |= FLAG_AUTHENABLE;
+ if (cp->flags & CONF_FLAG_PREFER)
+ fl |= FLAG_PREFER;
+ if (cp->flags & CONF_FLAG_NOSELECT)
+ fl |= FLAG_NOSELECT;
+ if (cp->flags & CONF_FLAG_BURST)
+ fl |= FLAG_BURST;
+ if (cp->flags & CONF_FLAG_SKEY)
+ fl |= FLAG_SKEY;
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ /* XXX W2DO? minpoll/maxpoll arguments ??? */
+ if (peer_config(&peeraddr, (struct interface *)0,
+ cp->hmode, cp->version, cp->minpoll, cp->maxpoll,
+ fl, cp->ttl, cp->keyid) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ cp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_unconf - remove a peer from the configuration list
+ */
+static void
+do_unconf(
+ struct sockaddr_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_unpeer *cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_in 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.
+ */
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ found = 0;
+ peer = (struct peer *)0;
+ 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++;
+ }
+
+ 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) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ peer_unconfig(&peeraddr, (struct interface *)0, -1);
+ cp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * set_sys_flag - set system flags
+ */
+static void
+set_sys_flag(
+ struct sockaddr_in *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_in *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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ u_long set
+ )
+{
+ register u_long flags;
+
+ if (INFO_NITEMS(inpkt->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_AUTHENTICATE |
+ SYS_FLAG_NTP | SYS_FLAG_KERNEL | SYS_FLAG_MONITOR |
+ SYS_FLAG_FILEGEN)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ if (flags & SYS_FLAG_BCLIENT)
+ proto_config(PROTO_BROADCLIENT, set, 0.);
+ if (flags & SYS_FLAG_AUTHENTICATE)
+ proto_config(PROTO_AUTHENTICATE, set, 0.);
+ if (flags & SYS_FLAG_NTP)
+ proto_config(PROTO_NTP, set, 0.);
+ if (flags & SYS_FLAG_KERNEL)
+ proto_config(PROTO_KERNEL, set, 0.);
+ if (flags & SYS_FLAG_MONITOR)
+ proto_config(PROTO_MONITOR, set, 0.);
+ if (flags & SYS_FLAG_FILEGEN)
+ proto_config(PROTO_FILEGEN, set, 0.);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * list_restrict - return the restrict list
+ */
+static void
+list_restrict(
+ struct sockaddr_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct info_restrict *ir;
+ register struct restrictlist *rl;
+ extern struct restrictlist *restrictlist;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants peer list summary\n");
+#endif
+
+ ir = (struct info_restrict *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_restrict));
+ for (rl = restrictlist; rl != 0 && ir != 0; rl = rl->next) {
+ ir->addr = htonl(rl->addr);
+ 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();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * do_resaddflags - add flags to a restrict entry (or create one)
+ */
+static void
+do_resaddflags(
+ struct sockaddr_in *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_in *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_in *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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ int op
+ )
+{
+ register struct conf_restrict *cr;
+ register int items;
+ struct sockaddr_in matchaddr;
+ struct sockaddr_in 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 = 1;
+ if (cr->addr == htonl(INADDR_ANY) && cr->mask != htonl(INADDR_ANY))
+ bad = 1;
+ cr++;
+ }
+
+ if (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_in));
+ memset((char *)&matchmask, 0, sizeof(struct sockaddr_in));
+ matchaddr.sin_family = AF_INET;
+ matchmask.sin_family = AF_INET;
+
+ while (items-- > 0) {
+ matchaddr.sin_addr.s_addr = cr->addr;
+ matchmask.sin_addr.s_addr = cr->mask;
+ 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_in *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,
+ sizeof(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)(current_time - md->lasttime));
+ im->firsttime = htonl((u_int32)(current_time - md->firsttime));
+ if (md->lastdrop)
+ im->lastdrop = htonl((u_int32)(current_time - md->lastdrop));
+ else
+ im->lastdrop = 0;
+ im->count = htonl((u_int32)(md->count));
+ im->addr = md->rmtadr;
+ 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_in *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;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants monitor 1 list\n");
+#endif
+ if (!mon_enabled) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ im = (struct info_monitor_1 *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(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)(current_time - md->lasttime));
+ im->firsttime = htonl((u_int32)(current_time - md->firsttime));
+ if (md->lastdrop)
+ im->lastdrop = htonl((u_int32)(current_time - md->lastdrop));
+ else
+ im->lastdrop = 0;
+ im->count = htonl((u_int32)md->count);
+ im->addr = md->rmtadr;
+ im->daddr =
+ (md->cast_flags == MDF_BCAST)
+ ? md->interface->bcast.sin_addr.s_addr
+ : (md->cast_flags
+ ? (md->interface->sin.sin_addr.s_addr
+ ? md->interface->sin.sin_addr.s_addr
+ : md->interface->bcast.sin_addr.s_addr
+ )
+ : 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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_long flags;
+ struct reset_entry *rent;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ flags = ((struct reset_flags *)inpkt->data)->flags;
+
+ if (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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_unpeer *cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_in peeraddr;
+ int bad;
+
+ /*
+ * We check first to see that every peer exists. If not,
+ * we return an error.
+ */
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ peer = findexistingpeer(&peeraddr, (struct peer *)0, -1);
+ if (peer == (struct peer *)0)
+ bad++;
+ cp++;
+ }
+
+ 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) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ peer = findexistingpeer(&peeraddr, (struct peer *)0, -1);
+ while (peer != 0) {
+ peer_reset(peer);
+ peer = findexistingpeer(&peeraddr, (struct peer *)peer, -1);
+ }
+ cp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_key_reread - reread the encryption key file
+ */
+static void
+do_key_reread(
+ struct sockaddr_in *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_in *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_in *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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ int 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_in *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_in *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,
+ sizeof(struct info_trap));
+
+ for (i = 0, tr = ctl_trap; i < CTL_MAXTRAPS; i++, tr++) {
+ if (tr->tr_flags & TRAP_INUSE) {
+ if (tr->tr_localaddr == any_interface)
+ it->local_address = 0;
+ else
+ it->local_address
+ = NSRCADR(&tr->tr_localaddr->sin);
+ it->trap_address = NSRCADR(&tr->tr_addr);
+ 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_in *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_in *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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt,
+ int set
+ )
+{
+ register struct conf_trap *ct;
+ register struct interface *linter;
+ int res;
+ struct sockaddr_in laddr;
+
+ /*
+ * Prepare sockaddr_in structure
+ */
+ memset((char *)&laddr, 0, sizeof laddr);
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = ntohs(NTP_PORT);
+
+ /*
+ * Restrict ourselves to one item only. This eliminates
+ * the error reporting problem.
+ */
+ if (INFO_NITEMS(inpkt->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 {
+ laddr.sin_addr.s_addr = ct->local_address;
+ linter = findinterface(&laddr);
+ if (linter == NULL) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ }
+
+ laddr.sin_addr.s_addr = ct->trap_address;
+ if (ct->trap_port != 0)
+ laddr.sin_port = ct->trap_port;
+ else
+ laddr.sin_port = 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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_long keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ u_long keyid;
+ extern u_long ctl_auth_keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->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_in *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_in *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_in *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_in addr;
+ l_fp ltmp;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = 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) {
+ addr.sin_addr.s_addr = *clkaddr++;
+ if (!ISREFCLOCKADR(&addr) ||
+ 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 = addr.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.fudgetime1, &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_in *srcadr,
+ struct interface *inter,
+ struct req_pkt *inpkt
+ )
+{
+ register struct conf_fudge *cf;
+ register int items;
+ struct refclockstat clock_stat;
+ struct sockaddr_in addr;
+ l_fp ltmp;
+
+ memset((char *)&addr, 0, sizeof addr);
+ memset((char *)&clock_stat, 0, sizeof clock_stat);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cf = (struct conf_fudge *) inpkt->data;
+
+ while (items-- > 0) {
+ addr.sin_addr.s_addr = cf->clockadr;
+ if (!ISREFCLOCKADR(&addr) ||
+ 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:
+ 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_in *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_in addr;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = 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) {
+ addr.sin_addr.s_addr = *clkaddr++;
+ if (!ISREFCLOCKADR(&addr) ||
+ 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 = addr.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/contrib/ntp/ntpd/ntp_restrict.c b/contrib/ntp/ntpd/ntp_restrict.c
new file mode 100644
index 0000000..0e5b9dc
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_restrict.c
@@ -0,0 +1,460 @@
+/*
+ * ntp_restrict.c - find out what restrictions this host is running under
+ */
+#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.
+ */
+
+/*
+ * 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
+
+/*
+ * The restriction list
+ */
+struct restrictlist *restrictlist;
+static int restrictcount; /* count of entries in the restriction list */
+
+/*
+ * The free list and associated counters. Also some uninteresting
+ * stat counters.
+ */
+static struct restrictlist *resfree;
+static int numresfree; /* number of structures on free list */
+
+static u_long res_calls;
+static u_long res_found;
+static u_long res_not_found;
+/* static u_long res_timereset; */
+
+/*
+ * Parameters of the RES_LIMITED restriction option.
+ * client_limit is the number of hosts allowed per source net
+ * client_limit_period is the number of seconds after which an entry
+ * is no longer considered for client limit determination
+ */
+u_long client_limit;
+u_long client_limit_period;
+/*
+ * 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;
+
+/*
+ * Our initial allocation of list entries.
+ */
+static struct restrictlist resinit[INITRESLIST];
+
+/*
+ * init_restrict - initialize the restriction data structures
+ */
+void
+init_restrict(void)
+{
+ register int i;
+ char bp[80];
+
+ /*
+ * Zero the list and put all but one on the free list
+ */
+ resfree = 0;
+ memset((char *)resinit, 0, sizeof resinit);
+
+ for (i = 1; i < INITRESLIST; i++) {
+ resinit[i].next = resfree;
+ resfree = &resinit[i];
+ }
+
+ numresfree = 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;
+ restrictlist = &resinit[0];
+ restrictcount = 1;
+
+
+ /*
+ * fix up stat counters
+ */
+ res_calls = 0;
+ res_found = 0;
+ res_not_found = 0;
+ /* res_timereset = 0; */
+
+ /*
+ * set default values for RES_LIMIT functionality
+ */
+ client_limit = 3;
+ client_limit_period = 3600;
+ res_limited_refcnt = 0;
+
+ sprintf(bp, "client_limit=%ld", client_limit);
+ set_sys_var(bp, strlen(bp)+1, RO);
+ sprintf(bp, "client_limit_period=%ld", client_limit_period);
+ set_sys_var(bp, strlen(bp)+1, RO);
+}
+
+
+/*
+ * restrictions - return restrictions for this host
+ */
+int
+restrictions(
+ struct sockaddr_in *srcadr
+ )
+{
+ register struct restrictlist *rl;
+ register struct restrictlist *match;
+ register u_int32 hostaddr;
+ register int isntpport;
+
+ res_calls++;
+ /*
+ * 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(ntohl(srcadr->sin_addr.s_addr)))
+ 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++;
+
+ /*
+ * The following implements limiting the number of clients
+ * accepted from a given network. The notion of "same network"
+ * is determined by the mask and addr fields of the restrict
+ * list entry. The monitor mechanism has to be enabled for
+ * collecting info on current clients.
+ *
+ * The policy is as follows:
+ * - take the list of clients recorded
+ * from the given "network" seen within the last
+ * client_limit_period seconds
+ * - if there are at most client_limit entries:
+ * --> access allowed
+ * - otherwise sort by time first seen
+ * - current client among the first client_limit seen
+ * hosts?
+ * if yes: access allowed
+ * else: eccess denied
+ */
+ if (match->flags & RES_LIMITED) {
+ int lcnt;
+ struct mon_data *md, *this_client;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("limited clients check: %ld clients, period %ld seconds, net is 0x%lX\n",
+ client_limit, client_limit_period,
+ (u_long)netof(hostaddr));
+#endif /*DEBUG*/
+ if (mon_enabled == MON_OFF) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("no limit - monitoring is off\n");
+#endif
+ return (int)(match->flags & ~RES_LIMITED);
+ }
+
+ /*
+ * How nice, MRU list provides our current client as the
+ * first entry in the list.
+ * Monitoring was verified to be active above, thus we
+ * know an entry for our client must exist, or some
+ * brain dead set the memory limit for mon entries to ZERO!!!
+ */
+ this_client = mon_mru_list.mru_next;
+
+ for (md = mon_fifo_list.fifo_next,lcnt = 0;
+ md != &mon_fifo_list;
+ md = md->fifo_next) {
+ if ((current_time - md->lasttime)
+ > client_limit_period) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("checking: %s: ignore: too old: %ld\n",
+ numtoa(md->rmtadr),
+ current_time - md->lasttime);
+#endif
+ continue;
+ }
+ if (md->mode == MODE_BROADCAST ||
+ md->mode == MODE_CONTROL ||
+ md->mode == MODE_PRIVATE) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("checking: %s: ignore mode %d\n",
+ numtoa(md->rmtadr),
+ md->mode);
+#endif
+ continue;
+ }
+ if (netof(md->rmtadr) !=
+ netof(hostaddr)) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("checking: %s: different net 0x%lX\n",
+ numtoa(md->rmtadr),
+ (u_long)netof(md->rmtadr));
+#endif
+ continue;
+ }
+ lcnt++;
+ if (lcnt > (int) client_limit ||
+ md->rmtadr == hostaddr) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("considering %s: found host\n",
+ numtoa(md->rmtadr));
+#endif
+ break;
+ }
+#ifdef DEBUG
+ else {
+ if (debug > 5)
+ printf("considering %s: same net\n",
+ numtoa(md->rmtadr));
+ }
+#endif
+
+ }
+#ifdef DEBUG
+ if (debug > 4)
+ printf("this one is rank %d in list, limit is %lu: %s\n",
+ lcnt, client_limit,
+ (lcnt <= (int) client_limit) ? "ALLOW" : "REJECT");
+#endif
+ if (lcnt <= (int) client_limit) {
+ this_client->lastdrop = 0;
+ return (int)(match->flags & ~RES_LIMITED);
+ } else {
+ this_client->lastdrop = current_time;
+ }
+ }
+ return (int)match->flags;
+}
+
+
+/*
+ * hack_restrict - add/subtract/manipulate entries on the restrict list
+ */
+void
+hack_restrict(
+ int op,
+ struct sockaddr_in *resaddr,
+ struct sockaddr_in *resmask,
+ int mflags,
+ int flags
+ )
+{
+ register u_int32 addr;
+ register u_int32 mask;
+ register struct restrictlist *rl;
+ register struct restrictlist *rlprev;
+ int i;
+
+ /*
+ * Get address and mask in host byte order
+ */
+ addr = SRCADR(resaddr);
+ mask = SRCADR(resmask);
+ addr &= mask; /* make sure low bits are zero */
+
+ /*
+ * If this is the default address, point at first on list. Else
+ * go searching for it.
+ */
+ if (addr == htonl(INADDR_ANY)) {
+ 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; /* exact match */
+ if (!(mflags & RESM_NTPONLY)) {
+ /*
+ * No flag fits before flag
+ */
+ rl = 0;
+ break;
+ }
+ /* continue on */
+ } else if (rl->mask > mask) {
+ rl = 0;
+ break;
+ }
+ }
+ rlprev = rl;
+ rl = rl->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
+ */
+ 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); /* ensure data gets collected */
+ }
+ 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:
+ /* Oh, well */
+ break;
+ }
+
+ /* done! */
+}
diff --git a/contrib/ntp/ntpd/ntp_timer.c b/contrib/ntp/ntpd/ntp_timer.c
new file mode 100644
index 0000000..0e2dc88
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_timer.c
@@ -0,0 +1,308 @@
+/*
+ * ntp_timer.c - event timer support routines
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/signal.h>
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#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 revoke_timer; /* keys revoke timer */
+u_long sys_revoke = KEY_REVOKE; /* keys revoke timeout */
+
+/*
+ * 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 */
+
+
+/*
+ * init_timer - initialize the timer data structures
+ */
+void
+init_timer(void)
+{
+#if !defined(VMS)
+# if !defined SYS_WINNT || defined(SYS_CYGWIN32)
+# ifndef HAVE_TIMER_SETTIME
+ struct itimerval itimer;
+# else
+ static timer_t ntpd_timerid; /* should be global if we ever want */
+ /* to kill timer without rebooting ... */
+ struct itimerspec itimer;
+# endif /* HAVE_TIMER_SETTIME */
+# else /* SYS_WINNT */
+ HANDLE hToken;
+ TOKEN_PRIVILEGES tkp;
+# endif /* SYS_WINNT */
+#endif /* !VMS */
+
+ /*
+ * Initialize...
+ */
+ alarm_flag = 0;
+ alarm_overflow = 0;
+ adjust_timer = 1;
+ hourly_timer = HOUR;
+ 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;
+ int n;
+
+ current_time += (1<<EVENT_TIMEOUT);
+
+ /*
+ * Adjustment timeout first.
+ */
+ if (adjust_timer <= current_time) {
+ adjust_timer += 1;
+ adj_host_clock();
+ }
+
+ /*
+ * 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();
+ }
+
+ /*
+ * Garbage collect revoked keys
+ */
+ if (revoke_timer <= current_time) {
+ revoke_timer += RANDPOLL(sys_revoke);
+ key_expire_all();
+ }
+
+ /*
+ * 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/contrib/ntp/ntpd/ntp_util.c b/contrib/ntp/ntpd/ntp_util.c
new file mode 100644
index 0000000..c53255a
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_util.c
@@ -0,0 +1,640 @@
+/*
+ * ntp_util.c - stuff I didn't have any other place for
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif
+# include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_filegen.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#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;
+
+/*
+ * 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"
+ 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);
+
+#undef PEERNAME
+#undef LOOPNAME
+#undef CLOCKNAME
+#undef RAWNAME
+
+}
+
+
+/*
+ * 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_error, sys_poll);
+
+ 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 ((fp = fopen(stats_drift_file, "r")) == NULL) {
+ break;
+ }
+ if (fscanf(fp, "%lf", &old_drift) != 1) {
+ msyslog(LOG_ERR, "invalid frequency from %s",
+ stats_drift_file);
+ (void) fclose(fp);
+ break;
+ }
+ (void) fclose(fp);
+ msyslog(LOG_INFO, "frequency initialized %.3f 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);
+ }
+ }
+ 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_in *addr,
+ int status,
+ double offset,
+ double delay,
+ double dispersion,
+ double skew
+ )
+{
+ struct timeval tv;
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+#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 */
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&peerstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (peerstats.fp != NULL) {
+ fprintf(peerstats.fp,
+ "%lu %lu.%03lu %s %x %.9f %.9f %.9f %.9f\n",
+ day, sec, msec, ntoa(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(void)
+{
+ struct timeval tv;
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+#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 */
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&loopstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (loopstats.fp != NULL) {
+ fprintf(loopstats.fp, "%lu %lu.%03lu %.9f %.6f %.9f %.6f %d\n",
+ day, sec, msec, last_offset, drift_comp * 1e6,
+ sys_error, clock_stability * 1e6, sys_poll);
+ 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_in *addr,
+ const char *text
+ )
+{
+ struct timeval tv;
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+#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 */
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&clockstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (clockstats.fp != NULL) {
+ fprintf(clockstats.fp, "%lu %lu.%03lu %s %s\n",
+ day, sec, msec, ntoa(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_in *srcadr,
+ struct sockaddr_in *dstadr,
+ l_fp *t1,
+ l_fp *t2,
+ l_fp *t3,
+ l_fp *t4
+ )
+{
+ struct timeval tv;
+#ifdef HAVE_GETCLOCK
+ struct timespec ts;
+#endif
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+#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 */
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&rawstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (rawstats.fp != NULL) {
+ fprintf(rawstats.fp, "%lu %lu.%03lu %s %s %s %s %s %s\n",
+ day, sec, msec, ntoa(srcadr), ntoa(dstadr),
+ ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9),
+ ulfptoa(t4, 9));
+ fflush(rawstats.fp);
+ }
+}
+
+/*
+ * 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);
+}
diff --git a/contrib/ntp/ntpd/ntpd.c b/contrib/ntp/ntpd/ntpd.c
new file mode 100644
index 0000000..923b1f7
--- /dev/null
+++ b/contrib/ntp/ntpd/ntpd.c
@@ -0,0 +1,1121 @@
+/*
+ * ntpd.c - main program for the fixed point NTP daemon
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#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*/
+# include <sys/param.h>
+# endif /* VMS */
+# include <sys/signal.h>
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif /* HAVE_SYS_IOCTL_H */
+# include <sys/time.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 <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 "ntpd.h"
+#include "ntp_io.h"
+
+#include "ntp_stdlib.h"
+#include "recvbuff.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
+
+/*
+ * 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 || defined SYS_CYGWIN32
+/* handles for various threads, process, and objects */
+HANDLE ResolverThreadHandle = NULL;
+/* variables used to inform the Service Control Manager of our current state */
+SERVICE_STATUS ssStatus;
+SERVICE_STATUS_HANDLE sshStatusHandle;
+HANDLE WaitHandles[3] = { NULL, NULL, NULL };
+char szMsgPath[255];
+static BOOL WINAPI OnConsoleEvent(DWORD dwCtrlType);
+#endif /* SYS_WINNT */
+
+/*
+ * Scheduling priority we run at
+ */
+#if !defined SYS_WINNT
+# define NTPD_PRIO (-12)
+#else
+# define NTPD_PRIO REALTIME_PRIORITY_CLASS
+#endif
+
+/*
+ * Debugging flag
+ */
+volatile int debug;
+
+/*
+ * No-fork flag. If set, we do not become a background daemon.
+ */
+int nofork;
+
+/*
+ * 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
+static RETSIGTYPE moredebug P((int));
+static RETSIGTYPE lessdebug P((int));
+#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 NO_MAIN_ALLOWED
+CALL(ntpd,"ntpd",ntpdmain);
+#else
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ return ntpdmain(argc, argv);
+}
+#endif
+
+#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)
+{
+ int done = 0;
+
+#ifdef SYS_WINNT
+ if (!SetPriorityClass(GetCurrentProcess(), (DWORD) REALTIME_PRIORITY_CLASS))
+ msyslog(LOG_ERR, "SetPriorityClass: %m");
+ else
+ ++done;
+#else /* not SYS_WINNT */
+# if defined(HAVE_SCHED_SETSCHEDULER)
+
+ if (!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
+ ++done;
+ }
+# endif /* HAVE_SCHED_SETSCHEDULER */
+# if defined(HAVE_RTPRIO)
+# ifdef RTP_SET
+ if (!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
+ ++done;
+ }
+# else /* not RTP_SET */
+ if (!done) {
+ if (rtprio(0, 120) < 0)
+ msyslog(LOG_ERR, "rtprio() error: %m");
+ else
+ ++done;
+ }
+# endif /* not RTP_SET */
+# endif /* HAVE_RTPRIO */
+# if defined(NTPD_PRIO) && NTPD_PRIO != 0
+# ifdef HAVE_ATT_NICE
+ if (!done) {
+ errno = 0;
+ if (-1 == nice (NTPD_PRIO) && errno != 0)
+ msyslog(LOG_ERR, "nice() error: %m");
+ else
+ ++done;
+ }
+# endif /* HAVE_ATT_NICE */
+# ifdef HAVE_BSD_NICE
+ if (!done) {
+ if (-1 == setpriority(PRIO_PROCESS, 0, NTPD_PRIO))
+ msyslog(LOG_ERR, "setpriority() error: %m");
+ else
+ ++done;
+ }
+# endif /* HAVE_BSD_NICE */
+# endif /* NTPD_PRIO && NTPD_PRIO != 0 */
+#endif /* not SYS_WINNT */
+ if (!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
+ {
+ unsigned int uv;
+
+ uv = umask(0);
+ if(uv)
+ (void) umask(uv);
+ else
+ (void) umask(022);
+ }
+#endif
+
+#ifdef HAVE_GETUID
+ {
+ 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
+
+ get_systime(&now);
+ SRANDOM((int)(now.l_i * now.l_uf));
+ getstartup(argc, argv); /* startup configuration, may set debug */
+
+#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 */
+
+ /*
+ * From 'Writing Reliable AIX Daemons,' SG24-4946-00,
+ * by Eric Agar (saves us from doing 32767 system
+ * calls)
+ */
+#if defined(F_CLOSEM)
+ 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 */
+
+ {
+ SERVICE_TABLE_ENTRY dispatchTable[] = {
+ { TEXT("NetworkTimeProtocol"), (LPSERVICE_MAIN_FUNCTION)service_main },
+ { NULL, NULL }
+ };
+
+ /* daemonize */
+ if (!StartServiceCtrlDispatcher(dispatchTable))
+ {
+ msyslog(LOG_ERR, "StartServiceCtrlDispatcher: %m");
+ ExitProcess(2);
+ }
+ }
+#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)
+ {
+ /* register our service control handler */
+ if (!(sshStatusHandle = RegisterServiceCtrlHandler( TEXT("NetworkTimeProtocol"),
+ (LPHANDLER_FUNCTION)service_ctrl)))
+ {
+ 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(SYS_WINNT) && !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);
+#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
+
+#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)
+ /*
+ * 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 && !defined SYS_CYGWIN32
+ (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();
+ 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.
+ */
+ getconfig(argc, argv);
+
+ initializing = 0;
+
+#if defined(SYS_WINNT) && !defined(NODETACH)
+# if defined(DEBUG)
+ if(!debug)
+ {
+#endif
+ /* 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
+
+ /*
+ * 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_recv_buff_event();
+ WaitHandles[2] = get_timer_handle();
+
+ for (;;) {
+ DWORD Index = MsgWaitForMultipleObjectsEx(sizeof(WaitHandles)/sizeof(WaitHandles[0]), WaitHandles, INFINITE, QS_ALLEVENTS, MWMO_ALERTABLE);
+ switch (Index) {
+ case WAIT_OBJECT_0 + 0 : /* exit request */
+ exit(0);
+ break;
+
+ case WAIT_OBJECT_0 + 1 : {/* recv buffer */
+ if (NULL != (rbuf = get_full_recv_buffer())) {
+ if (rbuf->receiver != NULL) {
+ rbuf->receiver(rbuf);
+ }
+ freerecvbuf(rbuf);
+ }
+ }
+ break;
+
+ case WAIT_OBJECT_0 + 2 : /* 1 second timer */
+ timer();
+ break;
+
+ case WAIT_OBJECT_0 + 3 : { /* Windows message */
+ MSG msg;
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT) {
+ exit(0);
+ }
+ DispatchMessage(&msg);
+ }
+ }
+ break;
+
+ case WAIT_IO_COMPLETION : /* loop */
+ case WAIT_TIMEOUT :
+ break;
+
+ }
+
+ }
+ }
+# 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");
+ else if (debug) {
+# if !defined SYS_VXWORKS && !defined SYS_CYGWIN32 && !defined SCO5_CLOCK /* to unclutter log */
+ msyslog(LOG_DEBUG, "select(): nfound=%d, error: %m", nfound);
+# endif
+ }
+# 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;
+ }
+
+ /*
+ * 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
+ */
+ }
+# endif /* HAVE_IO_COMPLETION_PORT */
+ exit(1); /* unreachable */
+ return 1; /* DEC OSF cc braindamage */
+}
+
+
+#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
+/*
+ * 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;
+}
+#else /* not DEBUG */
+/*
+ * 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 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/contrib/ntp/ntpd/refclock_acts.c b/contrib/ntp/ntpd/refclock_acts.c
new file mode 100644
index 0000000..9c367b5
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_acts.c
@@ -0,0 +1,981 @@
+/*
+ * 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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_control.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);
+
+ /*
+ * Yes, I know this code incorrectly thinks that 2000 is a leap
+ * year. The ACTS timecode format croaks then anyway. 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.
+ * NOTE: year 2000 IS a leap year!!! ghealton Y2KFixes
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if ( pp->year <= YEAR_PIVOT ) pp->year += 100; /* Y2KFixes */
+ if ( !isleap_tm(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.
+ */
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ 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/contrib/ntp/ntpd/refclock_arbiter.c b/contrib/ntp/ntpd/refclock_arbiter.c
new file mode 100644
index 0000000..045a93a
--- /dev/null
+++ b/contrib/ntp/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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.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;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+}
+
+#else
+int refclock_arbiter_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_arc.c b/contrib/ntp/ntpd/refclock_arc.c
new file mode 100644
index 0000000..1db8a9a
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_arc.c
@@ -0,0 +1,1323 @@
+/*
+ * refclock_arc - clock driver for ARCRON MSF receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
+static const char arc_version[] = { "V1.1 1997/06/23" };
+
+#undef ARCRON_DEBUG /* Define only while in development... */
+
+#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.
+
+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.
+
+-------------------------------------------------------------------------------
+
+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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.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
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the ARCRON MSF 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 DESCRIPTION "ARCRON MSF Receiver"
+
+#define NSAMPLESLONG 8 /* Stages of long filter. */
+
+#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
+ };
+
+/* Chose filter length dependent on fudge flag 4. */
+#define CHOSENSAMPLES(pp) \
+(((pp)->sloppyclockflag & CLK_FLAG4) ? NSAMPLESLONG : NSAMPLES)
+ /*
+Chose how many filter samples to keep. Several factors are in play.
+
+ 1) Discard at least one sample to allow a spike value to be
+ discarded.
+
+ 2) Discard about 1-in-8 to 1-in-30 samples to handle spikes.
+
+ 3) Keep an odd number of samples to avoid median value being biased
+ high or low.
+*/
+#define NKEEP(pp) ((CHOSENSAMPLES(pp) - 1 - (CHOSENSAMPLES(pp)>>3)) | 1)
+
+#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 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 { \
+ if((up)->ev.next != 0) { break; } /* WHOOPS! */ \
+ peer->nextdate = current_time + QUEUETICK; \
+ } while(0)
+
+#if 0
+/* Placeholder event handler---does nothing safely---soaks up lose tick. */
+static void
+dummy_event_handler(
+ struct peer *peer
+ )
+{
+#ifdef ARCRON_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 ARCRON_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 ARCRON_DEBUG
+ else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
+#endif
+ }
+}
+#endif /* 0 */
+
+/*
+ * 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 ARCRON_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 ARCRON_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 ARCRON_DEBUG
+ if(debug)
+ { printf("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;
+ memcpy((char *)&pp->refid, REFID, 4);
+ /* 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
+ return(1);
+}
+
+
+/*
+ * arc_shutdown - shut down the clock
+ */
+static void
+arc_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct arcunit *up;
+ struct refclockproc *pp;
+
+ 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 ARCRON_DEBUG
+ if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
+#endif
+ if(spaceleft < sl) { /* Should not normally happen... */
+#ifdef ARCRON_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, bst, status;
+ int arc_last_offset;
+
+ /*
+ * 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 ARCRON_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 ARCRON_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 ARCRON_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 ARCRON_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 ARCRON_DEBUG
+ if(debug > 1) { printf("arc: clearing timestamp.\n"); }
+#endif
+ }
+ }
+
+ /* 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 ARCRON_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. */
+ up->quality = (q & 0xf);
+#ifdef DEBUG
+ if(debug) { printf("arc: signal quality %d.\n", up->quality); }
+#endif
+ } else if( /* (r == '2') && */ up->resyncing) {
+#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. */
+
+#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 ARCRON_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 ARCRON_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(). */
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+
+ /* We don't use the micro-/milli- second part... */
+ pp->usec = 0;
+ pp->msec = 0;
+
+ 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, &bst, &status);
+
+ /* Validate format and numbers. */
+ if(n != 9) {
+#ifdef ARCRON_DEBUG
+ /* Would expect to have caught major problems already... */
+ if(debug) { printf("arc: badly formatted data.\n"); }
+#endif
+ 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. */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ /* Check that BST/UTC bits are the complement of one another. */
+ if(!(bst & 2) == !(bst & 4)) {
+ 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, bst, 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;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+ up->status = status;
+
+ 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) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ }
+ }
+
+ /* If clock signal quality is unknown, revert to default PRECISION...*/
+ if(up->quality == QUALITY_UNKNOWN) { peer->precision = PRECISION; }
+ /* ...else improve precision if flag3 is set... */
+ 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 ARCRON_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: CHOSENSAMPLES(pp) = %d.\n", CHOSENSAMPLES(pp));
+ printf("arc: NKEEP(pp) = %d.\n", NKEEP(pp));
+ 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)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ 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 ARCRON_DEBUG
+ msyslog(LOG_NOTICE, "ARCRON: unit %d: problem sending", unit);
+#endif
+ 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;
+ pp->lencode = 0;
+ memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
+
+#if 0
+ /* Flush input. */
+ tcflush(pp->io.fd, TCIFLUSH);
+#endif
+
+ /* Resync if our next scheduled resync time is here or has passed. */
+ resync_needed = (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
+ 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/contrib/ntp/ntpd/refclock_as2201.c b/contrib/ntp/ntpd/refclock_as2201.c
new file mode 100644
index 0000000..2c10440
--- /dev/null
+++ b/contrib/ntp/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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.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.%3d", &pp->year,
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->msec)
+ != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * 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/contrib/ntp/ntpd/refclock_atom.c b/contrib/ntp/ntpd/refclock_atom.c
new file mode 100644
index 0000000..63e17c1
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_atom.c
@@ -0,0 +1,488 @@
+/*
+ * refclock_atom - clock driver for 1-pps signals
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ATOM)
+
+#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"
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TERMIOS_H
+# include <sys/termios.h>
+#endif
+#ifdef HAVE_SYS_PPSCLOCK_H
+# include <sys/ppsclock.h>
+#endif
+#ifdef HAVE_PPSAPI
+#include <sys/timepps.h>
+#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.
+ *
+ * In order for this driver to work, the local clock must be set to
+ * within +-500 ms by another means, such as a radio clock or NTP
+ * itself. The 1-pps signal is connected via a serial port and gadget
+ * box consisting of a one-shot flopflop and RS232 level converter.
+ * Conntection is either via the carrier detect (DCD) lead or via the
+ * receive data (RD) lead. The incidental jitter using the DCD lead is
+ * essentially the interrupt latency. The incidental jitter using the RD
+ * lead has an additional component due to the line sampling clock. When
+ * operated at 38.4 kbps, this arrangement has a worst-case jitter less
+ * than 26 us.
+ *
+ * There are four ways in which this driver can be used. They are
+ * described in decreasing order of merit below. The first way uses the
+ * ppsapi STREAMS module and the LDISC_PPS line discipline, while the
+ * second way uses the ppsclock STREAMS module and the LDISC_PPS line
+ * discipline. Either of these works only for the baseboard serial ports
+ * of the Sun SPARC IPC and clones. However, the ppsapi uses the
+ * proposed IETF interface expected to become standard for PPS signals.
+ * The serial port to be used is specified by the pps command in the
+ * configuration file. This driver reads the timestamp directly by a
+ * designated ioctl() system call.
+ *
+ * The third way uses the LDISC_CLKPPS line discipline and works for
+ * any architecture supporting a serial port. If after a few seconds
+ * this driver finds no ppsclock module configured, it attempts to open
+ * a serial port device /dev/pps%d, where %d is the unit number, and
+ * assign the LDISC_CLKPPS line discipline to it. If the line discipline
+ * fails, no harm is done except the accuracy is reduced somewhat. The
+ * pulse generator in the gadget box is adjusted to produce a start bit
+ * of length 26 usec at 38400 bps. Used with the LDISC_CLKPPS line
+ * discipline, this produces an ASCII DEL character ('\377') followed by
+ * a timestamp at each seconds epoch.
+ *
+ * The fourth way involves an auxiliary radio clock driver which calls
+ * the PPS driver with a timestamp captured by that driver. This use is
+ * documented in the source code for the driver(s) involved. Note that
+ * some drivers collect the sample information themselves before calling
+ * pps_sample(), and others call knowing only that they are running
+ * shortly after an on-time tick and they expect to retrieve the PPS
+ * offset, fudge their result, and insert it into the timestream.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic. The fudge
+ * time1 parameter can be used to compensate for miscellaneous UART and
+ * OS delays. Allow about 247 us for uart delays at 38400 bps and about
+ * 1 ms for STREAMS nonsense with older workstations. Velocities may
+ * vary with modern workstations.
+ */
+/*
+ * Interface definitions
+ */
+#ifdef HAVE_PPSAPI
+extern int pps_assert;
+#endif /* HAVE_PPSAPI */
+#ifdef TTYCLK
+#define DEVICE "/dev/pps%d" /* device name and unit */
+#ifdef B38400
+#define SPEED232 B38400 /* uart speed (38400 baud) */
+#else
+#define SPEED232 EXTB /* as above */
+#endif
+#endif /* TTYCLK */
+
+#define PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "PPS\0" /* reference ID */
+#define DESCRIPTION "PPS Clock Discipline" /* WRU */
+
+#define FLAG_TTY 0x01 /* tty_clk heard from */
+#define FLAG_PPS 0x02 /* ppsclock heard from */
+#define FLAG_AUX 0x04 /* auxiliary PPS source */
+
+static struct peer *pps_peer; /* atom driver for auxiliary PPS sources */
+
+#ifdef TTYCLK
+static void atom_receive P((struct recvbuf *));
+#endif /* TTYCLK */
+
+/*
+ * Unit control structure
+ */
+struct atomunit {
+#ifdef HAVE_PPSAPI
+ pps_info_t pps_info; /* pps_info control */
+#endif /* HAVE_PPSAPI */
+#ifdef PPS
+ struct ppsclockev ev; /* ppsclock control */
+#endif /* PPS */
+ int flags; /* flags that wave */
+};
+
+/*
+ * Function prototypes
+ */
+static int atom_start P((int, struct peer *));
+static void atom_shutdown P((int, struct peer *));
+static void atom_poll P((int, struct peer *));
+#if defined(PPS) || defined(HAVE_PPSAPI)
+static int atom_pps P((struct peer *));
+#endif /* PPS || HAVE_PPSAPI */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_atom = {
+ atom_start, /* start up driver */
+ atom_shutdown, /* shut down driver */
+ atom_poll, /* transmit poll message */
+ noentry, /* not used (old atom_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old atom_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * atom_start - initialize data for processing
+ */
+static int
+atom_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ int flags;
+#ifdef TTYCLK
+ int fd = 0;
+ char device[20];
+ int ldisc = LDISC_CLKPPS;
+#endif /* TTYCLK */
+
+ pps_peer = peer;
+ flags = 0;
+
+#ifdef TTYCLK
+# if defined(SCO5_CLOCK)
+ ldisc = LDISC_RAW; /* DCD timestamps without any line discipline */
+# endif
+ /*
+ * Open serial port. Use LDISC_CLKPPS line discipline only
+ * if the LDISC_PPS line discipline is not availble,
+ */
+# if defined(PPS) || defined(HAVE_PPSAPI)
+ if (fdpps <= 0)
+# endif
+ {
+ (void)sprintf(device, DEVICE, unit);
+ if ((fd = refclock_open(device, SPEED232, ldisc)) != 0)
+ flags |= FLAG_TTY;
+ }
+#endif /* TTYCLK */
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct atomunit *)emalloc(sizeof(struct atomunit)))) {
+#ifdef TTYCLK
+ if (flags & FLAG_TTY)
+ (void) close(fd);
+#endif /* TTYCLK */
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct atomunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+#ifdef TTYCLK
+ if (flags & FLAG_TTY) {
+ pp->io.clock_recv = atom_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);
+ }
+ }
+#endif /* TTYCLK */
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->flags = flags;
+ return (1);
+}
+
+
+/*
+ * atom_shutdown - shut down the clock
+ */
+static void
+atom_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+#ifdef TTYCLK
+ if (up->flags & FLAG_TTY)
+ io_closeclock(&pp->io);
+#endif /* TTYCLK */
+ if (pps_peer == peer)
+ pps_peer = 0;
+ free(up);
+}
+
+
+#if defined(PPS) || defined(HAVE_PPSAPI)
+/*
+ * atom_pps - receive data from the LDISC_PPS discipline
+ */
+static int
+atom_pps(
+ struct peer *peer
+ )
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+#ifdef HAVE_PPSAPI
+ struct timespec timeout;
+# ifdef HAVE_TIMESPEC
+ struct timespec ts;
+# else
+ struct timeval ts;
+# endif /* HAVE_TIMESPEC */
+#endif /* HAVE_PPSAPI */
+ l_fp lftmp;
+ double doffset;
+ int i;
+#if !defined(HAVE_PPSAPI)
+ int request =
+# ifdef HAVE_CIOGETEV
+ CIOGETEV
+# endif
+# ifdef HAVE_TIOCGPPSEV
+ TIOCGPPSEV
+# endif
+ ;
+#endif /* HAVE_PPSAPI */
+
+ /*
+ * This routine is called once per second when the LDISC_PPS
+ * discipline 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.
+ */
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ /*
+ * 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. Round the nanoseconds
+ * to microseconds with great care.
+ */
+ if (fdpps <= 0)
+ return (1);
+#ifdef HAVE_PPSAPI
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 0;
+ i = up->pps_info.assert_sequence;
+ if (time_pps_fetch(fdpps, PPS_TSFMT_TSPEC, &up->pps_info, &timeout)
+ < 0)
+ return (2);
+ if (i == up->pps_info.assert_sequence)
+ return (3);
+ if (pps_assert)
+ ts = up->pps_info.assert_timestamp;
+ else
+ ts = up->pps_info.clear_timestamp;
+ pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
+ ts.tv_nsec = (ts.tv_nsec + 500) / 1000;
+ if (ts.tv_nsec > 1000000) {
+ ts.tv_nsec -= 1000000;
+ ts.tv_sec++;
+ }
+ TVUTOTSF(ts.tv_nsec, pp->lastrec.l_uf);
+#else
+ i = up->ev.serial;
+ if (ioctl(fdpps, request, (caddr_t)&up->ev) < 0)
+ return (2);
+ if (i == up->ev.serial)
+ return (3);
+ pp->lastrec.l_ui = up->ev.tv.tv_sec + JAN_1970;
+ TVUTOTSF(up->ev.tv.tv_usec, pp->lastrec.l_uf);
+#endif /* HAVE_PPSAPI */
+ up->flags |= FLAG_PPS;
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ LFPTOD(&lftmp, doffset);
+ SAMPLE(-doffset + pp->fudgetime1);
+ return (0);
+}
+#endif /* PPS || HAVE_PPSAPI */
+
+#ifdef TTYCLK
+/*
+ * atom_receive - receive data from the LDISC_CLK discipline
+ */
+static void
+atom_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp lftmp;
+ double doffset;
+
+ /*
+ * This routine is called once per second when the serial
+ * interface is in use. It snatches the timestamp from the
+ * buffer and saves the sign-extended fraction in a circular
+ * buffer for processing at the next poll event.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
+ &pp->lastrec);
+
+ /*
+ * Save the timestamp for billboards. Sign-extend the fraction
+ * and stash in the buffer. No harm is done if previous data are
+ * overwritten. Do this only if the ppsclock gizmo is not
+ * working.
+ */
+ if (up->flags & FLAG_PPS)
+ return;
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ LFPTOD(&lftmp, doffset);
+ SAMPLE(-doffset + pp->fudgetime1);
+}
+#endif /* TTYCLK */
+
+/*
+ * pps_sample - receive PPS data from some other clock driver
+ */
+int
+pps_sample(
+ l_fp *offset
+ )
+{
+ register struct peer *peer;
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ l_fp lftmp;
+ double doffset;
+
+ /*
+ * 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.
+ */
+ peer = pps_peer;
+ if (peer == 0) /* nobody home */
+ return 1;
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ /*
+ * 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.
+ */
+ up->flags |= FLAG_AUX;
+ 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
+ */
+static void
+atom_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+#if defined(PPS) || defined(HAVE_PPSAPI)
+ register struct atomunit *up;
+#endif /* PPS || HAVE_PPSAPI */
+ struct refclockproc *pp;
+
+ /*
+ * Accumulate samples in the median filter. At the end of each
+ * poll interval, do a little bookeeping and process the
+ * samples.
+ */
+ pp = peer->procptr;
+#if defined(PPS) || defined(HAVE_PPSAPI)
+ up = (struct atomunit *)pp->unitptr;
+ if (!(up->flags & !(FLAG_AUX | FLAG_TTY))) {
+ int err;
+
+ err = atom_pps(peer);
+ if (err > 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+#endif /* PPS || HAVE_PPSAPI */
+ pp->polls++;
+ if (peer->burst > 0)
+ return;
+ if (pp->coderecv == pp->codeproc) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ return;
+ }
+
+ /*
+ * Valid time (leap bits zero) 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.
+ */
+ if (pps_update) {
+ pp->leap = LEAP_NOWARNING;
+ } else {
+ pp->leap = LEAP_NOTINSYNC;
+ return;
+ }
+ pp->variance = 0;
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ peer->burst = MAXSTAGE;
+}
+
+#else
+int refclock_atom_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_bancomm.c b/contrib/ntp/ntpd/refclock_bancomm.c
new file mode 100644
index 0000000..95c211b
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_bancomm.c
@@ -0,0 +1,483 @@
+/* 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 <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.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 */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 2 /* max number of VME units */
+
+/*
+ * 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 */
+};
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static double 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_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,
+ vme_shutdown,
+ vme_poll,
+ noentry, /* not used (old vme_control) */
+ vme_init,
+ noentry, /* not used (old 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;
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor[i] = 0.0;
+ stratumtouse[i] = 0;
+ sloppyclockflag[i] = 0;
+ }
+}
+
+/*
+ * 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];
+
+ /*
+ * Check configuration info.
+ */
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "vme_start: unit %d invalid", unit);
+ return (0);
+ }
+
+ /*
+ * 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.
+ */
+ pp->leap = LEAP_NOWARNING;
+ peer->precision = VMEPRECISION;
+ peer->stratum = stratumtouse[unit];
+ memcpy( (char *)&peer->refid, USNOREFID,4);
+
+ peer->refid = htonl(VMEHSREFID);
+
+ 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;
+
+ pp = peer->procptr;
+
+ if (unit >= MAXUNITS) {
+ msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit);
+ return;
+ }
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ 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;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+}
+
+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/contrib/ntp/ntpd/refclock_chronolog.c b/contrib/ntp/ntpd/refclock_chronolog.c
new file mode 100644
index 0000000..2982ecc
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_chronolog.c
@@ -0,0 +1,343 @@
+/*
+ * 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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.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.
+ */
+ pp->msec = 0;
+ 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;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ 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/contrib/ntp/ntpd/refclock_chu.c b/contrib/ntp/ntpd/refclock_chu.c
new file mode 100644
index 0000000..0b2ae78
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_chu.c
@@ -0,0 +1,1464 @@
+/*
+ * refclock_chu - clock driver for Canadian radio CHU receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_CHU)
+
+/* #define AUDIO_CHUa */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+
+#ifdef AUDIO_CHU
+#ifdef HAVE_SYS_AUDIOIO_H
+#include <sys/audioio.h>
+#endif /* HAVE_SYS_AUDIOIO_H */
+#ifdef HAVE_SUN_AUDIOIO_H
+#include <sun/audioio.h>
+#endif /* HAVE_SUN_AUDIOIO_H */
+#endif /* AUDIO_CHU */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Clock driver for Canadian radio CHU receivers
+ *
+ * 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 the minimuf and icom programs as
+ * propagation conditions change throughout the day and night.
+ *
+ * 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 -d -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. When the modem driver
+ * is compiled, fudge flag3 enables the ppsclock line discipline. 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 speaker volume must be set before the driver is started.
+ */
+
+/*
+ * Interface definitions
+ */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "CHU" /* reference ID */
+#ifdef AUDIO_CHU
+#define DESCRIPTION "CHU Modem Receiver" /* WRU */
+
+/*
+ * Audio demodulator definitions
+ */
+#define AUDIO_BUFSIZ 160 /* codec buffer size (Solaris only) */
+#define SAMPLE 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 DRPOUT 100. /* dropout signal level */
+#define LIMIT 1000. /* soft limiter threshold */
+#define AGAIN 6. /* baseband gain */
+#define LAG 10 /* discriminator lag */
+#else
+#define DEVICE "/dev/chu%d" /* device name and unit */
+#define SPEED232 B300 /* UART speed (300 baud) */
+#define DESCRIPTION "CHU Audio Receiver" /* WRU */
+#endif /* AUDIO_CHU */
+
+/*
+ * 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 MINSYNC 8 /* min sync distance (of 16) */
+#define MINDEC .5 /* decoder majority rule (of 1.) */
+#define MINSTAMP 20 /* min timestamps (of 60) */
+
+/*
+ * Hex extension codes (>= 16)
+ */
+#define HEX_MISS 16 /* miss */
+#define HEX_SOFT 17 /* soft error */
+#define HEX_HARD 18 /* hard error */
+
+/*
+ * Error flags (up->errflg)
+ */
+#define CHU_ERR_RUNT 0x001 /* runt burst */
+#define CHU_ERR_NOISE 0x002 /* noise burst */
+#define CHU_ERR_BFRAME 0x004 /* invalid format B frame sync */
+#define CHU_ERR_BFORMAT 0x008 /* invalid format B data */
+#define CHU_ERR_AFRAME 0x010 /* invalid format A frame sync */
+#define CHU_ERR_DECODE 0x020 /* invalid data decode */
+#define CHU_ERR_STAMP 0x040 /* too few timestamps */
+#define CHU_ERR_AFORMAT 0x080 /* invalid format A data */
+#ifdef AUDIO_CHU
+#define CHU_ERR_ERROR 0x100 /* codec error (overrun) */
+#endif /* AUDIO_CHU */
+
+#ifdef AUDIO_CHU
+struct surv {
+ double shift[12]; /* mark register */
+ double max, min; /* max/min envelope signals */
+ double dist; /* sample distance */
+ int uart; /* decoded character */
+};
+#endif /* AUDIO_CHU */
+
+/*
+ * 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 bufptr; /* buffer index pointer */
+ int pollcnt; /* poll message counter */
+
+ /*
+ * 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 */
+
+#ifdef AUDIO_CHU
+ /*
+ * Audio codec variables
+ */
+ double comp[SIZE]; /* decompanding table */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int bufcnt; /* samples in buffer */
+ 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 /* AUDIO_CHU */
+};
+
+/*
+ * 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_update P((struct peer *, int));
+static void chu_year P((struct peer *, int));
+static int chu_dist P((int, int));
+#ifdef AUDIO_CHU
+static void chu_uart P((struct surv *, double));
+static void chu_rf P((struct peer *, double));
+static void chu_gain P((struct peer *));
+static int chu_audio P((void));
+static void chu_debug P((void));
+#endif /* AUDIO_CHU */
+
+/*
+ * Global variables
+ */
+static char hexchar[] = "0123456789abcdef_-=";
+#ifdef AUDIO_CHU
+#ifdef HAVE_SYS_AUDIOIO_H
+struct audio_device device; /* audio device ident */
+#endif /* HAVE_SYS_AUDIOIO_H */
+static struct audio_info info; /* audio device info */
+static int chu_ctl_fd; /* audio control file descriptor */
+#endif /* AUDIO_CHU */
+
+/*
+ * 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;
+
+ /*
+ * Local variables
+ */
+ int fd; /* file descriptor */
+#ifdef AUDIO_CHU
+ int i; /* index */
+ double step; /* codec adjustment */
+
+ /*
+ * Open audio device
+ */
+ fd = open("/dev/audio", O_RDWR | O_NONBLOCK, 0777);
+ if (fd == -1) {
+ perror("chu: audio");
+ return (0);
+ }
+#else
+ char device[20]; /* device name */
+
+ /*
+ * Open serial port. Use RAW line discipline (required).
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_RAW))) {
+ return (0);
+ }
+#endif /* AUDIO_CHU */
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct chuunit *)
+ emalloc(sizeof(struct chuunit)))) {
+ (void) 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)) {
+ (void)close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ DTOLFP(CHAR, &up->charstamp);
+ up->pollcnt = 2;
+#ifdef AUDIO_CHU
+ up->gain = (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN) / 2;
+ if (chu_audio() < 0) {
+ io_closeclock(&pp->io);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * 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. / SAMPLE, &up->tick);
+#endif /* AUDIO_CHU */
+ 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;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+#ifdef AUDIO_CHU
+
+/*
+ * chu_receive - receive data from the audio device
+ */
+static void
+chu_receive(
+ struct recvbuf *rbufp /* receive buffer structure pointer */
+ )
+{
+ struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ /*
+ * Local variables
+ */
+ double sample; /* codec sample */
+ u_char *dpt; /* buffer pointer */
+ l_fp ltemp; /* l_fp temp */
+ double dtemp; /* double temp */
+ int isneg; /* parity flag */
+ int i, j; /* index temps */
+
+ 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.
+ */
+ up->timestamp = rbufp->recv_time;
+ up->bufcnt = rbufp->recv_length;
+ DTOLFP(up->bufcnt * 1. / SAMPLE, &ltemp);
+ L_SUB(&up->timestamp, &ltemp);
+ dpt = (u_char *)&rbufp->recv_space;
+ for (up->bufptr = 0; up->bufptr < up->bufcnt; up->bufptr++) {
+ 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++;
+ }
+ up->seccnt = (up->seccnt + 1) % SAMPLE;
+ if (up->seccnt == 0) {
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = AUDIO_LINE_IN;
+ else
+ up->port = AUDIO_MICROPHONE;
+ chu_gain(peer);
+ up->clipcnt = 0;
+ }
+ chu_rf(peer, sample);
+
+ /*
+ * During development, it is handy to have an audio
+ * monitor that can be switched to various signals. This
+ * code converts the linear signal left in up->monitor
+ * to codec format. If we can get the grass out of this
+ * thing and improve modem performance, this expensive
+ * code will be permanently nixed.
+ */
+ isneg = 0;
+ dtemp = up->monitor;
+ if (sample < 0) {
+ isneg = 1;
+ dtemp-= dtemp;
+ }
+ i = 0;
+ j = OFFSET >> 1;
+ while (j != 0) {
+ if (dtemp > up->comp[i])
+ i += j;
+ else if (dtemp < up->comp[i])
+ i -= j;
+ else
+ break;
+ j >>= 1;
+ }
+ if (isneg)
+ *dpt = ~(i + OFFSET);
+ else
+ *dpt = ~i;
+ dpt++;
+ L_ADD(&up->timestamp, &up->tick);
+ }
+
+ /*
+ * Squawk to the monitor speaker if enabled.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ if (write(pp->io.fd, (u_char *)&rbufp->recv_space,
+ (u_int)up->bufcnt) < 0)
+ perror("chu:");
+}
+
+
+/*
+ * 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.
+ */
+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; /* index temps */
+
+ 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;
+/*
+printf("%8.3f %8.3f\n", disc, lpf);
+return;
+*/
+ /*
+ * 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. / SAMPLE;
+ if (up->baud > 1. / (BAUD * 8.)) {
+ up->baud -= 1. / (BAUD * 8.);
+ sp = &up->surv[up->decptr];
+ span = sp->max - sp->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.
+ */
+void
+chu_uart(
+ struct surv *sp, /* survivor structure pointer */
+ double sample /* baseband signal */
+ )
+{
+ /*
+ * Local variables
+ */
+ double max, min; /* max/min envelope */
+ double slice; /* slice level */
+ double dist; /* distance */
+ double dtemp; /* double temp */
+ int i; /* index temp */
+
+ /*
+ * Save the sample and shift right. At the same time, measure
+ * the maximum and minimum over all eleven samples.
+ */
+ max = -1e6;
+ min = 1e6;
+ sp->shift[0] = sample;
+ for (i = 11; i > 0; i--) {
+ sp->shift[i] = sp->shift[i - 1];
+ if (sp->shift[i] > max)
+ max = sp->shift[i];
+ if (sp->shift[i] < min)
+ 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 = (max + 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 - min;
+ } else if (i == 10) {
+ dist += max - dtemp;
+ } else {
+ if (dtemp > slice)
+ dist += dtemp - min;
+ else
+ dist += max - dtemp;
+ }
+ }
+ sp->max = max;
+ sp->min = min;
+ sp->dist = dist / (11 * (max - min));
+}
+
+
+#else /* AUDIO_CHU */
+/*
+ * chu_receive - receive data from the serial interface
+ */
+static void
+chu_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);
+}
+#endif /* AUDIO_CHU */
+
+
+/*
+ * chu_decode - decode the data
+ */
+static void
+chu_decode(
+ struct peer *peer, /* peer structure pointer */
+ int hexhex /* data character */
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ /*
+ * Local variables
+ */
+ l_fp tstmp; /* timestamp temp */
+ double dtemp; /* double temp */
+
+ 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;
+
+ /*
+ * Local variables
+ */
+ int i; /* index temp */
+
+ 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->errflg |= CHU_ERR_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; otherwise, it is a noise burst and
+ * of no use to anybody.
+ */
+ if (up->burdist >= MINDIST) {
+ chu_update(peer, up->ndx);
+ } else if (up->burdist <= -MINDIST) {
+ chu_year(peer, up->ndx);
+ } else {
+ up->errflg |= CHU_ERR_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)
+ up->pollcnt = 2;
+ else
+ peer->nextdate = current_time + 10;
+}
+
+
+/*
+ * chu_year - decode format B burst
+ */
+static void
+chu_year(
+ struct peer *peer,
+ int nchar
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ /*
+ * Local variables
+ */
+ u_char code[11]; /* decoded timecode */
+ l_fp offset; /* timestamp offset */
+ int leap; /* leap/dut code */
+ int dut; /* UTC1 correction */
+ int tai; /* TAI - UTC correction */
+ int dst; /* Canadian DST code */
+ int i; /* index temp */
+
+ 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.
+ */
+ sprintf(pp->a_lastcode, "%2d %2d ", nchar, -up->burdist);
+ for (i = 0; i < nchar; i++)
+ sprintf(&pp->a_lastcode[strlen(pp->a_lastcode)], "%02x",
+ up->cbuf[i]);
+ pp->lencode = strlen(pp->a_lastcode);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug > 2)
+ printf("chu: %s\n", pp->a_lastcode);
+#endif
+ if (-up->burdist < 40) {
+ up->errflg |= CHU_ERR_BFRAME;
+ return;
+ }
+
+ /*
+ * Convert the burst data to internal format. If this succeeds,
+ * save the timestamps for later. The leap, dut, tai and dst are
+ * presently unused.
+ */
+ 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", &leap, &dut,
+ &pp->year, &tai, &dst) != 5) {
+ up->errflg |= CHU_ERR_BFORMAT;
+ return;
+ }
+ 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_update - decode format A burst
+ */
+static void
+chu_update(
+ struct peer *peer,
+ int nchar
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ /*
+ * Local variables
+ */
+ l_fp offset; /* timestamp offset */
+ int val; /* distance */
+ int temp; /* common temp */
+ int i, j, k; /* index temps */
+
+ 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 AUDIO_CHU
+ sprintf(pp->a_lastcode, "%3d %4.0f %2d %2d %2d %2d %1d ",
+ up->gain, up->maxsignal, nchar, up->burdist, k, up->syndist,
+ temp);
+#else
+ sprintf(pp->a_lastcode, "%2d %2d %2d %2d %1d ", nchar,
+ up->burdist, k, up->syndist, temp);
+#endif /* AUDIO_CHU */
+ for (i = 0; i < nchar; i++)
+ sprintf(&pp->a_lastcode[strlen(pp->a_lastcode)], "%02x",
+ up->cbuf[i]);
+ pp->lencode = strlen(pp->a_lastcode);
+ if (pp->sloppyclockflag & CLK_FLAG4)
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug > 2)
+ printf("chu: %s\n", pp->a_lastcode);
+#endif
+ if (up->syndist < MINSYNC) {
+ up->errflg |= CHU_ERR_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) {
+ 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]++;
+ up->decode[i++][(up->cbuf[j] >> 4) & 0xf]++;
+ }
+ up->burstcnt++;
+}
+
+
+/*
+ * chu_poll - called by the transmit procedure
+ */
+static void
+chu_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ /*
+ * Local variables
+ */
+ u_char code[11]; /* decoded timecode */
+ l_fp toffset, offset; /* l_fp temps */
+ int mindist; /* minimum distance */
+ int val1, val2; /* maximum distance */
+ int synchar; /* should be a 6 in traffic */
+ double dtemp; /* double temp */
+ int temp; /* common temp */
+ int i, j, k; /* index temps */
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Process the last burst, if still in the burst buffer.
+ * Don't mess with anything if nothing has been heard.
+ */
+ chu_burst(peer);
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ if (up->burstcnt == 0) {
+ chu_clear(peer);
+ return;
+ }
+
+ /*
+ * Majority decoder. Select the character with the most
+ * occurrences for each burst position. The distance for the
+ * character is this number of occurrences. If no occurrences
+ * are found, assume a miss '_'; if only a single occurrence is
+ * found, assume a soft error '-'; if two different characters
+ * with the same distance are found, assume a hard error '='.
+ * The decoding distance is defined as the minimum of the
+ * character distances.
+ */
+ mindist = 16;
+ for (i = 0; i < 10; 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 && val1 == val2)
+ code[i] = HEX_HARD;
+ else if (val1 < 2)
+ code[i] = HEX_SOFT;
+ else
+ code[i] = k;
+ if (val1 < mindist)
+ mindist = val1;
+ code[i] = hexchar[code[i]];
+ }
+ code[i] = 0;
+ if (mindist < up->burstcnt * 2 * MINDEC)
+ up->errflg |= CHU_ERR_DECODE;
+ if (up->ntstamp < MINSTAMP)
+ up->errflg |= CHU_ERR_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->errflg |= CHU_ERR_AFORMAT;
+ sprintf(pp->a_lastcode,
+ "%02x %4d %3d %02d:%02d:%02d %2d %2d %2d",
+ up->errflg, pp->year, pp->day, pp->hour, pp->minute,
+ pp->second, up->burstcnt, mindist, up->ntstamp);
+ pp->lencode = strlen(pp->a_lastcode);
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+#ifdef DEBUG
+ if (debug > 2)
+ printf("chu: %s\n", pp->a_lastcode);
+#endif
+ if (up->errflg & (CHU_ERR_DECODE | CHU_ERR_STAMP |
+ CHU_ERR_AFORMAT)) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ chu_clear(peer);
+ return;
+ }
+ L_CLR(&offset);
+ if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT,
+ up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ chu_clear(peer);
+ return;
+ }
+ pp->polls++;
+ pp->leap = LEAP_NOWARNING;
+ pp->lastref = offset;
+ pp->variance = 0;
+ for (i = 0; i < up->ntstamp; i++) {
+ toffset = offset;
+ L_SUB(&toffset, &up->tstamp[i]);
+ LFPTOD(&toffset, dtemp);
+ SAMPLE(dtemp + FUDGE + pp->fudgetime1);
+ }
+ if (i > 0)
+ refclock_receive(peer);
+ chu_clear(peer);
+}
+
+
+/*
+ * chu_clear - clear decoding matrix
+ */
+static void
+chu_clear(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct chuunit *up;
+
+ /*
+ * Local variables
+ */
+ int i, j; /* index temps */
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+
+ /*
+ * Clear stuff for following minute.
+ */
+ up->ndx = up->ntstamp = up->prevsec = 0;
+ up->errflg = 0;
+ up->burstcnt = 0;
+ for (i = 0; i < 20; i++) {
+ for (j = 0; j < 16; j++)
+ up->decode[i][j] = 0;
+ }
+}
+
+
+/*
+ * 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 */
+ )
+{
+ /*
+ * Local variables
+ */
+ int val; /* bit count */
+ int temp; /* misc temporary */
+ int i; /* index temporary */
+
+ /*
+ * 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 AUDIO_CHU
+/*
+ * 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. Set the new bits in the structure
+ * and call AUDIO_SETINFO. Upon return, the old bits are in the
+ * structure.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > AUDIO_MAX_GAIN)
+ up->gain = AUDIO_MAX_GAIN;
+ } else if (up->clipcnt > SAMPLE / 100) {
+ up->gain -= 4;
+ if (up->gain < AUDIO_MIN_GAIN)
+ up->gain = AUDIO_MIN_GAIN;
+ }
+ AUDIO_INITINFO(&info);
+ info.record.port = up->port;
+ info.record.gain = up->gain;
+ info.record.error = 0;
+ ioctl(chu_ctl_fd, (int)AUDIO_SETINFO, &info);
+ if (info.record.error)
+ up->errflg |= CHU_ERR_ERROR;
+}
+
+
+/*
+ * chu_audio - initialize audio device
+ *
+ * This code works with SunOS 4.1.3 and Solaris 2.6; however, it is
+ * believed generic and applicable to other systems with a minor twid
+ * or two. All it does is open the device, set the buffer size (Solaris
+ * only), preset the gain and set the input port. It assumes that the
+ * codec sample rate (8000 Hz), precision (8 bits), number of channels
+ * (1) and encoding (ITU-T G.711 mu-law companded) have been set by
+ * default.
+ */
+static int
+chu_audio(
+ )
+{
+ /*
+ * Open audio control device
+ */
+ if ((chu_ctl_fd = open("/dev/audioctl", O_RDWR)) < 0) {
+ perror("audioctl");
+ return(-1);
+ }
+#ifdef HAVE_SYS_AUDIOIO_H
+ /*
+ * Set audio device parameters.
+ */
+ AUDIO_INITINFO(&info);
+ info.record.buffer_size = AUDIO_BUFSIZ;
+ if (ioctl(chu_ctl_fd, (int)AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ close(chu_ctl_fd);
+ return(-1);
+ }
+#endif /* HAVE_SYS_AUDIOIO_H */
+#ifdef DEBUG
+ chu_debug();
+#endif /* DEBUG */
+ return(0);
+}
+
+
+#ifdef DEBUG
+/*
+ * chu_debug - display audio parameters
+ *
+ * This code doesn't really do anything, except satisfy curiousity and
+ * verify the ioctl's work.
+ */
+static void
+chu_debug(
+ )
+{
+ if (debug == 0)
+ return;
+#ifdef HAVE_SYS_AUDIOIO_H
+ ioctl(chu_ctl_fd, (int)AUDIO_GETDEV, &device);
+ printf("chu: name %s, version %s, config %s\n",
+ device.name, device.version, device.config);
+#endif /* HAVE_SYS_AUDIOIO_H */
+ ioctl(chu_ctl_fd, (int)AUDIO_GETINFO, &info);
+ printf(
+ "chu: samples %d, channels %d, precision %d, encoding %d\n",
+ info.record.sample_rate, info.record.channels,
+ info.record.precision, info.record.encoding);
+#ifdef HAVE_SYS_AUDIOIO_H
+ printf("chu: gain %d, port %d, buffer %d\n",
+ info.record.gain, info.record.port,
+ info.record.buffer_size);
+#else /* HAVE_SYS_AUDIOIO_H */
+ printf("chu: gain %d, port %d\n",
+ info.record.gain, info.record.port);
+#endif /* HAVE_SYS_AUDIOIO_H */
+ printf(
+ "chu: samples %d, eof %d, pause %d, error %d, waiting %d, balance %d\n",
+ info.record.samples, info.record.eof,
+ info.record.pause, info.record.error,
+ info.record.waiting, info.record.balance);
+ printf("chu: monitor %d, muted %d\n",
+ info.monitor_gain, info.output_muted);
+}
+#endif /* DEBUG */
+#endif /* AUDIO_CHU */
+
+#else
+int refclock_chu_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_conf.c b/contrib/ntp/ntpd/refclock_conf.c
new file mode 100644
index 0000000..d203dd7
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_conf.c
@@ -0,0 +1,262 @@
+/*
+ * 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
+
+#ifdef CLOCK_TRAK
+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_WWVB
+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(PPS)
+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 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
+
+#ifdef CLOCK_ONCORE
+extern struct refclock refclock_oncore;
+#else
+#define refclock_oncore refclock_none
+#endif
+
+#if defined(CLOCK_JUPITER) && defined(PPS)
+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
+
+
+/*
+ * 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_WWVB_SPECTRACOM */
+ &refclock_true, /* 5 REFCLK_TRUETIME */
+ &refclock_irig, /* 6 REFCLK_IRIG_AUDIO */
+ &refclock_chu, /* 7 REFCLK_CHU */
+ &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 */
+};
+
+u_char num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *);
+
+#else
+int refclock_conf_bs;
+#endif
diff --git a/contrib/ntp/ntpd/refclock_datum.c b/contrib/ntp/ntpd/refclock_datum.c
new file mode 100644
index 0000000..74a37db
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_datum.c
@@ -0,0 +1,873 @@
+/*
+** 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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.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 */
+
+#if defined (WWVBPPS)
+#include <sys/ppsclock.h>
+#endif /* WWVBPPS */
+
+#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 PTSPRECISION (-10) /* precision assumed 1/1024 ms */
+#define DATMREFID "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);
+
+ 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;
+ }
+
+ peer->precision = PTSPRECISION;
+ peer->stratum = 0;
+ memcpy((char *)&peer->refid, DATMREFID, 4);
+
+ /*
+ ** 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 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.
+ */
+
+ index = -1;
+ for (i=0; i<nunits; i++) {
+ if (datum_pts_unit[i]->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 (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) ) {
+
+ 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);*/
+ 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/contrib/ntp/ntpd/refclock_dumbclock.c b/contrib/ntp/ntpd/refclock_dumbclock.c
new file mode 100644
index 0000000..8138d9e
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_dumbclock.c
@@ -0,0 +1,381 @@
+/*
+ * 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(REFCLOCK) && defined(CLOCK_DUMBCLOCK)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.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
+ if (!(fd = refclock_open(device, SPEED232, 0)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct dumbclock_unit *)
+ emalloc(sizeof(struct dumbclock_unit)))) {
+ (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 = 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...
+ */
+ pp->msec = 0;
+ 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;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ up->lasthour = 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/contrib/ntp/ntpd/refclock_gpsvme.c b/contrib/ntp/ntpd/refclock_gpsvme.c
new file mode 100644
index 0000000..a7c6d23
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_gpsvme.c
@@ -0,0 +1,613 @@
+/*
+ * 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 <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/time.h>
+
+#include "gps.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.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 */
+unsigned short *greg[NREGS]; /* GPS registers defined in gps.h */
+void *gps_base; /* Base address of GPS VME card returned by */
+ /* the map_vme() call */
+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();
+
+/* 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 (u_int, struct peer *);
+static void vme_shutdown (int);
+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 (u_int, struct refclockstat *, struct refclockstat *);
+static void vme_buginfo (int, struct refclockbug *);
+
+/*
+ * 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));
+ }
+ }
+ 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;
+
+
+ vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit *));
+ tptr = (struct vmedate *)emalloc(sizeof(struct vmedate *));
+
+
+ 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->usec = tptr->frac;
+
+#ifdef DEBUG
+ if (debug)
+ printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
+ vme->day, vme->hour, vme->minute, vme->second,
+ vme->usec, 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;
+ }
+ TVUTOTSF(vme->usec, vme->lastref.l_uf);
+ tstmp = vme->lastref;
+
+ L_SUB(&tstmp, &vme->lastrec);
+ vme->coderecv++;
+
+ L_ADD(&tstmp, &(fudgefactor[vme->unit]));
+
+ refclock_receive(vme->peer);
+}
+
+/*
+ * vme_control - set fudge factors, return statistics
+ */
+static void
+vme_control(
+ u_int unit,
+ struct refclockstat *in,
+ struct refclockstat *out
+ )
+{
+ 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)
+ fudgefactor[unit] = in->fudgetime1;
+ 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;
+ out->fudgetime1 = fudgefactor[unit];
+ out->fudgetime2.l_ui = 0;
+ out->fudgetime2.l_uf = 0;
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = 0;
+ out->flags = sloppyclockflag[unit];
+ if (unitinuse[unit]) {
+ vme = vmeunits[unit];
+ out->lencode = vme->lencode;
+ out->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->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
+ )
+{
+ 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->usec;
+ 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)
+{
+ 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);
+
+ time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
+ *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 ((void *)NULL);
+ }
+ else
+ return (time_vme);
+}
+
+#else
+int refclock_gpsvme_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_heath.c b/contrib/ntp/ntpd/refclock_heath.c
new file mode 100644
index 0000000..9d297d1
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_heath.c
@@ -0,0 +1,487 @@
+/*
+ * refclock_heath - clock driver for Heath GC-1000 Most Accurate Clock
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HEATH)
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef TM_IN_SYS_TIME
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* not HAVE_SYS_IOCTL_H */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.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.
+ *
+ * The internal DIPswitches should be set to operate at 1200 baud in
+ * MANUAL mode and the current year. The external DIPswitches should be
+ * set to GMT and 24-hour format, or to the host local time zone (with
+ * DST) and 12-hour format. 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. In 12-hour mode
+ * with DST selected the clock will be incorrect by an hour for an
+ * indeterminate amount of time between 0000Z and 0200 on the day DST
+ * changes.
+ *
+ * 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.
+ *
+ * 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 SPEED232 B1200 /* uart speed (1200 baud) */
+#define PRECISION (-4) /* precision assumed (about 100 ms) */
+#define REFID "WWV\0" /* reference ID */
+#define DESCRIPTION "Heath GC-1000 Most Accurate Clock" /* WRU */
+
+#define LENHEATH 23 /* 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 heathunit {
+ int pollcnt; /* poll message counter */
+ l_fp tstamp; /* timestamp of last poll */
+};
+
+/*
+ * 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 */
+};
+
+#if 0
+/*
+ * Gee, Unix so thoughfully omitted code to convert from a struct tm to
+ * a long, so I'll just have to ferret out the inverse myself, the hard way.
+ * (Newton's method.)
+ */
+#define timelocal(x) invert(x, localtime)
+/*
+ * comparetm compares two tm structures and returns -1 if the first
+ * is less than the second, 0 if they are equal, and +1 if the first
+ * is greater than the second. Only the year, month, day, hour, minute
+ * and second are compared. The yearday (Julian), day of week, and isdst
+ * are not compared.
+ */
+
+static int
+comparetm(
+ struct tm *a,
+ struct tm *b
+ )
+{
+ if (a->tm_year < b->tm_year ) return -1;
+ if (a->tm_year > b->tm_year ) return 1;
+ if (a->tm_mon < b->tm_mon ) return -1;
+ if (a->tm_mon > b->tm_mon ) return 1;
+ if (a->tm_mday < b->tm_mday ) return -1;
+ if (a->tm_mday > b->tm_mday ) return 1;
+ if (a->tm_hour < b->tm_hour ) return -1;
+ if (a->tm_hour > b->tm_hour ) return 1;
+ if (a->tm_min < b->tm_min ) return -1;
+ if (a->tm_min > b->tm_min ) return 1;
+ if (a->tm_sec < b->tm_sec ) return -1;
+ if (a->tm_sec > b->tm_sec ) return 1;
+ return 0;
+}
+
+static long
+invert (
+ struct tm *x,
+ struct tm *(*func)()
+ )
+{
+ struct tm *y;
+ int result;
+ long trial;
+ long lower=0L;
+ long upper=(long)((unsigned long)(~lower) >> 1);
+
+ do {
+ trial = (upper + lower) / 2L;
+ y = (*func)(&trial);
+ result = comparetm(x, y);
+ if (result < 0) upper = trial;
+ if (result > 0) lower = trial;
+ } while (result != 0);
+ return trial;
+}
+#endif /* 0 */
+
+
+/*
+ * heath_start - open the devices and initialize data for processing
+ */
+static int
+heath_start(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, 0)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct heathunit *)
+ emalloc(sizeof(struct heathunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct heathunit));
+ 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);
+ 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->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * heath_shutdown - shut down the clock
+ */
+static void
+heath_shutdown(
+ int unit,
+ struct peer *peer
+ )
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct heathunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * heath_receive - receive data from the serial interface
+ */
+static void
+heath_receive(
+ struct recvbuf *rbufp
+ )
+{
+ register struct heathunit *up;
+ 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;
+ up = (struct heathunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ /*
+ * We get a buffer and timestamp for each <cr>; however, we use
+ * the timestamp captured at the RTS modem control line toggle
+ * on the assumption that's what the radio bases the timecode
+ * on. Apparently, the radio takes about a second to make up its
+ * mind to send a timecode, so the receive timestamp is
+ * worthless.
+ */
+ pp->lastrec = up->tstamp;
+ up->pollcnt = 2;
+#ifdef DEBUG
+ if (debug)
+ printf("heath: 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 < LENHEATH) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "hh:mm:ss.f AM mm/dd/yy"
+ */
+ 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;
+ }
+
+ /*
+ * If AM or PM is received, assume the clock is displaying local
+ * time. First, convert to 24-hour format.
+ */
+
+ switch (a[1]) {
+ case 'P':
+ if (12 > pp->hour)
+ pp->hour += 12;
+ break;
+
+ case 'A':
+ if (12 == pp->hour)
+ pp->hour -= 12;
+ break;
+ }
+
+ /*
+ * Now make a struct tm out of it, convert to UTC, and
+ * repopulate pp->
+ */
+
+ if (' ' != a[1]) {
+ struct tm t, *q;
+ time_t l;
+
+ t.tm_sec = pp->second;
+ t.tm_min = pp->minute;
+ t.tm_hour = pp->hour;
+ t.tm_mday = day; /* not converted to yday yet */
+ t.tm_mon = month-1; /* ditto */
+ t.tm_year = pp->year;
+ if ( t.tm_year < YEAR_PIVOT ) t.tm_year += 100; /* Y2KFixes */
+
+ t.tm_wday = -1; /* who knows? */
+ t.tm_yday = -1; /* who knows? */
+ t.tm_isdst = -1; /* who knows? */
+
+ l = mktime(&t);
+ if (l == -1) {
+ /* HMS: do we want to do this? */
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ q = gmtime(&l);
+
+ pp->year = q->tm_year;
+ month = q->tm_mon+1;
+ day = q->tm_mday; /* still not converted */
+ pp->hour = q->tm_hour;
+ /* pp->minute = q->tm_min; GC-1000 cannot adjust timezone */
+ /* pp->second = q->tm_sec; by other than hour increments */
+ }
+
+
+
+ /*
+ * 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.
+ *
+ * Yes, I know this code incorrectly thinks that 2000 is a leap
+ * year; but, the latest year that can be set by the DIPswitches
+ * is 1997 anyay. Life is short.
+ * Hey! Year 2000 IS a leap year! Y2KFixes
+ */
+ 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->msec = (dsec - '0') * 100;
+ 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
+ )
+{
+ register struct heathunit *up;
+ 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.
+ */
+ pp = peer->procptr;
+ up = (struct heathunit *)pp->unitptr;
+ pp->polls++;
+
+ /*
+ * We toggle the RTS modem control lead 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(&up->tstamp);
+ ioctl(pp->io.fd, TIOCMBIS, (char *)&bits);
+ 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;
+}
+
+#else
+int refclock_heath_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_hpgps.c b/contrib/ntp/ntpd/refclock_hpgps.c
new file mode 100644
index 0000000..30aa494
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_hpgps.c
@@ -0,0 +1,603 @@
+/*
+ * refclock_hpgps - clock driver for HP 58503A GPS receiver
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_HPGPS)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.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;
+ }
+ 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/contrib/ntp/ntpd/refclock_irig.c b/contrib/ntp/ntpd/refclock_irig.c
new file mode 100644
index 0000000..eccbda8
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_irig.c
@@ -0,0 +1,1079 @@
+/*
+ * refclock_irig - audio IRIG-B/E demodulator/decoder
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_IRIG)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <math.h>
+#ifdef HAVE_SYS_AUDIOIO_H
+#include <sys/audioio.h>
+#endif /* HAVE_SYS_AUDIOIO_H */
+#ifdef HAVE_SUN_AUDIOIO_H
+#include <sun/audioio.h>
+#endif /* HAVE_SUN_AUDIOIO_H */
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.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 -d -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 47 20 0.083 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),
+ * field phase (0-79), time constant (2-20), modulation index (0-1),
+ * carrier phase error (0+-0.5) 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, 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.
+ *
+ * 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 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 speaker volume
+ * must be set before the driver is started. Fudge flag4 causes the
+ * debugging output described above to be recorded in the clockstats
+ * file. Any of these flags can be changed during operation with the
+ * ntpdc program.
+ */
+
+/*
+ * Interface definitions
+ */
+#define PRECISION (-17) /* precision assumed (about 10 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "Generic IRIG Audio Driver" /* WRU */
+
+#define AUDIO_BUFSIZ 160 /* codec buffer size (Solaris only) */
+#define SAMPLES 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 DRPOUT 100. /* dropout signal level */
+#define MODMIN 0.5 /* minimum modulation index */
+#define MAXFREQ (250e-6 * SAMPLES) /* freq tolerance (.025%) */
+#define PI 3.1415926535 /* the real thing */
+
+/*
+ * Experimentally determined fudge factors
+ */
+#define IRIG_B .0019 /* IRIG-B phase delay */
+#define IRIG_E .0019 /* IRIG-E phase 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) */
+
+/*
+ * 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 comp[SIZE]; /* decompanding table */
+ double integ[BAUD]; /* baud integrator */
+ double phase, freq; /* logical clock phase and frequency */
+ double zxing; /* phase detector integrator */
+ double yxing; /* phase detector display */
+ double modndx; /* modulation index */
+ double irig_b; /* IRIG-B signal amplitude */
+ double irig_e; /* IRIG-E signal amplitude */
+ int errflg; /* error flags */
+ int bufcnt; /* samples in buffer */
+ int bufptr; /* buffer index pointer */
+ int pollcnt; /* poll counter */
+ int port; /* codec port */
+ int gain; /* codec gain */
+ int clipcnt; /* sample clipped count */
+ int seccnt; /* second interval counter */
+ int decim; /* sample decimation factor */
+
+ /*
+ * 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 xxing; /* phase detector interpolated output */
+ double fdelay; /* filter delay */
+ 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
+ */
+ l_fp montime; /* reference timestamp for eyeball */
+ int timecnt; /* timecode counter */
+ 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 */
+};
+
+/*
+ * 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 *));
+static int irig_audio P((void));
+static void irig_debug P((void));
+
+/*
+ * 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
+ */
+#ifdef HAVE_SYS_AUDIOIO_H
+struct audio_device device; /* audio device ident */
+#endif /* HAVE_SYS_AUDIOIO_H */
+static struct audio_info info; /* audio device info */
+static int irig_ctl_fd; /* audio control file descriptor */
+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 (not used) */
+ 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 = open("/dev/audio", O_RDWR | O_NONBLOCK, 0777);
+ if (fd == -1) {
+ perror("audio");
+ return (0);
+ }
+
+ /*
+ * 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 = (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN) / 2;
+ if (irig_audio() < 0) {
+ io_closeclock(&pp->io);
+ return(0);
+ }
+ up->pollcnt = 2;
+
+ /*
+ * 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. / SAMPLES, &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 */
+ 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.
+ */
+ up->timestamp = rbufp->recv_time;
+ up->bufcnt = rbufp->recv_length;
+ DTOLFP((double)up->bufcnt / SAMPLES, &ltemp);
+ L_SUB(&up->timestamp, &ltemp);
+ dpt = rbufp->recv_buffer;
+ for (up->bufptr = 0; up->bufptr < up->bufcnt; up->bufptr++) {
+ 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. Choose either IRIG-B or IRIG-E
+ * according to the energy at the respective filter
+ * output.
+ */
+ if (sample > MAXSIG) {
+ sample = MAXSIG;
+ up->clipcnt++;
+ } else if (sample < -MAXSIG) {
+ sample = -MAXSIG;
+ up->clipcnt++;
+ }
+
+ /*
+ * Variable frequency oscillator. A phase change of one
+ * unit produces a change of 360 degrees; a frequency
+ * change of one unit produces a change of 1 Hz.
+ */
+ up->phase += up->freq / SAMPLES;
+ 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, codec
+ * port and gain.
+ */
+ up->seccnt = (up->seccnt + 1) % SAMPLES;
+ 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;
+ }
+ if (pp->sloppyclockflag & CLK_FLAG2)
+ up->port = AUDIO_LINE_IN;
+ else
+ up->port = AUDIO_MICROPHONE;
+ irig_gain(peer);
+ up->clipcnt = 0;
+ up->irig_b = up->irig_e = 0;
+ }
+ }
+
+ /*
+ * Squawk to the monitor speaker if enabled.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ if (write(pp->io.fd, (u_char *)&rbufp->recv_space,
+ (u_int)up->bufcnt) < 0)
+ perror("irig:");
+}
+
+/*
+ * 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 lope; /* integrator output */
+ double env; /* envelope detector output */
+ double dtemp; /* double temp */
+ int i; /* index 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) {
+ up->xxing = lope / (up->lastsig - lope);
+ up->zxing += (up->carphase - 4 + up->xxing) / 8.;
+ }
+ 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->intmax > 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;
+ dtemp = BAUD - up->zxing;
+ i = up->envxing - up->envphase;
+ if (i < 0)
+ i -= i;
+ if (i <= 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 {
+ dtemp -= 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.
+ */
+ DTOLFP(up->decim * (dtemp / SAMPLES + 1.) + up->fdelay,
+ &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;
+
+ /*
+ * Local variables
+ */
+ char syncchar; /* sync character (Spectracom only) */
+ 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;
+ up->montime = pp->lastrec;
+ if (up->errflg == 0) {
+ up->timecnt++;
+ refclock_process(pp);
+ }
+ if (up->timecnt >= MAXSTAGE) {
+ refclock_receive(peer);
+ up->timecnt = 0;
+ up->pollcnt = 2;
+ }
+ 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, adjust the
+ * gain and set the input port. Set the port
+ * here on the assumption somebody might even
+ * change it on-wing.
+ */
+ 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->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 %2d %6.3f %6.1f %s",
+ up->errflg, syncchar, pp->year, pp->day,
+ pp->hour, pp->minute, pp->second,
+ up->maxsignal, info.record.gain, up->modndx,
+ up->envxing, up->tc, up->yxing, up->freq *
+ 1e6 / SAMPLES, ulfptoa(&up->montime, 6));
+ pp->lencode = strlen(pp->a_lastcode);
+ if (up->timecnt == 0 || pp->sloppyclockflag &
+ CLK_FLAG4)
+ record_clock_stats(&peer->srcadr,
+ pp->a_lastcode);
+#ifdef DEBUG
+ if (debug > 2)
+ printf("irig: %s\n", pp->a_lastcode);
+#endif /* DEBUG */
+ }
+ }
+ up->lastbit = bit;
+}
+
+
+/*
+ * irig_poll - called by the transmit procedure
+ *
+ * This routine keeps track of status. If nothing is heard for two
+ * successive poll intervals, 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;
+
+ /*
+ * Keep book for tattletales
+ */
+ if (up->pollcnt == 0) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ up->timecnt = 0;
+ return;
+ }
+ up->pollcnt--;
+ 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. Set the new bits in the structure
+ * and call AUDIO_SETINFO. Upon return, the old bits are in the
+ * structure.
+ */
+ if (up->clipcnt == 0) {
+ up->gain += 4;
+ if (up->gain > AUDIO_MAX_GAIN)
+ up->gain = AUDIO_MAX_GAIN;
+ } else if (up->clipcnt > SAMPLES / 100) {
+ up->gain -= 4;
+ if (up->gain < AUDIO_MIN_GAIN)
+ up->gain = AUDIO_MIN_GAIN;
+ }
+ AUDIO_INITINFO(&info);
+ info.record.port = up->port;
+ info.record.gain = up->gain;
+ info.record.error = 0;
+ ioctl(irig_ctl_fd, (int)AUDIO_SETINFO, &info);
+ if (info.record.error)
+ up->errflg |= IRIG_ERR_ERROR;
+}
+
+
+/*
+ * irig_audio - initialize audio device
+ *
+ * This code works with SunOS 4.1.3 and Solaris 2.6; however, it is
+ * believed generic and applicable to other systems with a minor twid
+ * or two. All it does is open the device, set the buffer size (Solaris
+ * only), preset the gain and set the input port. It assumes that the
+ * codec sample rate (8000 Hz), precision (8 bits), number of channels
+ * (1) and encoding (ITU-T G.711 mu-law companded) have been set by
+ * default.
+ */
+static int
+irig_audio(
+ )
+{
+ /*
+ * Open audio control device
+ */
+ if ((irig_ctl_fd = open("/dev/audioctl", O_RDWR)) < 0) {
+ perror("audioctl");
+ return(-1);
+ }
+#ifdef HAVE_SYS_AUDIOIO_H
+ /*
+ * Set audio device parameters.
+ */
+ AUDIO_INITINFO(&info);
+ info.record.buffer_size = AUDIO_BUFSIZ;
+ if (ioctl(irig_ctl_fd, (int)AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ close(irig_ctl_fd);
+ return(-1);
+ }
+#endif /* HAVE_SYS_AUDIOIO_H */
+#ifdef DEBUG
+ irig_debug();
+#endif /* DEBUG */
+ return(0);
+}
+
+
+#ifdef DEBUG
+/*
+ * irig_debug - display audio parameters
+ *
+ * This code doesn't really do anything, except satisfy curiousity and
+ * verify the ioctl's work.
+ */
+static void
+irig_debug(
+ )
+{
+ if (debug == 0)
+ return;
+#ifdef HAVE_SYS_AUDIOIO_H
+ ioctl(irig_ctl_fd, (int)AUDIO_GETDEV, &device);
+ printf("irig: name %s, version %s, config %s\n",
+ device.name, device.version, device.config);
+#endif /* HAVE_SYS_AUDIOIO_H */
+ ioctl(irig_ctl_fd, (int)AUDIO_GETINFO, &info);
+ printf(
+ "irig: samples %d, channels %d, precision %d, encoding %d\n",
+ info.record.sample_rate, info.record.channels,
+ info.record.precision, info.record.encoding);
+#ifdef HAVE_SYS_AUDIOIO_H
+ printf("irig: gain %d, port %d, buffer %d\n",
+ info.record.gain, info.record.port,
+ info.record.buffer_size);
+#else /* HAVE_SYS_AUDIOIO_H */
+ printf("irig: gain %d, port %d\n",
+ info.record.gain, info.record.port);
+#endif /* HAVE_SYS_AUDIOIO_H */
+ printf(
+ "irig: samples %d, eof %d, pause %d, error %d, waiting %d, balance %d\n",
+ info.record.samples, info.record.eof,
+ info.record.pause, info.record.error,
+ info.record.waiting, info.record.balance);
+#ifdef __NetBSD__
+ printf("irig: monitor %d, blocksize %d, hiwat %d, lowat %d, mode %d\n",
+ info.monitor_gain, info.blocksize, info.hiwat, info.lowat, info.mode);
+#else /* __NetBSD__ */
+ printf("irig: monitor %d, muted %d\n",
+ info.monitor_gain, info.output_muted);
+#endif /* __NetBSD__ */
+}
+#endif /* DEBUG */
+
+#else
+int refclock_irig_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_jupiter.c b/contrib/ntp/ntpd/refclock_jupiter.c
new file mode 100644
index 0000000..f22714b
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_jupiter.c
@@ -0,0 +1,1262 @@
+/*
+ * Copyright (c) 1997, 1998
+ * 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(PPS)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+#include "ntp_calendar.h"
+
+#include "jupiter.h"
+
+#include <sys/ppsclock.h>
+
+#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 */
+
+/*
+ * The number of raw samples which we acquire to derive a single estimate.
+ * NSAMPLES ideally should not exceed the default poll interval 64.
+ * NKEEP must be a power of 2 to simplify the averaging process.
+ */
+#define NSAMPLES 64
+#define NKEEP 8
+#define REFCLOCKMAXDISPERSE .25 /* max sample dispersion */
+
+/*
+ * 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 jupiterunit {
+ 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 */
+ u_int gweek; /* current GPS week number */
+ u_int32 lastsweek; /* last seconds into GPS week */
+ u_int32 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_long sloppyclockflag; /* fudge flags */
+ u_int known; /* position known yet? */
+ int coderecv; /* total received samples */
+ int nkeep; /* number of samples to preserve */
+ int rshift; /* number of rshifts for division */
+ l_fp filter[NSAMPLES]; /* offset filter */
+ l_fp lastref; /* last reference timestamp */
+ u_short sbuf[512]; /* local input buffer */
+ int ssize; /* space used in sbuf */
+};
+
+/*
+ * Function prototypes
+ */
+static void jupiter_canmsg P((struct peer *, u_int));
+static u_short jupiter_cksum P((u_short *, u_int));
+#ifdef QSORT_USES_VOID_P
+ int jupiter_cmpl_fp P((const void *, const void *));
+#else
+ int jupiter_cmpl_fp P((const l_fp *, const l_fp *));
+#endif /* not QSORT_USES_VOID_P */
+static void jupiter_config P((struct peer *));
+static void jupiter_debug P((struct peer *, char *, ...))
+ __attribute__ ((format (printf, 2, 3)));
+static char * jupiter_offset P((struct peer *));
+static char * jupiter_parse_t P((struct peer *, u_short *));
+static void jupiter_platform P((struct peer *, u_int));
+static void jupiter_poll P((int, struct peer *));
+static int jupiter_pps P((struct peer *));
+static char * jupiter_process P((struct peer *));
+static int jupiter_recv P((struct peer *));
+static void jupiter_receive P((register struct recvbuf *rbufp));
+static void jupiter_reqmsg P((struct peer *, u_int, u_int));
+static void jupiter_reqonemsg P((struct peer *, u_int));
+static char * jupiter_send P((struct peer *, struct jheader *));
+static void jupiter_shutdown P((int, struct peer *));
+static int jupiter_start P((int, struct peer *));
+static int jupiter_ttyinit P((struct peer *, int));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_jupiter = {
+ jupiter_start, /* start up driver */
+ jupiter_shutdown, /* shut down driver */
+ jupiter_poll, /* transmit poll message */
+ noentry, /* (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(
+ register int unit,
+ register struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ register struct jupiterunit *up;
+ register int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ fd = open(gpsdev, O_RDWR
+#ifdef O_NONBLOCK
+ | O_NONBLOCK
+#endif
+ , 0);
+ if (fd < 0) {
+ jupiter_debug(peer, "jupiter_start: open %s: %s\n",
+ gpsdev, strerror(errno));
+ return (0);
+ }
+ if (!jupiter_ttyinit(peer, fd))
+ return (0);
+
+ /* Allocate unit structure */
+ if ((up = (struct jupiterunit *)
+ emalloc(sizeof(struct jupiterunit))) == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct jupiterunit));
+ 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(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 */
+ jupiter_config(peer);
+
+ /* Turn on pulse gathering by requesting the first sample */
+ if (ioctl(fd, CIOGETEV, (caddr_t)&up->ppsev) < 0) {
+ jupiter_debug(peer, "jupiter_ttyinit: CIOGETEV: %s\n",
+ strerror(errno));
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ up->lastserial = up->ppsev.serial;
+ memset(&up->ppsev, 0, sizeof(up->ppsev));
+ return (1);
+}
+
+/*
+ * jupiter_shutdown - shut down the clock
+ */
+static void
+jupiter_shutdown(register int unit, register struct peer *peer)
+{
+ register struct jupiterunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct jupiterunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+/*
+ * jupiter_config - Configure the receiver
+ */
+static void
+jupiter_config(register struct peer *peer)
+{
+ register int i;
+ register struct jupiterunit *up;
+ register struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct jupiterunit *)pp->unitptr;
+
+ /*
+ * Initialize the unit variables
+ *
+ * STRANGE BEHAVIOUR WARNING: The fudge flags are not available
+ * at the time jupiter_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, "jupiter_config: mobile platform");
+ } else {
+ up->moving = 0; /* Static Installation */
+ }
+
+ /* XXX fludge flags don't make the trip from the config to here... */
+#ifdef notdef
+ /* Configure for trailing edge triggers */
+#ifdef CIOSETTET
+ i = ((pp->sloppyclockflag & CLK_FLAG3) != 0);
+ jupiter_debug(peer, "jupiter_configure: (sloppyclockflag 0x%lx)\n",
+ pp->sloppyclockflag);
+ if (ioctl(pp->io.fd, CIOSETTET, (char *)&i) < 0)
+ msyslog(LOG_DEBUG, "jupiter_configure: CIOSETTET %d: %m", i);
+#else
+ if (pp->sloppyclockflag & CLK_FLAG3)
+ msyslog(LOG_DEBUG, "jupiter_configure: \
+No kernel support for trailing edge trigger");
+#endif
+#endif
+
+ up->pollcnt = 2;
+ up->polled = 0;
+ up->known = 0;
+ up->gweek = 0;
+ up->lastsweek = 2 * WEEKSECS;
+ up->timecode = 0;
+ up->stime = 0;
+ up->ssize = 0;
+ up->coderecv = 0;
+ up->nkeep = NKEEP;
+ if (up->nkeep > NSAMPLES)
+ up->nkeep = NSAMPLES;
+ if (up->nkeep >= 1)
+ up->rshift = 0;
+ if (up->nkeep >= 2)
+ up->rshift = 1;
+ if (up->nkeep >= 4)
+ up->rshift = 2;
+ if (up->nkeep >= 8)
+ up->rshift = 3;
+ if (up->nkeep >= 16)
+ up->rshift = 4;
+ if (up->nkeep >= 32)
+ up->rshift = 5;
+ if (up->nkeep >= 64)
+ up->rshift = 6;
+ up->nkeep = 1;
+ i = up->rshift;
+ while (i > 0) {
+ up->nkeep *= 2;
+ i--;
+ }
+
+ /* Stop outputting all messages */
+ jupiter_canmsg(peer, JUPITER_ALL);
+
+ /* Request the receiver id so we can syslog the firmware version */
+ jupiter_reqonemsg(peer, JUPITER_O_ID);
+
+ /* Flag that this the id was requested (so we don't get called again) */
+ up->wantid = 1;
+
+ /* Request perodic time mark pulse messages */
+ jupiter_reqmsg(peer, JUPITER_O_PULSE, 1);
+
+ /* Set application platform type */
+ if (up->moving)
+ jupiter_platform(peer, JUPITER_I_PLAT_MED);
+ else
+ jupiter_platform(peer, JUPITER_I_PLAT_LOW);
+}
+
+/*
+ * jupiter_poll - jupiter watchdog routine
+ */
+static void
+jupiter_poll(register int unit, register struct peer *peer)
+{
+ register struct jupiterunit *up;
+ register struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct jupiterunit *)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 the receiver id to trigger a reconfig */
+ jupiter_reqonemsg(peer, JUPITER_O_ID);
+ up->wantid = 0;
+ }
+
+ /*
+ * polled every 64 seconds. Ask jupiter_receive to hand in
+ * a timestamp.
+ */
+ up->polled = 1;
+ pp->polls++;
+}
+
+/*
+ * jupiter_receive - receive gps data
+ * Gag me!
+ */
+static void
+jupiter_receive(register struct recvbuf *rbufp)
+{
+ register int bpcnt, cc, size, ppsret;
+ register u_int32 last_timecode, laststime;
+ register char *cp;
+ register u_char *bp;
+ register u_short *sp;
+ register u_long sloppyclockflag;
+ register struct jupiterunit *up;
+ register struct jid *ip;
+ register struct jheader *hp;
+ register struct refclockproc *pp;
+ register struct peer *peer;
+
+ /* Initialize pointers and read the timecode and timestamp */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct jupiterunit *)pp->unitptr;
+
+ /*
+ * If operating mode has been changed, then reinitialize the receiver
+ * before doing anything else.
+ */
+/* XXX Sloppy clock flags are broken!! */
+ sloppyclockflag = up->sloppyclockflag;
+ up->sloppyclockflag = pp->sloppyclockflag;
+ if ((pp->sloppyclockflag & CLK_FLAG2) !=
+ (sloppyclockflag & CLK_FLAG2)) {
+ jupiter_debug(peer,
+ "jupiter_receive: mode switch: reset receiver\n");
+ jupiter_config(peer);
+ return;
+ }
+
+ up->pollcnt = 2;
+
+ bp = (u_char *)rbufp->recv_buffer;
+ bpcnt = rbufp->recv_length;
+
+ /* This shouldn't happen */
+ if (bpcnt > sizeof(up->sbuf) - up->ssize)
+ bpcnt = sizeof(up->sbuf) - up->ssize;
+
+ /* Append to input buffer */
+ memcpy((u_char *)up->sbuf + up->ssize, bp, bpcnt);
+ up->ssize += bpcnt;
+
+ /* While there's at least a header and we parse a intact message */
+ while (up->ssize > sizeof(*hp) && (cc = jupiter_recv(peer)) > 0) {
+ hp = (struct jheader *)up->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\n",
+ 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 = up->stime;
+ up->stime = DS2UI(((struct jpulse *)sp)->stime);
+ if (laststime != 0 && up->stime - laststime <= 21) {
+ jupiter_debug(peer, "jupiter_receive: \
+avoided firmware bug (stime %.2f, laststime %.2f)\n",
+ (double)up->stime * 0.01, (double)laststime * 0.01);
+ break;
+ }
+
+ /* Retrieve pps timestamp */
+ ppsret = jupiter_pps(peer);
+
+ /* Parse timecode (even when there's no pps) */
+ last_timecode = up->timecode;
+ if ((cp = jupiter_parse_t(peer, sp)) != NULL) {
+ jupiter_debug(peer,
+ "jupiter_receive: pulse: %s\n", 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 */
+ if ((cp = jupiter_offset(peer)) != NULL) {
+ jupiter_debug(peer,
+ "jupiter_receive: offset: %s\n", cp);
+ refclock_report(peer, CEVNT_BADTIME);
+ break;
+ }
+
+ /*
+ * 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)
+ break;
+
+ /*
+ * It's a live one! Remember this time.
+ */
+ pp->lasttime = current_time;
+
+ /*
+ * Determine the reference clock offset and
+ * dispersion. NKEEP of NSAMPLE offsets are
+ * passed through a median filter.
+ * Save the (filtered) offset and dispersion in
+ * pp->offset and pp->disp.
+ */
+ if ((cp = jupiter_process(peer)) != NULL) {
+ jupiter_debug(peer,
+ "jupiter_receive: process: %s\n", cp);
+ refclock_report(peer, CEVNT_BADTIME);
+ break;
+ }
+ /*
+ * 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.
+ */
+ jupiter_debug(peer,
+ "jupiter_receive: process time: \
+%4d-%03d %02d:%02d:%02d at %s, %s\n",
+ pp->year, pp->day,
+ pp->hour, pp->minute, pp->second,
+ prettydate(&pp->lastrec), lfptoa(&pp->offset, 6));
+
+ refclock_receive(peer);
+
+ /*
+ * We have succeeded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+ break;
+
+ case JUPITER_O_ID:
+ if (size != sizeof(struct jid)) {
+ jupiter_debug(peer,
+ "jupiter_receive: id: len %d != %u\n",
+ size, (int)sizeof(struct jid));
+ refclock_report(peer, CEVNT_BADREPLY);
+ break;
+ }
+ /*
+ * If we got this message because the Jupiter
+ * just powered up, it needs to be reconfigured.
+ */
+ ip = (struct jid *)sp;
+ jupiter_debug(peer,
+ "jupiter_receive: >> %s chan ver %s, %s (%s)\n",
+ 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 (up->wantid)
+ up->wantid = 0;
+ else {
+ jupiter_debug(peer,
+ "jupiter_receive: reset receiver\n");
+ jupiter_config(peer);
+ /* Rese since jupiter_config() just zeroed it */
+ up->ssize = cc;
+ }
+ break;
+
+ default:
+ jupiter_debug(peer,
+ "jupiter_receive: >> unknown message id %d\n",
+ getshort(hp->id));
+ break;
+ }
+ up->ssize -= cc;
+ if (up->ssize < 0) {
+ fprintf(stderr, "jupiter_recv: negative ssize!\n");
+ abort();
+ } else if (up->ssize > 0)
+ memcpy(up->sbuf, (u_char *)up->sbuf + cc, up->ssize);
+ }
+ record_clock_stats(&peer->srcadr, "<timecode is binary>");
+}
+
+/*
+ * jupiter_offset - Calculate the offset, and add to the rolling filter.
+ */
+static char *
+jupiter_offset(register struct peer *peer)
+{
+ register struct jupiterunit *up;
+ register struct refclockproc *pp;
+ register int i;
+ l_fp offset;
+
+ pp = peer->procptr;
+ up = (struct jupiterunit *)pp->unitptr;
+
+ /*
+ * Calculate the offset
+ */
+ if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT,
+ pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui)) {
+ return ("jupiter_process: clocktime failed");
+ }
+ if (pp->usec) {
+ TVUTOTSF(pp->usec, offset.l_uf);
+ } else {
+ MSUTOTSF(pp->msec, offset.l_uf);
+ }
+ L_ADD(&offset, &pp->fudgetime1);
+ up->lastref = offset; /* save last reference time */
+ L_SUB(&offset, &pp->lastrec); /* form true offset */
+
+ /*
+ * A rolling filter. Initialize first time around.
+ */
+ i = ((up->coderecv)) % NSAMPLES;
+
+ up->filter[i] = offset;
+ if (up->coderecv == 0)
+ for (i = 1; (u_int) i < NSAMPLES; i++)
+ up->filter[i] = up->filter[0];
+ up->coderecv++;
+
+ return (NULL);
+}
+
+/*
+ * jupiter_process - process the sample from the clock,
+ * passing it through a median filter and optionally averaging
+ * the samples. Returns offset and dispersion in "up" structure.
+ */
+static char *
+jupiter_process(register struct peer *peer)
+{
+ register struct jupiterunit *up;
+ register struct refclockproc *pp;
+ register int i, n;
+ register int j, k;
+ l_fp offset, median, lftmp;
+ u_fp disp;
+ l_fp off[NSAMPLES];
+
+ pp = peer->procptr;
+ up = (struct jupiterunit *)pp->unitptr;
+
+ /*
+ * Copy the raw offsets and sort into ascending order
+ */
+ for (i = 0; i < NSAMPLES; i++)
+ off[i] = up->filter[i];
+ qsort((char *)off, NSAMPLES, sizeof(l_fp), jupiter_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median of NSAMPLES samples until
+ * NKEEP samples remain.
+ */
+ i = 0;
+ n = NSAMPLES;
+ while ((n - i) > up->nkeep) {
+ lftmp = off[n - 1];
+ median = off[(n + i) / 2];
+ L_SUB(&lftmp, &median);
+ L_SUB(&median, &off[i]);
+ if (L_ISHIS(&median, &lftmp)) {
+ /* reject low end */
+ i++;
+ } else {
+ /* reject high end */
+ n--;
+ }
+ }
+
+ /*
+ * Copy key values to the billboard to measure performance.
+ */
+ pp->lastref = up->lastref;
+ pp->coderecv = up->coderecv;
+ pp->filter[0] = off[0]; /* smallest offset */
+ pp->filter[1] = off[NSAMPLES-1]; /* largest offset */
+ for (j = 2, k = i; k < n; j++, k++)
+ pp->filter[j] = off[k]; /* offsets actually examined */
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets. Add to this the time since
+ * the last clock update, which represents the dispersion
+ * increase with time. We know that NTP_MAXSKEW is 16. If the
+ * sum is greater than the allowed sample dispersion, bail out.
+ * If the loop is unlocked, return the most recent offset;
+ * otherwise, return the median offset.
+ */
+ lftmp = off[n - 1];
+ L_SUB(&lftmp, &off[i]);
+ disp = LFPTOFP(&lftmp);
+ if (disp > REFCLOCKMAXDISPERSE)
+ return ("Maximum dispersion exceeded");
+
+ /*
+ * Now compute the offset estimate. If fudge flag 1
+ * is set, average the remainder, otherwise pick the
+ * median.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG1) {
+ L_CLR(&lftmp);
+ while (i < n) {
+ L_ADD(&lftmp, &off[i]);
+ i++;
+ }
+ i = up->rshift;
+ while (i > 0) {
+ L_RSHIFT(&lftmp);
+ i--;
+ }
+ offset = lftmp;
+ } else {
+ i = (n + i) / 2;
+ offset = off[i];
+ }
+
+ /*
+ * The payload: filtered offset and dispersion.
+ */
+
+ pp->offset = offset;
+ pp->disp = disp;
+
+ return (NULL);
+
+}
+
+/* Compare two l_fp's, used with qsort() */
+int
+#ifdef QSORT_USES_VOID_P
+jupiter_cmpl_fp(register const void *p1, register const void *p2)
+#else
+jupiter_cmpl_fp(register const l_fp *fp1, register const l_fp *fp2)
+#endif
+{
+#ifdef QSORT_USES_VOID_P
+ register const l_fp *fp1 = (const l_fp *)p1;
+ register const l_fp *fp2 = (const l_fp *)p2;
+#endif
+
+ if (!L_ISGEQ(fp1, fp2))
+ return (-1);
+ if (L_ISEQU(fp1, fp2))
+ return (0);
+ return (1);
+}
+
+static char *
+jupiter_parse_t(register struct peer *peer, register u_short *sp)
+{
+ register struct refclockproc *pp;
+ register struct jupiterunit *up;
+ register struct tm *tm;
+ register char *cp;
+ register struct jpulse *jp;
+ register struct calendar *jt;
+ register u_int32 sweek;
+ register u_int32 last_timecode;
+ register u_short flags;
+ time_t t;
+ struct calendar cal;
+
+ pp = peer->procptr;
+ up = (struct jupiterunit *)pp->unitptr;
+ jp = (struct jpulse *)sp;
+
+ /* The timecode is presented as seconds into the current GPS week */
+ sweek = DS2UI(jp->sweek);
+
+ /*
+ * 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 (up->gweek == 0)
+ up->gweek = (time(NULL) - GPS_EPOCH) / WEEKSECS;
+ else if (sweek == 0 && up->lastsweek == WEEKSECS - 1) {
+ ++up->gweek;
+ jupiter_debug(peer,
+ "jupiter_parse_t: NEW gps week %u\n", up->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 (up->lastsweek == sweek)
+ jupiter_debug(peer,
+ "jupiter_parse_t: gps sweek not incrementing (%d)\n",
+ sweek);
+ else if (up->lastsweek != 2 * WEEKSECS &&
+ up->lastsweek + 1 != sweek &&
+ !(sweek == 0 && up->lastsweek == WEEKSECS - 1))
+ jupiter_debug(peer,
+ "jupiter_parse_t: gps sweek jumped (was %d, now %d)\n",
+ up->lastsweek, sweek);
+ up->lastsweek = sweek;
+
+ /* This timecode describes next pulse */
+ last_timecode = up->timecode;
+ up->timecode = (u_int32)JAN_1970 +
+ GPS_EPOCH + (up->gweek * WEEKSECS) + sweek;
+
+ if (last_timecode == 0)
+ /* XXX debugging */
+ jupiter_debug(peer,
+ "jupiter_parse_t: UTC <none> (gweek/sweek %u/%u)\n",
+ up->gweek, sweek);
+ else {
+ /* XXX debugging */
+ t = last_timecode - (u_int32)JAN_1970;
+ tm = gmtime(&t);
+ cp = asctime(tm);
+
+ jupiter_debug(peer,
+ "jupiter_parse_t: UTC %.24s (gweek/sweek %u/%u)\n",
+ cp, up->gweek, sweek);
+
+ /* Billboard last_timecode (which is now the current time) */
+ jt = &cal;
+ caljulian(last_timecode, jt);
+ pp = peer->procptr;
+ pp->year = jt->year;
+ pp->day = jt->yearday;
+ pp->hour = jt->hour;
+ pp->minute = jt->minute;
+ pp->second = jt->second;
+ pp->msec = 0;
+ pp->usec = 0;
+ }
+
+ /* XXX debugging */
+ tm = gmtime(&up->ppsev.tv.tv_sec);
+ cp = asctime(tm);
+ flags = getshort(jp->flags);
+ jupiter_debug(peer,
+ "jupiter_parse_t: PPS %.19s.%06lu %.4s (serial %u)%s\n",
+ cp, up->ppsev.tv.tv_usec, cp + 20, up->ppsev.serial,
+ (flags & JUPITER_O_PULSE_VALID) == 0 ?
+ " NOT VALID" : "");
+
+ /* Toss if not designated "valid" by the gps */
+ if ((flags & JUPITER_O_PULSE_VALID) == 0) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return ("time mark not valid");
+ }
+
+ /* We better be sync'ed to UTC... */
+ if ((flags & JUPITER_O_PULSE_UTC) == 0) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return ("time mark not sync'ed to UTC");
+ }
+
+ return (NULL);
+}
+
+/*
+ * Process a PPS signal, returning a timestamp.
+ */
+static int
+jupiter_pps(register struct peer *peer)
+{
+ register struct refclockproc *pp;
+ register struct jupiterunit *up;
+ register int firsttime;
+ struct timeval ntp_tv;
+
+ pp = peer->procptr;
+ up = (struct jupiterunit *)pp->unitptr;
+
+ /*
+ * Grab the timestamp of the PPS signal.
+ */
+ firsttime = (up->ppsev.tv.tv_sec == 0);
+ if (ioctl(pp->io.fd, CIOGETEV, (caddr_t)&up->ppsev) < 0) {
+ /* XXX Actually, if this fails, we're pretty much screwed */
+ jupiter_debug(peer, "jupiter_pps: CIOGETEV: %s\n",
+ strerror(errno));
+ refclock_report(peer, CEVNT_FAULT);
+ return (1);
+ }
+
+ /*
+ * Check pps serial number against last one
+ */
+ if (!firsttime && up->lastserial + 1 != up->ppsev.serial) {
+ if (up->ppsev.serial == up->lastserial)
+ jupiter_debug(peer, "jupiter_pps: no new pps event\n");
+ else
+ jupiter_debug(peer,
+ "jupiter_pps: missed %d pps events\n",
+ up->ppsev.serial - up->lastserial - 1);
+ up->lastserial = up->ppsev.serial;
+ refclock_report(peer, CEVNT_FAULT);
+ return (1);
+ }
+ up->lastserial = up->ppsev.serial;
+
+ /*
+ * Return the timestamp in pp->lastrec
+ */
+ ntp_tv = up->ppsev.tv;
+ ntp_tv.tv_sec += (u_int32)JAN_1970;
+ TVTOTS(&ntp_tv, &pp->lastrec);
+
+ return (0);
+}
+
+/*
+ * jupiter_debug - print debug messages
+ */
+#if __STDC__
+static void
+jupiter_debug(struct peer *peer, char *fmt, ...)
+#else
+static void
+jupiter_debug(peer, fmt, va_alist)
+ struct peer *peer;
+ char *fmt;
+#endif
+{
+ va_list ap;
+
+ if (debug) {
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ /*
+ * Print debug message to stdout
+ * In the future, we may want to get get more creative...
+ */
+ vfprintf(stderr, fmt, ap);
+
+ va_end(ap);
+ }
+}
+
+/* Checksum and transmit a message to the Jupiter */
+static char *
+jupiter_send(register struct peer *peer, register struct jheader *hp)
+{
+ register u_int len, size;
+ register int cc;
+ register 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(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, 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(register struct peer *peer, register u_int id,
+ register u_int interval)
+{
+ register struct jheader *hp;
+ register struct jrequest *rp;
+ register 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(peer, hp)) != NULL)
+ jupiter_debug(peer, "jupiter_reqmsg: %u: %s\n", id, cp);
+}
+
+/* Cancel periodic message output */
+static struct jheader canmsg = {
+ putshort(JUPITER_SYNC), 0, 0, 0,
+ putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC),
+ 0
+};
+
+static void
+jupiter_canmsg(register struct peer *peer, register u_int id)
+{
+ register struct jheader *hp;
+ register char *cp;
+
+ hp = &canmsg;
+ hp->id = putshort(id);
+ if ((cp = jupiter_send(peer, hp)) != NULL)
+ jupiter_debug(peer, "jupiter_canmsg: %u: %s\n", id, cp);
+}
+
+/* Request a single message output */
+static struct jheader reqonemsg = {
+ putshort(JUPITER_SYNC), 0, 0, 0,
+ putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY),
+ 0
+};
+
+static void
+jupiter_reqonemsg(register struct peer *peer, register u_int id)
+{
+ register struct jheader *hp;
+ register char *cp;
+
+ hp = &reqonemsg;
+ hp->id = putshort(id);
+ if ((cp = jupiter_send(peer, hp)) != NULL)
+ jupiter_debug(peer, "jupiter_reqonemsg: %u: %s\n", 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,
+ putshort(JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK), 0 },
+ { 0, 0, 0 }
+};
+
+static void
+jupiter_platform(register struct peer *peer, register u_int platform)
+{
+ register struct jheader *hp;
+ register struct jplat *pp;
+ register char *cp;
+
+ hp = &platmsg.jheader;
+ pp = &platmsg.jplat;
+ pp->platform = putshort(platform);
+ if ((cp = jupiter_send(peer, hp)) != NULL)
+ jupiter_debug(peer, "jupiter_platform: %u: %s\n", platform, cp);
+}
+
+/* Checksum "len" shorts */
+static u_short
+jupiter_cksum(register u_short *sp, register u_int len)
+{
+ register 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(register struct peer *peer)
+{
+ register int n, len, size, cc;
+ register struct refclockproc *pp;
+ register struct jupiterunit *up;
+ register struct jheader *hp;
+ register u_char *bp;
+ register u_short *sp;
+
+ pp = peer->procptr;
+ up = (struct jupiterunit *)pp->unitptr;
+
+ /* Must have at least a header's worth */
+ cc = sizeof(*hp);
+ size = up->ssize;
+ if (size < cc)
+ return (0);
+
+ /* Search for the sync short if missing */
+ sp = up->sbuf;
+ hp = (struct jheader *)sp;
+ if (getshort(hp->sync) != JUPITER_SYNC) {
+ /* Wasn't at the front, sync up */
+ jupiter_debug(peer, "syncing");
+ bp = (u_char *)sp;
+ n = size;
+ while (n >= 2) {
+ if (bp[0] != (JUPITER_SYNC & 0xff)) {
+ jupiter_debug(peer, "{0x%x}", bp[0]);
+ ++bp;
+ --n;
+ continue;
+ }
+ if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff))
+ break;
+ jupiter_debug(peer, "{0x%x 0x%x}", bp[0], bp[1]);
+ bp += 2;
+ n -= 2;
+ }
+ jupiter_debug(peer, "\n");
+ /* Shuffle data to front of input buffer */
+ if (n > 0)
+ memcpy(sp, bp, n);
+ size = n;
+ up->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(peer, "jupiter_recv: bad header checksum!\n");
+ /* This is drastic but checksum errors should be rare */
+ up->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(peer,
+ "jupiter_recv: bad payload checksum!\n");
+ /* This is drastic but checksum errors should be rare */
+ up->ssize = 0;
+ return (0);
+ }
+ cc += n;
+ }
+ return (cc);
+}
+
+static int
+jupiter_ttyinit(register struct peer *peer, register int fd)
+{
+ struct termios termios;
+
+ memset((char *)&termios, 0, sizeof(termios));
+ if (cfsetispeed(&termios, B9600) < 0 ||
+ cfsetospeed(&termios, B9600) < 0) {
+ jupiter_debug(peer,
+ "jupiter_ttyinit: cfsetispeed/cfgetospeed: %s\n",
+ strerror(errno));
+ return (0);
+ }
+#ifdef HAVE_CFMAKERAW
+ cfmakeraw(&termios);
+#else
+ termios.c_iflag &= ~(IMAXBEL | IXOFF | INPCK | BRKINT | PARMRK |
+ ISTRIP | INLCR | IGNCR | ICRNL | IXON | IGNPAR);
+ termios.c_iflag |= IGNBRK;
+ termios.c_oflag &= ~OPOST;
+ termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | ISIG |
+ IEXTEN | NOFLSH | TOSTOP | PENDIN);
+ termios.c_cflag &= ~(CSIZE | PARENB);
+ termios.c_cflag |= CS8 | CREAD;
+ termios.c_cc[VMIN] = 1;
+#endif
+ termios.c_cflag |= CLOCAL;
+ if (tcsetattr(fd, TCSANOW, &termios) < 0) {
+ jupiter_debug(peer, "jupiter_ttyinit: tcsetattr: %s\n",
+ strerror(errno));
+ return (0);
+ }
+
+#ifdef TIOCSPPS
+ if (ioctl(fd, TIOCSPPS, (char *)&fdpps) < 0) {
+ jupiter_debug(peer, "jupiter_ttyinit: TIOCSPPS: %s\n",
+ strerror(errno));
+ return (0);
+ }
+#endif
+#ifdef I_PUSH
+ if (ioctl(fd, I_PUSH, "ppsclock") < 0) {
+ jupiter_debug(peer, "jupiter_ttyinit: push ppsclock: %s\n",
+ strerror(errno));
+ return (0);
+ }
+#endif
+
+ return (1);
+}
+
+#else /* not (REFCLOCK && CLOCK_JUPITER && PPS) */
+int refclock_jupiter_bs;
+#endif /* not (REFCLOCK && CLOCK_JUPITER && PPS) */
diff --git a/contrib/ntp/ntpd/refclock_leitch.c b/contrib/ntp/ntpd/refclock_leitch.c
new file mode 100644
index 0000000..edee3f8
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_leitch.c
@@ -0,0 +1,619 @@
+/*
+ * 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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.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;
+ }
+#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/contrib/ntp/ntpd/refclock_local.c b/contrib/ntp/ntpd/refclock_local.c
new file mode 100644
index 0000000..12184db
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_local.c
@@ -0,0 +1,258 @@
+/* wjm 17-aug-1995: add a hook for special treatment of VMS_LOCALUNIT */
+
+/*
+ * refclock_local - local pseudo-clock driver
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef REFCLOCK
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.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 3 or 4) 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 3 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 */
+#define REFID "LCL\0" /* reference ID */
+#define DESCRIPTION "Undisciplined local clock" /* WRU */
+
+#define STRATUM 3 /* 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;
+ peer->stratum = STRATUM;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+#if defined(VMS) && defined(VMS_LOCALUNIT)
+ /* provide a non-standard REFID */
+ if(unit == VMS_LOCALUNIT)
+ memcpy((char *)&pp->refid,"LCLv",4);
+#endif /* VMS && VMS_LOCALUNIT */
+ poll_time = current_time;
+ return (1);
+}
+
+
+/*
+ * local_poll - called by the transmit procedure
+ */
+static void
+local_poll(
+ int unit,
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+#if defined(KERNEL_PLL) && defined(STA_CLK)
+ struct timex ntv;
+ int retval;
+#endif /* KERNEL_PLL STA_CLK */
+
+#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);
+ pp->leap = LEAP_NOWARNING;
+ pp->disp = DISPERSION;
+ pp->variance = 0;
+#if defined(KERNEL_PLL) && defined(STA_CLK)
+
+ /*
+ * If the kernel pll code is up and running, somebody else
+ * may come diddle the clock. If so, they better use ntp_adjtime(),
+ * and set the STA_CLK bit in the status word. In this case, the
+ * performance information is read from the kernel and becomes the
+ * variables presented to the clock mitigation process.
+ */
+ if (pll_control && kern_enable && (peer->flags & FLAG_PREFER)) {
+ memset((char *)&ntv, 0, sizeof ntv);
+ retval = ntp_adjtime(&ntv);
+ if (ntv.status & STA_CLK) {
+ ext_enable = 1;
+ switch(retval) {
+
+ case TIME_OK:
+ pp->leap = LEAP_NOWARNING;
+ break;
+
+ case TIME_INS:
+ pp->leap = LEAP_ADDSECOND;
+ break;
+
+ case TIME_DEL:
+ pp->leap = LEAP_DELSECOND;
+ break;
+
+ case TIME_ERROR:
+ pp->leap = LEAP_NOTINSYNC;
+ }
+ pp->disp = ntv.maxerror / 1e6;
+ pp->variance = SQUARE(ntv.esterror / 1e6);
+ }
+ } else {
+ ext_enable = 0;
+ }
+#endif /* KERNEL_PLL STA_CLK */
+ refclock_receive(peer);
+ pp->fudgetime1 = 0;
+}
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_msfees.c b/contrib/ntp/ntpd/refclock_msfees.c
new file mode 100644
index 0000000..01fe27f
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_msfees.c
@@ -0,0 +1,1444 @@
+/* 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 <ctype.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.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 loose 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 }; */
+
+#ifdef DEBUG
+static int debug;
+#endif
+
+#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 int offcompare P((l_fp *a, l_fp *b));
+static void ees_process P((struct eesunit *ees));
+
+/*
+ * 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: %x == %x: 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 */
+
+static int
+offcompare(
+ l_fp *a,
+ l_fp *b
+ )
+{
+ return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
+}
+
+
+/* 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, (u_int)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 & (1 << 31)) !=
+ (((long) offset.l_uf) & ( 1 << 31)))
+ { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
+ msyslog(LOG_INFO, "I: %lx != %lx (%lx %lx), so add %d",
+ new & (1 << 31),
+ ((long) offset.l_uf) & ( 1 << 31),
+ 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/contrib/ntp/ntpd/refclock_mx4200.c b/contrib/ntp/ntpd/refclock_mx4200.c
new file mode 100644
index 0000000..28f563f
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_mx4200.c
@@ -0,0 +1,1774 @@
+/*
+ * 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(PPS)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#include "mx4200.h"
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.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_TIMESPEC
+ struct timespec tv;
+# else
+ struct timeval tv;
+# endif
+ u_int serial;
+};
+#endif /* ! HAVE_STRUCT_PPSCLOCKEV */
+
+/*
+ * 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_RIGHT_NOW 1998
+
+/*
+ * 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.
+ * Reference: Dr. Thomas A. Clark's Totally Accurate Clock (TAC) files at
+ * ftp://aleph.gsfc.nasa.gov/GPS/totally.accurate.clock/
+ * For a 6-channel Motorola Oncore, he indicates that good nominal
+ * HDOP and VDOP are 1.50 and 2.00 respectively. Given the relationship
+ * HDOP^2 = NDOP^2 + EDOP^2 and assuming EDOP and NDOP are equal, we
+ * have a nominal NDOP = EDOP = sqrt((HDOP*HDOP)/2). An 8-channel
+ * Oncore does well with HDOP=1.20 and VDOP=1.70.
+ */
+#define INTERVAL 1 /* Interval between position measurements (s) */
+#define AVGING_TIME 24 /* Number of hours to average */
+#define USUAL_EDOP 1.06066 /* used for normalizing EDOP */
+#define USUAL_NDOP 1.06066 /* used for normalizing NDOP */
+#define USUAL_VDOP 2.00 /* used for normalizing VDOP */
+#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 filt_lat; /* latitude filter length */
+ double filt_lon; /* longitude filter length */
+ double filt_alt; /* height filter length */
+ double edop; /* EDOP (east DOP) */
+ double ndop; /* NDOP (north DOP) */
+ double vdop; /* VDOP (vertical DOP) */
+ 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 */
+};
+
+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
+#endif
+#else
+#ifndef __attribute__
+#define __attribute__(args)
+#endif
+#endif
+/* 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_d 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 void 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];
+
+#ifdef HAVE_TIOCGPPSEV
+#ifdef HAVE_TERMIOS
+ struct termios ttyb;
+#endif /* HAVE_TERMIOS */
+#ifdef HAVE_SYSV_TTYS
+ struct termio ttyb;
+#endif /* HAVE_SYSV_TTYS */
+#ifdef HAVE_BSD_TTYS
+ struct sgttyb ttyb;
+#endif /* HAVE_BSD_TTYS */
+#endif /* HAVE_TIOCGPPSEV */
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_PPS))) {
+ return (0);
+ }
+#ifdef HAVE_TIOCGPPSEV
+ if (fdpps > 0) {
+ /*
+ * Truly nasty hack in order to get this to work on Solaris 7.
+ * Really, refclock_open() should set the port properly, but
+ * it doesn't work (as of ntp-4.0.98a) - almost 99% dropped
+ * PPS signals with "Interrupted system call". Even this
+ * still gives a 5% error rate.
+ */
+ ttyb.c_iflag = IGNCR;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = CS8 | CREAD | CLOCAL;
+ ttyb.c_lflag = ICANON;
+ if (tcsetattr(fdpps, TCSAFLUSH, &ttyb) < 0) {
+ return (0);
+ }
+ }
+#endif /* HAVE_TIOCGPPSEV */
+
+ /*
+ * Allocate unit structure
+ */
+ if (!(up = (struct mx4200unit *) emalloc(sizeof(struct mx4200unit)))) {
+ (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 */
+ mx4200_config(peer);
+ return (1);
+}
+
+
+/*
+ * 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 void
+mx4200_config(
+ struct peer *peer
+ )
+{
+ char tr_mode;
+ int add_mode;
+ register struct mx4200unit *up;
+ struct refclockproc *pp;
+
+ 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->filt_lat = 0.0;
+ up->filt_lon = 0.0;
+ up->filt_alt = 0.0;
+ up->edop = USUAL_EDOP;
+ up->ndop = USUAL_NDOP;
+ up->vdop = USUAL_VDOP;
+ up->last_leap = 0; /* LEAP_NOWARNING */
+ up->clamp_time = current_time + (AVGING_TIME * 60 * 60);
+ up->log_time = current_time + SLEEPTIME;
+
+ /*
+ * "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 "022" DOPs
+ */
+ mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ PMVXG_D_DOPS, /* 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 */
+
+
+ /*
+ * "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 */
+}
+
+/*
+ * 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 "022" DOPs
+ */
+ mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg,
+ PMVXG_S_PORTCONF,
+ PMVXG_D_DOPS, /* 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 */
+
+ /*
+ * "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,", 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 */
+ /* Altitude Reference */
+
+ 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);
+
+ /*
+ * "000" Status message
+ */
+
+ if (sentence_type == 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);
+ return;
+ }
+
+ /*
+ * "021" Position, Height, Velocity message,
+ * if we are still averaging our position
+ */
+ if (sentence_type == PMVXG_D_PHV && !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 %.9f %.9f %.4f\n",
+ up->avg_lat, up->avg_lon, up->avg_alt);
+ mx4200_debug(peer,
+ "mx4200_receive: position len %.4f %.4f %.4f\n",
+ up->filt_lat, up->filt_lon, up->filt_alt);
+ mx4200_debug(peer,
+ "mx4200_receive: position dop %.1f %.1f %.1f\n",
+ up->ndop, up->edop, up->vdop);
+ /*
+ * 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);
+ }
+ return;
+ }
+
+ /*
+ * "022" DOPs, if we are still averaging our position
+ */
+ if (sentence_type == PMVXG_D_DOPS && !up->known) {
+ if ((cp = mx4200_parse_d(peer)) != NULL) {
+ mx4200_debug(peer, "mx4200_receive: dop: %s\n", cp);
+ return;
+ }
+ return;
+ }
+
+ /*
+ * Print to the syslog:
+ * "004" Mode Data
+ * "030" Software Configuration
+ * "523" Time Recovery Parameters Currently in Use
+ */
+ if (sentence_type == PMVXG_D_MODEDATA ||
+ sentence_type == PMVXG_D_SOFTCONF ||
+ sentence_type == PMVXG_D_TRECOVUSEAGE ) {
+ if ((cp = mx4200_parse_s(peer)) != NULL) {
+ mx4200_debug(peer,
+ "mx4200_receive: multi-record: %s\n", cp);
+ return;
+ }
+ return;
+ }
+
+ /*
+ * "830" Time Recovery Results message
+ */
+ if (sentence_type == 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);
+
+ refclock_receive(peer);
+
+ /*
+ * We have succeeded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+ return;
+ }
+
+ /*
+ * Ignore all other sentence types
+ */
+ 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 occurence, 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, 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_RIGHT_NOW) {
+ 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;
+ pp->msec = 0;
+ pp->usec = 0;
+
+ /*
+ * 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, weight;
+ 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 weighted averages
+ */
+ weight = USUAL_EDOP / up->edop;
+ weight *= weight;
+ up->avg_lon = (up->filt_lon * up->avg_lon) + (weight * lon);
+ up->filt_lon += weight;
+ up->avg_lon = up->avg_lon / up->filt_lon;
+
+ weight = USUAL_NDOP / up->ndop;
+ weight *= weight;
+ up->avg_lat = (up->filt_lat * up->avg_lat) + (weight * lat);
+ up->filt_lat += weight;
+ up->avg_lat = up->avg_lat / up->filt_lat;
+
+ weight = USUAL_VDOP / up->vdop;
+ weight *= weight;
+ up->avg_alt = (up->filt_alt * up->avg_alt) + (weight * alt);
+ up->filt_alt += weight;
+ up->avg_alt = up->avg_alt / up->filt_alt;
+
+ mx4200_debug(peer,
+ "mx4200_receive: position rdg %.9f %.9f %.4f (CM=%.9f)\n",
+ lat, lon, alt, up->central_meridian);
+
+ return (NULL);
+}
+
+/*
+ * Parse a mx4200 DOP sentence.
+ *
+ * A typical message looks like this. Checksum has already been stripped.
+ *
+ * $PMVXG,022,SSSSSS.SSEE.E,NN.N,VV.V,XX,XX,XX,XX,XX,XX
+ *
+ * Field Field Contents
+ * ----- --------------
+ * Block Label: $PMVXG
+ * Sentence Type: 022=DOPs. The DOP values in this sentence
+ * correspond to the satellites listed. The PRNs in
+ * the message are listed in receiver channel number order
+ * 1 UTC measurement time (seconds into week)
+ * 2 EDOP (east DOP)
+ * 3 NDOP (north DOP)
+ * 4 VDOP (vertical DOP)
+ * 5 PRN on channel 1
+ * 6 PRN on channel 2
+ * 7 PRN on channel 3
+ * 8 PRN on channel 4
+ * 9 PRN on channel 5
+ * 10 PRN on channel 6
+ * 11 PRN on channel 7 (12-channel receivers only)
+ * 12 PRN on channel 8 (12-channel receivers only)
+ * 13 PRN on channel 9 (12-channel receivers only)
+ * 14 PRN on channel 10 (12-channel receivers only)
+ * 15 PRN on channel 11 (12-channel receivers only)
+ * 16 PRN on channel 12 (12-channel receivers only)
+ */
+static char *
+mx4200_parse_d(
+ struct peer *peer
+ )
+{
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+ int sentence_type;
+ double mtime, edop, ndop, vdop;
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /* Should never happen! */
+ if (up->moving) return ("mobile platform - no dop!");
+
+ sscanf ( pp->a_lastcode, "$PMVXG,%d,%lf,%lf,%lf,%lf",
+ &sentence_type, &mtime, &edop, &ndop, &vdop);
+
+ /* Sentence type */
+ if (sentence_type != PMVXG_D_DOPS)
+ return ("wrong rec-type");
+
+ /* Update values */
+ if (edop <= 0.0 || ndop <= 0.0 || vdop <= 0.0)
+ return ("nonpositive dop");
+ up->edop = edop;
+ up->ndop = ndop;
+ up->vdop = vdop;
+
+ 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, returning a timestamp.
+ */
+static int
+mx4200_pps(
+ struct peer *peer
+ )
+{
+ int temp_serial;
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ int request;
+#ifdef HAVE_CIOGETEV
+ request = CIOGETEV;
+#endif
+#ifdef HAVE_TIOCGPPSEV
+ request = TIOCGPPSEV;
+#endif
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ /*
+ * Grab the timestamp of the PPS signal.
+ */
+ temp_serial = up->ppsev.serial;
+ if (ioctl(fdpps, request, (caddr_t)&up->ppsev) < 0) {
+ /* XXX Actually, if this fails, we're pretty much screwed */
+ mx4200_debug(peer,
+ "mx4200_pps: CIOGETEV/TIOCGPPSEV: serial=%d, fdpps=%d, %s\n",
+ up->ppsev.serial, fdpps, strerror(errno));
+ refclock_report(peer, CEVNT_FAULT);
+ return(1);
+ }
+ if (temp_serial == up->ppsev.serial) {
+ mx4200_debug(peer,
+ "mx4200_pps: ppsev serial not incrementing: %d\n",
+ up->ppsev.serial);
+ refclock_report(peer, CEVNT_FAULT);
+ return(1);
+ }
+
+ /*
+ * Check pps serial number against last one
+ */
+ if (up->lastserial + 1 != up->ppsev.serial && up->lastserial != 0) {
+ if (up->ppsev.serial == up->lastserial)
+ mx4200_debug(peer, "mx4200_pps: no new pps event\n");
+ else
+ mx4200_debug(peer, "mx4200_pps: missed %d pps events\n",
+ up->ppsev.serial - up->lastserial - 1);
+ refclock_report(peer, CEVNT_FAULT);
+ }
+ up->lastserial = up->ppsev.serial;
+
+ /*
+ * Return the timestamp in pp->lastrec
+ */
+ up->ppsev.tv.tv_sec += (u_int32) JAN_1970;
+ TVTOTS(&up->ppsev.tv,&pp->lastrec);
+
+ return(0);
+}
+
+/*
+ * mx4200_debug - print debug messages
+ */
+#if __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
+{
+ va_list ap;
+ struct refclockproc *pp;
+ struct mx4200unit *up;
+
+ if (debug) {
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ 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.
+ */
+static void
+#if __STDC__
+mx4200_send(struct peer *peer, char *fmt, ...)
+#else
+ 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 __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif /* __STDC__ */
+
+ pp = peer->procptr;
+ up = (struct mx4200unit *)pp->unitptr;
+
+ cp = buf;
+ *cp++ = '$';
+#ifdef notdef
+ /* BSD is rational */
+ n = vsnprintf(cp, sizeof(buf) - 1, fmt, ap);
+#else
+ /* SunOS sucks */
+ (void)vsprintf(cp, fmt, ap);
+ n = strlen(cp);
+#endif /* notdef */
+ ck = mx4200_cksum(cp, n);
+ cp += n;
+ ++n;
+#ifdef notdef
+ /* BSD is rational */
+ n += snprintf(cp, sizeof(buf) - n - 5, "*%02X\r\n", ck);
+#else
+ /* SunOS sucks */
+ sprintf(cp, "*%02X\r\n", ck);
+ n += strlen(cp);
+#endif /* notdef */
+
+ 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/contrib/ntp/ntpd/refclock_nmea.c b/contrib/ntp/ntpd/refclock_nmea.c
new file mode 100644
index 0000000..211d01e
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_nmea.c
@@ -0,0 +1,411 @@
+/*
+ * 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(REFCLOCK) && defined(CLOCK_NMEA)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * 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.
+ */
+
+/*
+ * Definitions
+ */
+#define DEVICE "/dev/gps%d" /* name of radio device */
+#define SPEED232 B4800 /* uart speed (4800 bps) */
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define DCD_PRECISION (-20) /* precision assumed (about 1 us) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "NMEA GPS Clock" /* who we are */
+
+#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 */
+};
+
+/*
+ * Function prototypes
+ */
+static int nmea_start P((int, struct peer *));
+static void nmea_shutdown P((int, struct peer *));
+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 */
+ noentry, /* handle control */
+ 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);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct nmeaunit *)
+ emalloc(sizeof(struct nmeaunit)))) {
+ (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 = DCD_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);
+
+ return (1);
+}
+
+/*
+ * 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;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+/*
+ * 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;
+ l_fp trtmp;
+ int month, day;
+ int i;
+ char *cp, *dp;
+ int cmdtype;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+
+ /*
+ * There is a case that a <CR><LF> gives back a "blank" line
+ */
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * We get a buffer and timestamp for each <cr>.
+ */
+ pp->lastrec = up->tstamp = trtmp;
+ up->pollcnt = 2;
+#ifdef DEBUG
+ if (debug)
+ printf("nmea: timecode %d %s\n", pp->lencode,
+ pp->a_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
+ * $GPGGA,162617.0,4548.339,N,00837.719,E,1,07,0.97,00262,M,048,M,,*5D
+ */
+#define GPRMC 0
+#define GPXXX 1
+#define GPGCA 2
+ cp = pp->a_lastcode;
+ cmdtype=0;
+ if(strncmp(cp,"$GPRMC",6)==0) {
+ cmdtype=GPRMC;
+ }
+ else if(strncmp(cp,"$GPGGA",6)==0) {
+ cmdtype=GPGCA;
+ }
+ else if(strncmp(cp,"$GPXXX",6)==0) {
+ cmdtype=GPXXX;
+ }
+ else
+ return;
+
+ switch( cmdtype ) {
+ case GPRMC:
+ case GPGCA:
+ /*
+ * Check time code format of NMEA
+ */
+
+ dp = field_parse(cp,1);
+ 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;
+ }
+
+ /*
+ * Test for synchronization. Check for quality byte.
+ */
+ dp = field_parse(cp,2);
+ if( dp[0] != 'A') {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+ case GPXXX:
+ return;
+ default:
+ return;
+
+ }
+
+ if (cmdtype ==GPGCA) {
+ /* 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;
+ } else {
+ dp = field_parse(cp,9);
+ /*
+ * Convert date and check values.
+ */
+ 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';
+ }
+
+ 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;
+
+ dp = field_parse(cp,1);
+ /*
+ * 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';
+ pp->msec = 0;
+
+ if (pp->hour > 23 || pp->minute > 59 || pp->second > 59) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * 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;
+
+ refclock_receive(peer);
+
+ 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/contrib/ntp/ntpd/refclock_oncore.c b/contrib/ntp/ntpd/refclock_oncore.c
new file mode 100644
index 0000000..9ff3d23
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_oncore.c
@@ -0,0 +1,1660 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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.
+ *
+ * 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
+ *
+ * --------------------------------------------------------------------------
+ * 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 (where n and N are the unit number, viz 127.127.30.N)
+ * or /etc/ntp.oncore
+ * --------------------------------------------------------------------------
+ * Reg.Clemens <reg@dwf.com> Sep98.
+ * Original code written for FreeBSD.
+ * With these mods it works on SunOS, Solaris (untested) and Linux
+ * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + changes).
+ *
+ * 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 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ONCORE)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_PPSAPI
+# ifdef HAVE_TIMEPPS_H
+# include <timepps.h>
+# else
+# ifdef HAVE_SYS_TIMEPPS_H
+# include <sys/timepps.h>
+# endif
+# endif
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#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_TIMESPEC
+ struct timespec tv;
+# else
+ struct timeval tv;
+# endif
+ u_int serial;
+};
+#endif /* not HAVE_STRUCT_PPSCLOCKEV */
+
+enum receive_state {
+ ONCORE_NO_IDEA,
+ ONCORE_RESET_SENT,
+ ONCORE_TEST_SENT,
+ ONCORE_ID_SENT,
+ ONCORE_ALMANAC,
+ ONCORE_RUN
+};
+
+enum site_survey_state {
+ ONCORE_SS_UNKNOWN,
+ ONCORE_SS_HW,
+ ONCORE_SS_SW,
+ ONCORE_SS_DONE
+};
+
+struct instance {
+ int unit; /* 127.127.30.unit */
+ int ttyfd; /* TTY file descriptor */
+ int ppsfd; /* PPS file descriptor */
+#ifdef HAVE_PPSAPI
+ pps_handle_t pps_h;
+ pps_params_t pps_p;
+#endif
+ enum receive_state o_state; /* Receive state */
+
+ enum site_survey_state site_survey; /* Site Survey state */
+
+ struct refclockproc *pp;
+ struct peer *peer;
+
+ int Bj_day;
+ int assert;
+
+ long delay; /* ns */
+ long offset; /* ns */
+
+ double ss_lat;
+ double ss_long;
+ double ss_ht;
+ int ss_count;
+ u_char ss_ht_type;
+ int posn_set;
+
+ int printed;
+ int pollcnt;
+ int polled;
+ u_int ev_serial;
+ int Rcvptr;
+ u_char Rcvbuf[500];
+ u_char Ea[77];
+ u_char En[70];
+ u_char Cj[300];
+ u_char As;
+ u_char Ay;
+ u_char Az;
+ u_char init_type;
+ s_char saw_tooth;
+};
+
+#define rcvbuf instance->Rcvbuf
+#define rcvptr instance->Rcvptr
+
+static void oncore_consume P((struct instance *));
+static void oncore_poll P((int, struct peer *));
+static void oncore_read_config P((struct instance *));
+static void oncore_receive P((struct recvbuf *));
+static void oncore_sendmsg P((int fd, u_char *, u_int));
+static void oncore_shutdown P((int, struct peer *));
+static int oncore_start P((int, struct peer *));
+static void oncore_stats P((struct instance *));
+
+static void oncore_msg_any P((struct instance *, u_char *, u_int, int));
+static void oncore_msg_As P((struct instance *, u_char *, u_int));
+static void oncore_msg_At P((struct instance *, u_char *, u_int));
+static void oncore_msg_Ay P((struct instance *, u_char *, u_int));
+static void oncore_msg_Az P((struct instance *, u_char *, u_int));
+static void oncore_msg_Bj P((struct instance *, u_char *, u_int));
+static void oncore_msg_Cf P((struct instance *, u_char *, u_int));
+static void oncore_msg_Cj P((struct instance *, u_char *, u_int));
+static void oncore_msg_Ea P((struct instance *, u_char *, u_int));
+static void oncore_msg_En P((struct instance *, u_char *, u_int));
+static void oncore_msg_Fa P((struct instance *, u_char *, u_int));
+
+struct refclock refclock_oncore = {
+ oncore_start, /* start up driver */
+ oncore_shutdown, /* shut down driver */
+ oncore_poll, /* transmit poll message */
+ noentry, /* not used */
+ 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 UT or VP Oncore.
+ */
+
+static struct {
+ const char flag[3];
+ const int len;
+ void (*handler) P((struct instance *, u_char *, u_int));
+ const char *fmt;
+} oncore_messages[] = {
+ /* Ea and En first since they're most common */
+ { "Ea", 76, oncore_msg_Ea, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC" },
+ { "En", 69, oncore_msg_En, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC" },
+ { "Ab", 10, 0, "" },
+ { "Ac", 11, 0, "" },
+ { "Ad", 11, 0, "" },
+ { "Ae", 11, 0, "" },
+ { "Af", 15, 0, "" },
+ { "As", 20, oncore_msg_As, "" },
+ { "At", 8, oncore_msg_At, "" },
+ { "Aw", 8, 0, "" },
+ { "Ay", 11, oncore_msg_Ay, "" },
+ { "Az", 11, oncore_msg_Az, "" },
+ { "AB", 8, 0, "" },
+ { "Bb", 92, 0, "" },
+ { "Bj", 8, oncore_msg_Bj, "" },
+ { "Cb", 33, 0, "" },
+ { "Cf", 7, oncore_msg_Cf, "" },
+ { "Cg", 8, 0, "" },
+ { "Ch", 9, 0, "" },
+ { "Cj", 294, oncore_msg_Cj, "" },
+ { "Ek", 71, 0, "" },
+ { "Fa", 9, oncore_msg_Fa, "" },
+ { "Sz", 8, 0, "" },
+ { {0}, 7, 0, ""}
+};
+
+
+/*
+ * Position Set.
+ */
+u_char oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 };
+u_char oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 };
+u_char oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 };
+
+/*
+ * Position-Hold Mode
+ * Start automatic site survey
+ */
+static u_char oncore_cmd_At[] = { 'A', 't', 2 };
+
+/*
+ * Position-Hold Position
+ */
+u_char oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };
+u_char oncore_cmd_Asx[]= { 'A', 's', 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff, 0xff };
+
+/*
+ * Set to UTC time (not GPS).
+ */
+u_char oncore_cmd_Aw[] = { 'A', 'w', 1 };
+
+/*
+ * Read back PPS Offset for Output
+ */
+u_char oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 };
+u_char oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff };
+
+/*
+ * Read back Cable Delay for Output
+ */
+u_char oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 };
+u_char oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff };
+
+/*
+ * Application type = static.
+ */
+u_char oncore_cmd_AB[] = { 'A', 'B', 4 };
+
+/*
+ * Visible Satellite Status Msg.
+ */
+u_char oncore_cmd_Bb[] = { 'B', 'b', 0 }; /* just turn off */
+
+/*
+ * Leap Second Pending Message
+ * Request message once
+ */
+u_char oncore_cmd_Bj[] = { 'B', 'j', 0 };
+
+/*
+ * Set to Defaults
+ */
+static u_char oncore_cmd_Cf[] = { 'C', 'f' };
+
+/*
+ * Set to Position Fix mode (only needed on VP).
+ */
+u_char oncore_cmd_Cg[] = { 'C', 'g', 1 };
+
+/*
+ * Receiver Id
+ */
+static u_char oncore_cmd_Cj[] = { 'C', 'j' };
+
+/*
+ * Position/Status/Data message
+ * Send once per second
+ */
+static u_char oncore_cmd_Ea[] = { 'E', 'a', 1 };
+
+/*
+ * Position/Status Extension Msg
+ */
+u_char oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */
+
+/*
+ * Time Raim Setup & Status Message
+ * Send once per second
+ * Time-RAIM on
+ * Alarm limit 1us
+ * PPS on when we have the first sat
+ */
+static u_char oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/*
+ * Self-test
+ */
+static u_char oncore_cmd_Fa[] = { 'F', 'a' };
+
+#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) { unsigned 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, mode;
+ 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);
+ }
+
+ if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) {
+ /* same device here */
+ if (!(fd1 = refclock_open(device1, SPEED, LDISC_RAW
+#ifdef HAVE_PPSAPI
+#else
+ | 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);
+ }
+ }
+
+ /* Devices now open, initialize instance structure */
+
+ if (!(instance = (struct instance *)emalloc(sizeof *instance))) {
+ perror("malloc");
+ close(fd1);
+ return (0);
+ }
+ memset((char *) instance, 0, sizeof *instance);
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)instance;
+ instance->unit = unit;
+ instance->ttyfd = fd1;
+ instance->ppsfd = fd2;
+
+ instance->Bj_day = -1;
+ instance->assert = 1;
+
+ /* go read any input data in /etc/ntp.oncoreX */
+
+ oncore_read_config(instance);
+
+#ifdef HAVE_PPSAPI
+ if (time_pps_create(fd2, &instance->pps_h) < 0) {
+ perror("time_pps_create");
+ exit(1);
+ }
+
+ if (instance->assert) {
+ 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;
+ if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
+ perror("time_pps_setparams");
+ exit(1);
+ }
+ if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS,
+ instance->pps_p.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR),
+ PPS_TSFMT_TSPEC) < 0) {
+ perror("time_pps_kcbind");
+ }
+#endif
+ instance->pp = pp;
+ instance->peer = peer;
+ instance->o_state = ONCORE_NO_IDEA;
+ cp = "state = ONCORE_NO_IDEA";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+
+ /*
+ * Initialize miscellaneous variables
+ */
+
+ peer->precision = -26;
+ peer->minpoll = 4;
+ peer->maxpoll = 4;
+ pp->clockdesc = "Motorola UT/VP Oncore GPS Receiver";
+ memcpy((char *)&pp->refid, "GPS\0", 4);
+
+ 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);
+ }
+ pp->unitptr = (caddr_t)instance;
+
+ /*
+ * This will start the Oncore receiver.
+ * We send info from config to Oncore later.
+ */
+
+ mode = instance->init_type;
+ if (mode == 3 || mode == 4) {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cf, sizeof oncore_cmd_Cf);
+ instance->o_state = ONCORE_RESET_SENT;
+ cp = "state = ONCORE_RESET_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ } else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof oncore_cmd_Fa);
+ instance->o_state = ONCORE_TEST_SENT;
+ cp = "state = ONCORE_TEST_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+
+ instance->pollcnt = 2;
+
+ return (1);
+}
+
+/*
+ * Read Input file if it exists.
+ */
+static void
+oncore_read_config(
+ struct instance *instance
+ )
+{
+/*
+ * First we try to open the configuration file /etc/ntp.oncoreN, where
+ * N is the unit number viz 127.127.30.N.
+ * If we don't find it, then we try the file /etc/ntp.oncore.
+ *
+ * If we find NEITHER 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
+ * 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' (or 'HTMSL' or 'HTGPS') 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 same as HTGPS.
+ * HTMSL = HT above mean_sea_level,
+ * HTGPS = HT above GPS ellipse.
+ *
+ * There are two optional lines, starting with DELAY and 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.
+ * DELAY is cable delay, typically a few tens of ns.
+ * 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 another 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.
+ *
+ * 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, line[100], units[2], device[20];
+ int i, j, sign, lat_flg, long_flg, ht_flg, mode;
+ double f1, f2, f3;
+
+
+ sprintf(device, "%s%d", INIT_FILE, instance->unit);
+ if ((fd=fopen(device, "r")) == NULL)
+ if ((fd=fopen(INIT_FILE, "r")) == NULL) {
+ instance->init_type = 4;
+ return;
+ }
+
+ mode = 0;
+ lat_flg = long_flg = ht_flg = 0;
+ while (fgets(line, 100, fd)) {
+ if ((cp=strchr(line, '#')))
+ *cp = '\0';
+ i = strlen(line);
+ for (j=0; j<i; j++) /* just in case lower case */
+ if (islower((int)line[j]))
+ line[j] = toupper(line[j]);
+ for (j=0; j<i; j++) /* let them use `=' between terms */
+ if (line[j] == '=')
+ line[j] = ' ';
+ for (j=0; j<i; j++)
+ if (line[j] != ' ')
+ break;
+ if (!strncmp(&line[j], "LAT", 3)) {
+ j += 3;
+ f1 = f2 = f3 = 0;
+ sscanf(&line[j], "%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(&line[j], "LON", 3)) {
+ j += 3;
+ f1 = f2 = f3 = 0;
+ sscanf(&line[j], "%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(&line[j], "HT", 2)) {
+ instance->ss_ht_type = 0;
+ if (!strncmp(&line[j], "HTGPS", 5)) {
+ instance->ss_ht_type = 0;
+ j +=3;
+ }
+ if (!strncmp(&line[j], "HTMSL", 5)) {
+ instance->ss_ht_type = 1;
+ j +=3;
+ }
+ j += 2;
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(&line[j], "%lf %1s", &f1, units);
+ if (units[0] == 'F')
+ f1 = 0.3048 * f1;
+ instance->ss_ht = 100 * f1; /* cm */
+ ht_flg++;
+ } else if (!strncmp(&line[j], "DELAY", 5)) {
+ j += 5;
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(&line[j], "%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;
+ instance->delay = f1; /* delay in ns */
+ } else if (!strncmp(&line[j], "OFFSET", 6)) {
+ j += 6;
+ f1 = 0;
+ units[0] = '\0';
+ sscanf(&line[j], "%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;
+ instance->offset = f1; /* offset in ns */
+ } else if (!strncmp(&line[j], "MODE", 4)) {
+ j += 4;
+ sscanf(&line[j], "%d", &mode);
+ if (mode < 0 || mode > 4)
+ mode = 4;
+ instance->init_type = mode;
+ } else if (!strncmp(&line[j], "ASSERT", 6)) {
+ instance->assert = 1;
+ } else if (!strncmp(&line[j], "CLEAR", 5)) {
+ instance->assert = 0;
+ }
+ }
+ 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) && !(lat_flg * long_flg * ht_flg)) {
+ printf("ONCORE: incomplete data on %s\n", INIT_FILE);
+ instance->posn_set = 0;
+ if (mode == 1 || mode == 3)
+ instance->init_type++;
+ }
+}
+
+
+
+/*
+ * 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;
+ 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->pollcnt)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ instance->pollcnt--;
+ peer->procptr->polls++;
+ instance->polled = 1;
+}
+
+
+
+/*
+ * move dta from NTP to buffer (toss in unlikely case it wont fit)
+ */
+static void
+oncore_receive(
+ struct recvbuf *rbufp
+ )
+{
+ u_int 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, l, j, m;
+
+ 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: >>> skipping %d chars\n", i);
+ if (i != rcvptr)
+ memcpy(rcvbuf, rcvbuf+i, (unsigned)(rcvptr-i));
+ rcvptr -= i;
+ }
+
+ /* 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), 2))
+ break;
+ l = oncore_messages[m].len;
+#if 0
+ if (debug > 3)
+ printf("ONCORE: GOT: %c%c %d of %d entry %d\n", rcvbuf[2], rcvbuf[3], rcvptr, l, m);
+#endif
+ /* Got the entire message ? */
+
+ if (rcvptr < l)
+ return;
+
+ /* Check the checksum */
+
+ j = 0;
+ for (i = 2; i < l-3; i++)
+ j ^= rcvbuf[i];
+ if (j == rcvbuf[l-3]) {
+ oncore_msg_any(instance, rcvbuf, (unsigned) (l-3), m);
+ if (oncore_messages[m].handler)
+ oncore_messages[m].handler(instance, rcvbuf, (unsigned) (l-3));
+ } else if (debug) {
+ printf("ONCORE: Checksum mismatch! calc %o is %o\n", j, rcvbuf[l-3]);
+ printf("ONCORE: @@%c%c ", rcvbuf[2], rcvbuf[3]);
+ for (i=4; i<l; i++)
+ printf("%03o ", rcvbuf[i]);
+ printf("\n");
+ }
+
+ if (l != rcvptr)
+ memcpy(rcvbuf, rcvbuf+l, (unsigned) (rcvptr-l));
+ rcvptr -= l;
+ }
+}
+
+
+
+/*
+ * write message to Oncore.
+ */
+static void
+oncore_sendmsg(
+ int fd,
+ u_char *ptr,
+ u_int len
+ )
+{
+ u_char cs = 0;
+
+ printf("ONCORE: Send @@%c%c %d\n", ptr[0], ptr[1], len);
+ write(fd, "@@", 2);
+ write(fd, ptr, len);
+ while (len--)
+ cs ^= *ptr++;
+ write(fd, &cs, 1);
+ write(fd, "\r\n", 2);
+}
+
+
+
+static void
+oncore_msg_any(
+ struct instance *instance,
+ u_char *buf,
+ u_int len,
+ int idx
+ )
+{
+ int i;
+ const char *fmt = oncore_messages[idx].fmt;
+ const char *p;
+ struct timeval tv;
+
+ if (debug > 3) {
+ GETTIMEOFDAY(&tv, 0);
+ printf("ONCORE: %ld.%06ld\n", (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");
+ }
+ }
+}
+
+
+
+/*
+ * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup
+ * not so for VP (eeprom) or UT with battery
+ */
+static void
+oncore_msg_Cf(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ const char *cp;
+
+ if (instance->o_state == ONCORE_RESET_SENT) {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Fa, sizeof oncore_cmd_Fa);
+ instance->o_state = ONCORE_TEST_SENT;
+ cp = "state = ONCORE_TEST_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+}
+
+
+
+static void
+oncore_msg_Fa(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ const char *cp;
+
+ if (instance->o_state == ONCORE_TEST_SENT) {
+ if (debug > 2)
+ printf("ONCORE: >>@@Fa %x %x\n", buf[4], buf[5]);
+ if (buf[4] || buf[5]) {
+ printf("ONCORE: SELF TEST FAILED\n");
+ exit(1);
+ }
+
+ /* sometimes the @@Cj request does not produce any output
+ PERHAPS the ONCORE is still busy from the selftest???
+ try a 2 second sleep here to see if it makes any difference
+ */
+
+ sleep(2);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cj, sizeof oncore_cmd_Cj);
+ instance->o_state = ONCORE_ID_SENT;
+ cp = "state = ONCORE_ID_SENT";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+}
+
+
+
+/*
+ * preliminaries out of the way, this is the REAL start of initialization
+ */
+static void
+oncore_msg_Cj(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ const char *cp;
+ int mode;
+
+ if (instance->o_state != ONCORE_ID_SENT)
+ return;
+
+ memcpy(instance->Cj, buf, len);
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Cg, sizeof oncore_cmd_Cg); /* Set Posn Fix mode (not Idle (VP)) */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bb, sizeof oncore_cmd_Bb); /* turn off */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ek, sizeof oncore_cmd_Ek); /* turn off */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Aw, sizeof oncore_cmd_Aw); /* UTC time */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_AB, sizeof oncore_cmd_AB); /* Appl type static */
+
+ mode = instance->init_type;
+ if (debug)
+ printf("ONCORE: INIT mode = %d\n", mode);
+
+ /* 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 Site Survey.
+ */
+
+ switch (mode) {
+ case 0: /* NO initialization, don't change anything */
+ instance->site_survey = ONCORE_SS_DONE;
+ break;
+
+ case 1:
+ case 3:
+ w32_buf(&oncore_cmd_As[2], (int) instance->ss_lat);
+ w32_buf(&oncore_cmd_As[6], (int) instance->ss_long);
+ w32_buf(&oncore_cmd_As[10], (int) instance->ss_ht);
+ oncore_cmd_As[14] = instance->ss_ht_type;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_As, sizeof oncore_cmd_As);
+
+ instance->site_survey = ONCORE_SS_DONE;
+ oncore_cmd_At[2] = 1;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+ break;
+
+ case 2:
+ case 4:
+ if (instance->posn_set) {
+ w32_buf(&oncore_cmd_Ad[2], (int) instance->ss_lat);
+ w32_buf(&oncore_cmd_Ae[2], (int) instance->ss_long);
+ w32_buf(&oncore_cmd_Af[2], (int) instance->ss_ht);
+ oncore_cmd_Af[6] = instance->ss_ht_type;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ad, sizeof oncore_cmd_Ad);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ae, sizeof oncore_cmd_Ae);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Af, sizeof oncore_cmd_Af);
+ }
+ instance->site_survey = ONCORE_SS_UNKNOWN;
+ oncore_cmd_At[2] = 2;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
+ break;
+ }
+
+ if (mode != 0) {
+ /* cable delay in ns */
+ w32_buf(&oncore_cmd_Az[2], instance->delay);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Az, sizeof oncore_cmd_Az);
+
+ /* PPS offset in ns */
+ w32_buf(&oncore_cmd_Ay[2], instance->offset);
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ay, sizeof oncore_cmd_Ay);
+ }
+
+ /* 8chan - Position/Status/Data Output Message, 1/s */
+
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Ea, sizeof oncore_cmd_Ea);
+
+ instance->o_state = ONCORE_ALMANAC;
+ cp = "state = ONCORE_ALMANAC";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+}
+
+
+
+static void
+oncore_msg_Ea(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ const char *cp;
+
+ if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN)
+ return;
+
+ memcpy(instance->Ea, buf, len);
+
+ /* When we have an almanac, start the En messages */
+
+ if (instance->o_state == ONCORE_ALMANAC) {
+ if ((instance->Ea[72] & 1)) {
+ if (debug)
+ printf("ONCORE: waiting for almanac\n");
+ return;
+ } else {
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_En, sizeof oncore_cmd_En);
+ instance->o_state = ONCORE_RUN;
+ cp = "state = ONCORE_RUN";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+ }
+ }
+
+ /* must be ONCORE_RUN if we are here */
+ /* First check if Hardware SiteSurvey has Finished */
+
+ if ((instance->site_survey == ONCORE_SS_HW) && !(instance->Ea[37] & 0x20)) {
+ instance->site_survey = ONCORE_SS_DONE;
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+ }
+
+ if (!instance->printed && instance->site_survey == ONCORE_SS_DONE) { /* will print to clockstat when all */
+ instance->printed = 1; /* three messages respond */
+ /* Read back Position Hold Params */
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Asx, sizeof oncore_cmd_Asx);
+ /* Read back PPS Offset for Output */
+ 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);
+ }
+
+ /* Check the leap second status once per day */
+
+ /*
+ * The following additional check, checking for June/December, is a
+ * workaround for incorrect ONCORE firmware. The oncore starts
+ * reporting the leap second when the GPS satellite data message
+ * (page 18, subframe 4) is updated to a date in the future, which
+ * which can be several months before the leap second. WWV and other
+ * services seem to wait until the month of the event to turn
+ * on their indicators (which are usually a single bit).
+ */
+
+ if ((buf[4] == 6) || (buf[4] == 12)) {
+ if (instance->Bj_day != buf[5]) { /* do this 1/day */
+ instance->Bj_day = buf[5];
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_Bj, sizeof oncore_cmd_Bj);
+ }
+ }
+ 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];
+
+ if (instance->site_survey != ONCORE_SS_SW)
+ return;
+
+ /*
+ * We have to average our own position for the Position Hold Mode
+ */
+
+ /* We only take PDOP/3D fixes */
+
+ if (instance->Ea[37] & 1)
+ return;
+
+ /* Not if poor geometry or less than 3 sats */
+
+ if (instance->Ea[72] & 0x52)
+ return;
+
+ /* Only 3D fix */
+
+ if (!(instance->Ea[72] & 0x20))
+ return;
+
+ instance->ss_lat += buf_w32(&instance->Ea[15]);
+ instance->ss_long += buf_w32(&instance->Ea[19]);
+ instance->ss_ht += buf_w32(&instance->Ea[23]); /* GPS ellipse */
+ instance->ss_count++;
+
+ if (debug)
+ printf("ONCORE: AVG %d %d %d %d\n",
+ instance->ss_count,
+ (unsigned) (instance->ss_lat / instance->ss_count),
+ (unsigned) (instance->ss_long / instance->ss_count),
+ (unsigned) (instance->ss_ht / 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;
+
+ w32_buf(&oncore_cmd_As[2], (int) instance->ss_lat);
+ w32_buf(&oncore_cmd_As[6], (int) instance->ss_long);
+ w32_buf(&oncore_cmd_As[10], (int) instance->ss_ht);
+ oncore_cmd_As[14] = 0;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_As, sizeof oncore_cmd_As);
+
+ oncore_cmd_At[2] = 1;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
+ record_clock_stats(&(instance->peer->srcadr), "Now in 0D mode");
+ instance->site_survey = ONCORE_SS_DONE;
+}
+
+
+
+static void
+oncore_msg_En(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ int j;
+ l_fp ts, ts_tmp;
+ double dmy;
+#ifdef HAVE_TIMESPEC
+ struct timespec *tsp = 0;
+#else
+ struct timeval *tsp = 0;
+#endif
+#ifdef HAVE_PPSAPI
+ 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
+#endif /* ! HAVE_PPS_API */
+
+ if (instance->o_state != ONCORE_RUN)
+ return;
+
+ memcpy(instance->En, buf, len);
+
+ /* Don't do anything without an almanac to define the GPS->UTC delta */
+
+ if (instance->Ea[72] & 1)
+ return;
+
+ /* If Time RAIM doesn't like it, don't trust it */
+
+ if (instance->En[21])
+ 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)
+ printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n",
+ pps_i.assert_sequence, j, tsp->tv_sec, tsp->tv_nsec);
+
+ if (pps_i.assert_sequence == j) {
+ printf("ONCORE: oncore_msg_En, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = pps_i.assert_sequence;
+ } else {
+ tsp = &pps_i.clear_timestamp;
+
+ if (debug > 2)
+ printf("ONCORE: serial/j (%d, %d) %ld.%09ld\n",
+ pps_i.clear_sequence, j, tsp->tv_sec, tsp->tv_nsec);
+
+ if (pps_i.clear_sequence == j) {
+ printf("ONCORE: oncore_msg_En, 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 /* ! HAVE_PPSAPI */
+ j = instance->ev_serial;
+ if (ioctl(instance->ppsfd, r, (caddr_t) &ev) < 0) {
+ perror("ONCORE: IOCTL:");
+ return;
+ }
+
+ tsp = &ev.tv;
+
+ if (debug > 2)
+ printf("ONCORE: serial/j (%d, %d) %ld.%06ld\n",
+ ev.serial, j, tsp->tv_sec, tsp->tv_usec);
+
+ if (ev.serial == j) {
+ printf("ONCORE: oncore_msg_En, error serial pps\n");
+ return;
+ }
+ instance->ev_serial = ev.serial;
+
+ /* convert timeval -> ntp l_fp */
+
+ TVTOTS(tsp, &ts);
+#endif
+ /* now have timestamp in ts */
+ /* add in saw_tooth and offset */
+
+ /* 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 */
+
+ j = instance->saw_tooth + instance->offset;
+ instance->saw_tooth = (s_char) buf[25]; /* update for next time */
+#ifdef HAVE_PPSAPI
+ /* must hand this offset 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 =
+ -(instance->saw_tooth + instance->offset);
+ } else {
+ instance->pps_p.clear_offset.tv_nsec =
+ -(instance->saw_tooth + instance->offset);
+ }
+
+ if (time_pps_setparams(instance->pps_h, &instance->pps_p))
+ perror("time_pps_setparams");
+#else
+ /* if not PPSAPI, no way to inform kernel of OFFSET, just do it */
+
+ dmy = -1.0e-9*j;
+ 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;
+ instance->pp->msec = 0;
+
+ 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 */
+ sprintf(instance->pp->a_lastcode,
+ "%u.%09u %d %d %2d %2d %2d %2ld rstat %02x dop %d nsat %2d,%d raim %d sigma %d 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,
+
+ instance->Ea[72], instance->Ea[37], instance->Ea[38], instance->Ea[39], instance->En[21],
+ /*rstat dop nsat visible, nsat tracked, raim */
+ instance->En[23]*256+instance->En[24], (s_char) buf[25],
+ /* sigma neg-sawtooth */
+ /*sat*/ instance->Ea[41], instance->Ea[45], instance->Ea[49], instance->Ea[53],
+ instance->Ea[57], instance->Ea[61], instance->Ea[65], instance->Ea[69]
+ );
+
+ if (debug > 2) {
+ int i;
+ i = strlen(instance->pp->a_lastcode);
+ printf("ONCORE: len = %d %s\n", i, instance->pp->a_lastcode);
+ }
+
+ 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;
+*/
+ refclock_receive(instance->peer);
+ }
+}
+
+
+
+/*
+ * 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,
+ u_int len
+ )
+{
+
+ if (instance->site_survey != ONCORE_SS_UNKNOWN)
+ return;
+
+ if (buf[4] == 2)
+ instance->site_survey = ONCORE_SS_HW;
+ else {
+ instance->site_survey = ONCORE_SS_SW;
+
+ /*
+ * Probably a VP or an older UT which can't do site-survey.
+ * We will have to do it ourselves
+ */
+ oncore_cmd_At[2] = 0;
+ instance->ss_lat = instance->ss_long = instance->ss_ht = 0;
+ oncore_sendmsg(instance->ttyfd, oncore_cmd_At, sizeof oncore_cmd_At);
+ }
+}
+
+
+
+/* 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.
+ * Until this firmware bug is fixed, @@Bj is only called in June/December.
+ */
+
+static void
+oncore_msg_Bj(
+ struct instance *instance,
+ u_char *buf,
+ u_int 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);
+}
+
+
+
+/*
+ * get Position hold position
+ */
+static void
+oncore_msg_As(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ int lat, lon, ht;
+
+ if (!instance->printed || instance->As)
+ return;
+
+ instance->As = 1;
+
+ lat = buf_w32(&buf[4]);
+ instance->ss_lat = lat;
+
+ lon = buf_w32(&buf[8]);
+ instance->ss_long = lon;
+
+ ht = buf_w32(&buf[12]);
+ instance->ss_ht = ht;
+
+ instance->ss_ht_type = buf[16];
+
+ if (instance->Ay && instance->Az)
+ oncore_stats(instance);
+}
+
+
+
+/*
+ * get PPS Offset
+ */
+static void
+oncore_msg_Ay(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ if (!instance->printed || instance->Ay)
+ return;
+
+ instance->Ay = 1;
+
+ instance->offset = buf_w32(&buf[4]);
+
+ if (instance->As && instance->Az)
+ oncore_stats(instance);
+}
+
+
+
+/*
+ * get Cable Delay
+ */
+static void
+oncore_msg_Az(
+ struct instance *instance,
+ u_char *buf,
+ u_int len
+ )
+{
+ if (!instance->printed || instance->Az)
+ return;
+
+ instance->Az = 1;
+
+ instance->delay = buf_w32(&buf[4]);
+
+ if (instance->As && instance->Ay)
+ oncore_stats(instance);
+}
+
+
+
+/*
+ * print init data in ONCORE to clockstats file
+ */
+static void
+oncore_stats(
+ struct instance *instance
+ )
+{
+ char Msg[120], ew, ns, *cp, *cp1;
+ const char *Ht;
+ double xd, xm, xs, yd, ym, ys, hm, hft;
+ int idx, idy, is, imx, imy;
+ long lat, lon;
+
+ /* First, Receiver ID */
+
+ 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 Position */
+
+ 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;
+ Ht = instance->ss_ht_type ? "MSL" : "GPS";
+
+ 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) %s", ns, xd, ew, yd, hm, hft, Ht);
+ 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 = %5.2fm (%5.2fft) %s", ns, idx, xm, ew, idy, ym, hm, hft, Ht);
+ 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 = %5.2fm (%5.2fft) %s", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft, Ht);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ /* finally, cable delay and PPS offset */
+
+ sprintf(Msg, "Cable delay is set to %ld ns", instance->delay);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+ sprintf(Msg, "PPS Offset is set to %ld ns", instance->offset);
+ record_clock_stats(&(instance->peer->srcadr), Msg);
+
+#ifdef HAVE_PPSAPI
+ if (instance->assert)
+ cp = "Timing on Assert.";
+ else
+ cp = "Timing on Clear.";
+ record_clock_stats(&(instance->peer->srcadr), cp);
+#endif
+}
+
+#else
+int refclock_oncore_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_palisade.c b/contrib/ntp/ntpd/refclock_palisade.c
new file mode 100644
index 0000000..b9a0e2a
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_palisade.c
@@ -0,0 +1,880 @@
+/*
+ * This software was developed by the Software and Component Technologies
+ * group of Trimble Navigation, Ltd.
+ *
+ * Copyright (c) 1997, 1998, 1999 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(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 */
+};
+
+
+/*
+ * 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));
+
+ 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;
+ }
+
+ if (up->rpt_buf[0] == (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->usec = (long) (secfrac * 1000000);
+
+ 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->usec, 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->usec = (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->usec, 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->usec);
+#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->usec);
+ 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
+
+ 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 (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;
+
+ 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));
+}
+
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_palisade.h b/contrib/ntp/ntpd/refclock_palisade.h
new file mode 100644
index 0000000..b78d988
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_palisade.h
@@ -0,0 +1,167 @@
+/*
+ * This software was developed by the Software and Component Technologies
+ * group of Trimble Navigation, Ltd.
+ *
+ * Copyright (c) 1997, 1998, 1999 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 5 /* 16 seconds */
+#define TRMB_MAXPOLL 7 /* 64 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 */
+};
+
+/*
+ * 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/contrib/ntp/ntpd/refclock_parse.c b/contrib/ntp/ntpd/refclock_parse.c
new file mode 100644
index 0000000..6771a13
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_parse.c
@@ -0,0 +1,5340 @@
+/*
+ * /src/NTP/ntp-4/ntpd/refclock_parse.c,v 4.29 1999/02/28 19:58:23 kardel RELEASE_19990228_A
+ *
+ * refclock_parse.c,v 4.29 1999/02/28 19:58:23 kardel RELEASE_19990228_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.29 1999/02/28 19:58:23 kardel RELEASE_19990228_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_char pollneeddata; /* 1 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 rawdcfdtr_init P((struct parseunit *));
+#define RAWDCFDTR_DESCRIPTION "RAW DCF77 CODE (DTR OPTION)"
+#define RAWDCFDTR_INIT rawdcfdtr_init
+
+/*
+ * RAWDCF receivers that need to be powered from RTS
+ */
+static int rawdcfrts_init P((struct parseunit *));
+#define RAWDCFRTS_DESCRIPTION "RAW DCF77 CODE (RTS OPTION)"
+#define RAWDCFRTS_INIT rawdcfrts_init
+
+/*
+ * 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 loosing 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,
+ RAWDCFDTR_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFDTR_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 loosing 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 */
+ 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
+ },
+ { /* mode 17 */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ RAWDCFRTS_INIT,
+ NO_EVENT,
+ NO_END,
+ NO_MESSAGE,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ RAWDCF_BASEDELAY,
+ DCF_A_ID,
+ RAWDCFRTS_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_SPEED,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG,
+ RAWDCF_SAMPLES,
+ RAWDCF_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->burst = NTP_SHIFT;
+ peer->flags |= FLAG_BURST;
+
+ 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)
+ {
+ /*
+ * 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 = 1;
+
+ 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.sin_addr.s_addr), 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.sin_addr.s_addr), 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;
+ char buffer[512];
+ char *p;
+
+ get_mbg_antinfo(&bufp, &antinfo);
+ sprintf((char *)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((unsigned char **)&p, &antinfo.tm_disconn);
+ *p = '\0';
+ break;
+
+ case ANT_RECONN:
+ strcat(p, "RECONNECTED on ");
+ p += strlen(p);
+ mbg_tm_str((unsigned char **)&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((unsigned char **)&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;
+ char buffer[512];
+ 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((unsigned char **)&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((unsigned char **)&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((unsigned char **)&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(
+ 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
+ **/
+
+/*--------------------------------------------------
+ * rawdcfdtr_init - set up modem lines for RAWDCF receivers
+ */
+#if defined(TIOCMSET) && (defined(TIOCM_DTR) || defined(CIOCM_DTR))
+static int
+rawdcfdtr_init(
+ struct parseunit *parse
+ )
+{
+ /*
+ * 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.
+ */
+
+#ifdef TIOCM_DTR
+ int sl232 = TIOCM_DTR; /* turn on DTR for power supply */
+#else
+ int sl232 = CIOCM_DTR; /* turn on DTR for power supply */
+#endif
+
+ if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_DTR): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+}
+#else
+static int
+rawdcfdtr_init(
+ struct parseunit *parse
+ )
+{
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: OS interface incapable of setting DTR to power DCF modules", CLK_UNIT(parse->peer));
+ return 0;
+}
+#endif /* DTR initialisation type */
+
+/*--------------------------------------------------
+ * rawdcfrts_init - set up modem lines for RAWDCF receivers
+ */
+#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
+static int
+rawdcfrts_init(
+ struct parseunit *parse
+ )
+{
+ /*
+ * You can use the RS232 to supply the power for a DCF77 receiver.
+ * Here a voltage between the RTS and the DTR line is used.
+ */
+
+#ifdef TIOCM_RTS
+ int sl232 = TIOCM_RTS; /* turn on RTS for power supply */
+#else
+ int sl232 = CIOCM_RTS; /* turn on RTS for power supply */
+#endif
+
+ if (ioctl(parse->generic->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
+ {
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_RTS): %m", CLK_UNIT(parse->peer));
+ }
+ return 0;
+}
+#else
+static int
+rawdcfrts_init(
+ struct parseunit *parse
+ )
+{
+ msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: OS interface incapable of setting RTS to power DCF modules", CLK_UNIT(parse->peer));
+ return 0;
+}
+#endif /* RTS initialisation type */
+
+#else /* defined(REFCLOCK) && defined(PARSE) */
+int refclock_parse_bs;
+#endif /* defined(REFCLOCK) && defined(PARSE) */
+
+/*
+ * History:
+ *
+ * refclock_parse.c,v
+ * 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/contrib/ntp/ntpd/refclock_pst.c b/contrib/ntp/ntpd/refclock_pst.c
new file mode 100644
index 0000000..6af4382
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_pst.c
@@ -0,0 +1,318 @@
+/*
+ * 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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.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/pst%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 {
+ u_char 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;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ 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)
+ pp->lastrec = trtmp;
+ up->tcswitch++;
+ pp->lencode = up->lastptr - pp->a_lastcode;
+ if (up->tcswitch < 3)
+ return;
+#ifdef DEBUG
+ if (debug)
+ printf("pst: 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 < 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.%3d%c %9s%3d%13s%4ld",
+ &ampmchar, &pp->hour, &pp->minute, &pp->second,
+ &pp->msec, &daychar, junque, &pp->day,
+ info, &ltemp) != 10) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * 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;
+ pp->disp = PST_PHI * ltemp;
+
+ /*
+ * 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. If nothing is heard from the clock for two polls,
+ * 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);
+ else
+ pp->polls++;
+ 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;
+}
+
+#else
+int refclock_pst_int;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_ptbacts.c b/contrib/ntp/ntpd/refclock_ptbacts.c
new file mode 100644
index 0000000..09d7bf4
--- /dev/null
+++ b/contrib/ntp/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/contrib/ntp/ntpd/refclock_shm.c b/contrib/ntp/ntpd/refclock_shm.c
new file mode 100644
index 0000000..fe05b02
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_shm.c
@@ -0,0 +1,320 @@
+/*
+ * 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)
+
+#undef fileno
+#include <ctype.h>
+#undef fileno
+#include <sys/time.h>
+#undef fileno
+
+#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"
+
+#ifndef SYS_WINNT
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <assert.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.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 unit) {
+#ifndef SYS_WINNT
+ extern char *sys_errlist[ ];
+ extern int sys_nerr;
+ 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 */
+ char buf[20];
+ char *pe=buf;
+ if (errno<sys_nerr)
+ pe=sys_errlist[errno];
+ else {
+ sprintf (buf,"errno=%d",errno);
+ }
+ msyslog(LOG_ERR,"SHM shmget (unit %d): %s",unit,pe);
+ return 0;
+ }
+ else { /* no error */
+ struct shmTime *p=(struct shmTime *)shmat (shmid, 0, 0);
+ if ((int)(long)p==-1) { /* error */
+ char buf[20];
+ char *pe=buf;
+ if (errno<sys_nerr)
+ pe=sys_errlist[errno];
+ else {
+ sprintf (buf,"errno=%d",errno);
+ }
+ msyslog(LOG_ERR,"SHM shmat (unit %d): %s",unit,pe);
+ 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
+ return 0;
+}
+/*
+ * 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
+ 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-172800;
+ 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-172800;
+ 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);
+ }
+ /*msyslog(LOG_NOTICE,"poll2a tvr.s %d tvr.u %d tvt.s %d tvt.u %d",tvr.tv_sec,tvr.tv_usec,tvt.tv_sec,tvt.tv_usec);*/
+ up->valid=0;
+ if (ok) {
+ TVTOTS(&tvr,&pp->lastrec);
+ /* pp->lasttime = current_time; */
+ pp->polls++;
+ t=gmtime (&tvt.tv_sec);
+ pp->day=t->tm_yday;/*+2; */
+ pp->hour=t->tm_hour;
+ pp->minute=t->tm_min;
+ pp->second=t->tm_sec;
+ pp->usec=tvt.tv_usec;
+ 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;
+ }
+ refclock_receive(peer);
+}
+
+#else
+int refclock_shm_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_tpro.c b/contrib/ntp/ntpd/refclock_tpro.c
new file mode 100644
index 0000000..a29383a
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_tpro.c
@@ -0,0 +1,209 @@
+/*
+ * 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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "sys/tpro.h"
+#include "ntp_stdlib.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.
+ */
+ 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->usec)
+ != 5) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ 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;
+ }
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ peer->burst = NSTAGE;
+}
+
+#else
+int refclock_tpro_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_trak.c b/contrib/ntp/ntpd/refclock_trak.c
new file mode 100644
index 0000000..e7833af
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_trak.c
@@ -0,0 +1,361 @@
+/*
+ * 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)
+
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.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;
+ }
+ 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/contrib/ntp/ntpd/refclock_true.c b/contrib/ntp/ntpd/refclock_true.c
new file mode 100644
index 0000000..b841f72
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_true.c
@@ -0,0 +1,852 @@
+/*
+ * 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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.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:
+ * ? +/- 500 milliseconds # +/- 50 milliseconds
+ * * +/- 5 milliseconds . +/- 1 millisecond
+ * space less than 1 millisecond
+ * OM-DC OMEGA Receiver:
+ * > >+- 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[20];
+
+ sprintf(filename, "/tmp/true%d.debug", up->unit);
+ up->debug = fopen(filename, "w");
+ if (up->debug) {
+#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[20];
+ int fd;
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(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 */
+
+ /*
+ * 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.
+ */
+ 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;
+ pp->a_lastcode[pp->lencode] = '\0';
+ 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] == '?') {
+ 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"
+ * (from a TM/TMD clock during initialization.)
+ */
+ if (strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0) {
+ true_doevent(peer, e_F18);
+ NLOG(NLOG_CLOCKSTATUS) {
+ msyslog(LOG_INFO, "TM/TMD: %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
+ */
+ if (synced != ' ' && synced != '.' && synced != '*')
+ pp->leap = LEAP_NOTINSYNC;
+
+ 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;
+ }
+#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;
+ }
+ refclock_receive(peer);
+
+ /*
+ * 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/contrib/ntp/ntpd/refclock_ulink.c b/contrib/ntp/ntpd/refclock_ulink.c
new file mode 100644
index 0000000..347040d
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_ulink.c
@@ -0,0 +1,319 @@
+/*
+ * refclock_ulink - clock driver for Ultralink Model 320 WWVB receivers
+ * By Dave Strout <dstrout@linuxfoundary.com>
+ *
+ * Latest version is always on www.linuxfoundary.com
+ *
+ * Based on the Spectracom driver
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_ULINK)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Ultralink Model 320 WWVB receiver. The Model 320 is
+ * an RS-232 powered unit which consists of two parts: a DB-25 shell that contains
+ * a microprocessor, and an approx 2"x4" plastic box that contains the antenna.
+ * The two are connected by a 6-wire RJ-25 cable of length up to 1000'. The
+ * microprocessor steals power from the RS-232 port, which means that the port must
+ * be kept open all of the time. The unit also has an internal clock for loss of signal
+ * periods. Claimed accuracy is 0.1 sec.
+ *
+ * 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, else '?'
+ * 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' = inset, 'D'=delete
+ * T = DST <-> STD transition indicators
+ *
+ * Note that this driver does not do anything with the L or T flags.
+ *
+ * The M320 also has a 'U' command which returns UT1 correction information. It
+ * is not used in this driver.
+ *
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/ulink%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-13) /* precision assumed (about 100 us) */
+#define REFID "M320" /* reference ID */
+#define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */
+
+#define LENWWVB0 28 /* format 0 timecode length */
+#define LENWWVB2 24 /* format 2 timecode length */
+#define LENWWVB3 29 /* format 3 timecode length */
+
+#define MONLIN 15 /* number of monitoring lines */
+
+/*
+ * ULINK unit control structure
+ */
+struct ulinkunit {
+ 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 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 *));
+static int fd; /* We need to keep the serial port open to power the ULM320 */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_ulink = {
+ ulink_start, /* start up driver */
+ ulink_shutdown, /* shut down driver */
+ ulink_poll, /* transmit poll message */
+ noentry, /* not used (old wwvb_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old wwvb_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * 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;
+ char device[20];
+ fprintf(stderr, "Starting Ulink driver\n");
+ /*
+ * 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->flags |= FLAG_BURST;
+ 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);
+ close(fd);
+}
+
+
+/*
+ * 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 */
+ char syncchar; /* synchronization indicator */
+ char qualchar; /* quality indicator */
+ char modechar; /* Modes: 'R'=rx, 'N'=noise, ' '=standby */
+ char leapchar; /* leap indicator */
+ int temp; /* int temp */
+
+ /*
+ * 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. 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;
+#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. This code uses the timecode length to determine
+ * whether format 0 or format 2. If the timecode has invalid
+ * length or is not in proper format, we declare bad format and
+ * exit.
+ */
+ syncchar = qualchar = leapchar = ' ';
+ pp->msec = 0;
+
+ /*
+ * Timecode format SQRYYYYDDD+HH:MM:SS.mmLT
+ */
+ sscanf(pp->a_lastcode, "%c%c%c%4d%3d%c%2d:%2d:%2d.%2d",
+ &syncchar, &qualchar, &modechar, &pp->year, &pp->day,
+ &leapchar,&pp->hour, &pp->minute, &pp->second,&pp->msec);
+
+ pp->msec *= 10; /* M320 returns 10's of msecs */
+ qualchar = ' ';
+
+ /*
+ * 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.
+ */
+ pp->disp = .001;
+ 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
+ )
+{
+ register struct ulinkunit *up;
+ struct refclockproc *pp;
+ char pollchar;
+
+ pp = peer->procptr;
+ up = (struct ulinkunit *)pp->unitptr;
+ pollchar = 'T';
+ if (write(pp->io.fd, &pollchar, 1) != 1)
+ refclock_report(peer, CEVNT_FAULT);
+ else
+ pp->polls++;
+ 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;
+
+ /*
+ * 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_ulink_bs;
+#endif /* REFCLOCK */
diff --git a/contrib/ntp/ntpd/refclock_usno.c b/contrib/ntp/ntpd/refclock_usno.c
new file mode 100644
index 0000000..cf404e7
--- /dev/null
+++ b/contrib/ntp/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 <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+#include "ntp_control.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.
+ */
+ record_clock_stats(&peer->srcadr, pp->a_lastcode);
+ refclock_receive(peer);
+ 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/contrib/ntp/ntpd/refclock_wwvb.c b/contrib/ntp/ntpd/refclock_wwvb.c
new file mode 100644
index 0000000..9d3b3d6
--- /dev/null
+++ b/contrib/ntp/ntpd/refclock_wwvb.c
@@ -0,0 +1,440 @@
+/*
+ * refclock_wwvb - clock driver for Spectracom WWVB receivers
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(REFCLOCK) && defined(CLOCK_WWVB)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.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 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;
+ peer->burst = NSTAGE;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ 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 */
+
+ /*
+ * 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;
+#ifdef DEBUG
+ if (debug)
+ printf("wwvb: 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 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;
+ pp->msec = 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 %cTZ=%2d",
+ &syncchar, &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &dstchar, &tz) == 7)
+ 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.%3d %c",
+ &syncchar, &qualchar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->msec,
+ &leapchar) == 9)
+ 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);
+ 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;
+ 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);
+ else
+ pp->polls++;
+ 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;
+
+ /*
+ * 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 */
OpenPOWER on IntegriCloud