diff options
Diffstat (limited to 'usr.sbin/xntpd/xntpd')
54 files changed, 28257 insertions, 9718 deletions
diff --git a/usr.sbin/xntpd/xntpd/Makefile.tmpl b/usr.sbin/xntpd/xntpd/Makefile.tmpl index 6a5a2b1f81c3..919d2a0c9174 100644 --- a/usr.sbin/xntpd/xntpd/Makefile.tmpl +++ b/usr.sbin/xntpd/xntpd/Makefile.tmpl @@ -1,5 +1,5 @@ # -# Makefile.tmpl,v 3.1 1993/07/06 01:11:10 jbj Exp +# Makefile.tmpl # PROGRAM= xntpd # @@ -29,24 +29,28 @@ TOP=../ SOURCE= ntp_config.c ntp_control.c ntp_io.c ntp_leap.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_unixclock.c ntp_util.c ntpd.c refclock_chu.c \ - refclock_conf.c refclock_local.c refclock_pst.c \ - refclock_wwvb.c refclock_goes.c refclock_mx4200.c \ - refclock_parse.c refclock_as2201.c refclock_omega.c \ - refclock_tpro.c refclock_leitch.c refclock_irig.c \ - refclock_msfees.c refclock_gpstm.c refclock_trak.c \ - ntp_intres.c ntp_filegen.c + ntp_unixclock.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_goes.c \ + refclock_mx4200.c refclock_parse.c refclock_as2201.c \ + refclock_omega.c refclock_tpro.c refclock_leitch.c \ + refclock_irig.c refclock_msfees.c refclock_gpstm.c \ + refclock_trak.c refclock_datum.c refclock_acts.c \ + refclock_heath.c, refclock_nmea.c refclock_moto.c \ + refclock_atom.c OBJS= ntp_config.o ntp_control.o ntp_io.o ntp_leap.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_unixclock.o ntp_util.o ntpd.o refclock_chu.o \ - refclock_conf.o refclock_local.o refclock_pst.o \ - refclock_wwvb.o refclock_goes.o refclock_mx4200.o \ - refclock_parse.o refclock_as2201.o refclock_omega.o \ - refclock_tpro.o refclock_leitch.o refclock_irig.o \ - refclock_msfees.o refclock_gpstm.o refclock_trak.o \ - ntp_intres.o ntp_filegen.o + ntp_unixclock.o ntp_util.o ntp_intres.o ntp_filegen.o ntpd.o \ + refclock_conf.o refclock_chu.o refclock_local.o \ + refclock_pst.o refclock_wwvb.o refclock_goes.o \ + refclock_mx4200.o refclock_parse.o refclock_as2201.o \ + refclock_omega.o refclock_tpro.o refclock_leitch.o \ + refclock_irig.o refclock_msfees.o refclock_gpstm.o \ + refclock_trak.o refclock_datum.o refclock_acts.o \ + refclock_heath.o refclock_nmea.o refclock_moto.o \ + refclock_atom.o all: $(PROGRAM) @@ -144,3 +148,21 @@ refclock_trak.o: refclock_trak.c refclock_gpstm.o: refclock_gpstm.c $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c + +refclock_datum.o: refclock_datum.c + $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c + +refclock_acts.o: refclock_acts.c + $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c + +refclock_heath.o: refclock_heath.c + $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c + +refclock_nmea.o: refclock_nmea.c + $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c + +refclock_moto.o: refclock_moto.c + $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c + +refclock_atom.o: refclock_atom.c + $(CC) $(COPTS) $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) $(INCL) -c $*.c diff --git a/usr.sbin/xntpd/xntpd/ntp_config.c b/usr.sbin/xntpd/xntpd/ntp_config.c index 8f356ac8c2e7..385e53623f8e 100644 --- a/usr.sbin/xntpd/xntpd/ntp_config.c +++ b/usr.sbin/xntpd/xntpd/ntp_config.c @@ -1,17 +1,12 @@ /* - * ntp_config.c - read and apply configuration information + k ntp_config.c - read and apply configuration information */ -#define RESOLVE_INTERNAL /* gdt */ - #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <signal.h> #include <sys/wait.h> - -#ifdef RESOLVE_INTERNAL -#include <sys/time.h> -#endif +#include <sys/time.h> #include "ntpd.h" #include "ntp_io.h" @@ -42,35 +37,33 @@ /* * We understand the following configuration entries and defaults. * - * peer 128.100.1.1 [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] - * server 128.100.2.2 [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] + * peer [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] + * server [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ] * precision -7 - * broadcast 128.100.224.255 [ version 3 ] [ key 0 ] [ ttl 1 ] + * broadcast [ addr ] [ version 3 ] [ key 0 ] [ ttl 1 ] * broadcastclient * multicastclient [224.0.1.1] * broadcastdelay 0.0102 - * authenticate yes|no - * monitor yes|no + * authenticate yes|no XXX depredated + * monitor yes|no XXX depredated * authdelay 0.00842 - * pps [ delay 0.000247 ] [ baud 38400 ] - * restrict 128.100.100.0 [ mask 255.255.255.0 ] ignore|noserve|notrust|noquery + * 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 ] - * resolver /path/progname * clientlimit [ n ] * clientperiod [ 3600 ] * trustedkey [ key ] * requestkey [ key] * controlkey [ key ] - * trap [ address ] - * fudge [ ... ] + * trap [ addr ] + * fudge [ addr ] [ stratum ] [ refid ] ... * pidfile [ ] - * logfile [ ] * setvar [ ] - * - * And then some. See the manual page. + * enable auth|bclient|pll|pps|monitor|stats + * disable auth|bclient|pll|pps|monitor|stats + * phone ... */ /* @@ -99,13 +92,14 @@ #define CONFIG_STATSDIR 19 #define CONFIG_FILEGEN 20 #define CONFIG_STATISTICS 21 -#define CONFIG_PPS 22 -#define CONFIG_PIDFILE 23 -#define CONFIG_LOGFILE 24 -#define CONFIG_SETVAR 25 -#define CONFIG_CLIENTLIMIT 26 -#define CONFIG_CLIENTPERIOD 27 -#define CONFIG_MULTICASTCLIENT 28 +#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 CONF_MOD_VERSION 1 #define CONF_MOD_KEY 2 @@ -113,9 +107,7 @@ #define CONF_MOD_MAXPOLL 4 #define CONF_MOD_PREFER 5 #define CONF_MOD_TTL 6 - -#define CONF_PPS_DELAY 1 -#define CONF_PPS_BAUD 2 +#define CONF_MOD_MODE 7 #define CONF_RES_MASK 1 #define CONF_RES_IGNORE 2 @@ -134,8 +126,8 @@ #define CONF_FDG_TIME1 1 #define CONF_FDG_TIME2 2 -#define CONF_FDG_VALUE1 3 -#define CONF_FDG_VALUE2 4 +#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 @@ -148,15 +140,6 @@ #define CONF_FGEN_FLAG_ENABLE 5 #define CONF_FGEN_FLAG_DISABLE 6 -#define CONF_BAUD_300 1 -#define CONF_BAUD_600 2 -#define CONF_BAUD_1200 3 -#define CONF_BAUD_2400 4 -#define CONF_BAUD_4800 5 -#define CONF_BAUD_9600 6 -#define CONF_BAUD_19200 7 -#define CONF_BAUD_38400 8 - /* * Translation table - keywords to function index */ @@ -165,6 +148,9 @@ struct keyword { int keytype; }; +/* + * Command keywords + */ static struct keyword keywords[] = { { "peer", CONFIG_PEER }, { "server", CONFIG_SERVER }, @@ -177,7 +163,6 @@ static struct keyword keywords[] = { { "keys", CONFIG_KEYS }, { "monitor", CONFIG_MONITOR }, { "authdelay", CONFIG_AUTHDELAY }, - { "pps", CONFIG_PPS }, { "restrict", CONFIG_RESTRICT }, { "broadcastdelay", CONFIG_BDELAY }, { "trustedkey", CONFIG_TRUSTEDKEY }, @@ -185,20 +170,21 @@ static struct keyword keywords[] = { { "controlkey", CONFIG_CONTROLKEY }, { "trap", CONFIG_TRAP }, { "fudge", CONFIG_FUDGE }, - { "resolver", CONFIG_RESOLVER }, { "statsdir", CONFIG_STATSDIR }, { "filegen", CONFIG_FILEGEN }, { "statistics", CONFIG_STATISTICS }, { "pidfile", CONFIG_PIDFILE }, - { "logfile", CONFIG_LOGFILE }, { "setvar", CONFIG_SETVAR }, { "clientlimit", CONFIG_CLIENTLIMIT }, { "clientperiod", CONFIG_CLIENTPERIOD }, + { "enable", CONFIG_ENABLE }, + { "disable", CONFIG_DISABLE }, + { "phone", CONFIG_PHONE }, { "", CONFIG_UNKNOWN } }; /* - * Modifier keywords + * "peer", "server", "broadcast" modifier keywords */ static struct keyword mod_keywords[] = { { "version", CONF_MOD_VERSION }, @@ -206,21 +192,13 @@ static struct keyword mod_keywords[] = { { "minpoll", CONF_MOD_MINPOLL }, { "maxpoll", CONF_MOD_MAXPOLL }, { "prefer", CONF_MOD_PREFER }, - { "ttl", CONF_MOD_TTL }, - { "", CONFIG_UNKNOWN } -}; - -/* - * PPS modifier keywords - */ -static struct keyword pps_keywords[] = { - { "delay", CONF_PPS_DELAY }, - { "baud", CONF_PPS_BAUD }, + { "mode", CONF_MOD_MODE }, /* reference clocks */ + { "ttl", CONF_MOD_TTL }, /* NTP peers */ { "", CONFIG_UNKNOWN } }; /* - * Special restrict keywords + * "restrict" modifier keywords */ static struct keyword res_keywords[] = { { "mask", CONF_RES_MASK }, @@ -231,29 +209,14 @@ static struct keyword res_keywords[] = { { "nomodify", CONF_RES_NOMODIFY }, { "nopeer", CONF_RES_NOPEER }, { "notrap", CONF_RES_NOTRAP }, - { "lowpriotrap", CONF_RES_LPTRAP }, + { "lowpriotrap", CONF_RES_LPTRAP }, { "ntpport", CONF_RES_NTPPORT }, { "limited", CONF_RES_LIMITED }, { "", CONFIG_UNKNOWN } }; /* - * Baud rate keywords - */ -static struct keyword baud_keywords[] = { - { "300", CONF_BAUD_300 }, - { "600", CONF_BAUD_600 }, - { "1200", CONF_BAUD_1200 }, - { "2400", CONF_BAUD_2400 }, - { "4800", CONF_BAUD_4800 }, - { "9600", CONF_BAUD_9600 }, - { "19200", CONF_BAUD_19200 }, - { "38400", CONF_BAUD_38400 }, - { "", CONFIG_UNKNOWN } -}; - -/* - * Keywords for the trap command + * "trap" modifier keywords */ static struct keyword trap_keywords[] = { { "port", CONF_TRAP_PORT }, @@ -263,13 +226,13 @@ static struct keyword trap_keywords[] = { /* - * Keywords for the fudge command + * "fudge" modifier keywords */ static struct keyword fudge_keywords[] = { { "time1", CONF_FDG_TIME1 }, { "time2", CONF_FDG_TIME2 }, - { "value1", CONF_FDG_VALUE1 }, - { "value2", CONF_FDG_VALUE2 }, + { "stratum", CONF_FDG_STRATUM }, + { "refid", CONF_FDG_REFID }, { "flag1", CONF_FDG_FLAG1 }, { "flag2", CONF_FDG_FLAG2 }, { "flag3", CONF_FDG_FLAG3 }, @@ -279,7 +242,7 @@ static struct keyword fudge_keywords[] = { /* - * Keywords for the filegen command + * "filegen" modifier keywords */ static struct keyword filegen_keywords[] = { { "file", CONF_FGEN_FILE }, @@ -291,6 +254,9 @@ static struct keyword filegen_keywords[] = { { "", CONFIG_UNKNOWN } }; +/* + * "type" modifier keywords + */ static struct keyword fgen_types[] = { { "none", FILEGEN_NONE }, { "pid", FILEGEN_PID }, @@ -302,12 +268,25 @@ static struct keyword fgen_types[] = { { "", CONFIG_UNKNOWN} }; +/* + * "enable", "disable" modifier keywords + */ +static struct keyword flags_keywords[] = { + { "auth", PROTO_AUTHENTICATE }, + { "bclient", PROTO_BROADCLIENT }, + { "pll", PROTO_PLL }, + { "pps", PROTO_PPS }, + { "monitor", PROTO_MONITOR }, + { "stats", PROTO_FILEGEN }, + { "", CONFIG_UNKNOWN } +}; /* * 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 MAXFILENAME 128 /* maximum length of a file name (alloca()?) */ @@ -333,20 +312,21 @@ static char res_file[20]; /* enough for /tmp/xntpXXXXXX\0 */ #ifdef DEBUG extern int debug; #endif -extern char *FindConfig(); - char *progname; -static char *xntp_options = "abc:de:f:k:l:mp:r:s:t:v:V:"; - -static int gettokens P((FILE *, char *, char **, int *)); -static int matchkey P((char *, struct keyword *)); -static int getnetnum P((char *, struct sockaddr_in *, int)); -static void save_resolve P((char *, int, int, int, int, int, int, U_LONG)); -static void do_resolve P((char *, U_LONG, char *)); -#ifdef RESOLVE_INTERNAL -static void do_resolve_internal P((void)); -#endif /* RESOLVE_INTERNAL */ -static void abort_resolve P((void)); -static RETSIGTYPE catchchild P((int)); +extern char *FindConfig(); + char *progname; + char sys_phone[MAXPHONE][MAXDIAL]; /* ACTS phone numbers */ +static char *xntp_options = "abc:dD:e:f:k:l:mp:r:s:t:v:V:"; + +/* + * Function prototypes + */ +static int gettokens P((FILE *, char *, char **, int *)); +static int matchkey P((char *, struct keyword *)); +static int getnetnum P((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)); +static RETSIGTYPE catchchild P((int)); /* * getstartup - search through the options looking for a debugging flag @@ -360,6 +340,7 @@ getstartup(argc, argv) int errflg; int c; extern int ntp_optind; + extern char *ntp_optarg; debug = 0; /* no debugging by default */ @@ -385,6 +366,10 @@ getstartup(argc, argv) case 'd': ++debug; break; + case 'D': + debug = strtol(ntp_optarg, 0, 0); + printf("Debug1: %s -> %x = %d\n", ntp_optarg, debug, debug); + break; case '?': ++errflg; break; @@ -428,7 +413,7 @@ getconfig(argc, argv) int minpoll; int maxpoll; int ttl; - U_LONG peerkey; + u_long peerkey; int peerflags; int hmode; struct sockaddr_in peeraddr; @@ -441,17 +426,12 @@ getconfig(argc, argv) struct interface *localaddr; char *config_file; struct refclockstat clock; - int have_resolver; -#ifdef RESOLVE_INTERNAL - int resolve_internal; -#endif - char resolver_name[MAXFILENAME]; int have_keyfile; char keyfile[MAXFILENAME]; extern int ntp_optind; extern char *ntp_optarg; extern char *Version; - extern U_LONG info_auth_keyid; + extern u_long info_auth_keyid; FILEGEN *filegen; /* @@ -464,7 +444,8 @@ getconfig(argc, argv) config_file = CONFIG_FILE; progname = argv[0]; res_fp = NULL; - have_resolver = have_keyfile = 0; + have_keyfile = 0; + memset((char *)sys_phone, 0, sizeof(sys_phone)); /* * install a non default variable with this daemon version @@ -472,21 +453,17 @@ getconfig(argc, argv) (void) sprintf(line, "daemon_version=\"%s\"", Version); set_sys_var(line, strlen(line)+1, RO); -#ifdef RESOLVE_INTERNAL - resolve_internal = 1; -#endif - /* * Decode argument list */ while ((c = ntp_getopt(argc, argv, xntp_options)) != EOF) { switch (c) { case 'a': - proto_config(PROTO_AUTHENTICATE, (LONG)1); + proto_config(PROTO_AUTHENTICATE, 1); break; case 'b': - proto_config(PROTO_BROADCLIENT, (LONG)1); + proto_config(PROTO_BROADCLIENT, 1); break; case 'c': @@ -500,6 +477,12 @@ getconfig(argc, argv) errflg++; #endif /* DEBUG */ break; + case 'D': +#ifdef DEBUG + debug = strtol(ntp_optarg, 0, 0); + printf("Debug2: %s -> %x = %d\n", ntp_optarg, debug, debug); +#endif /* DEBUG */ + break; case 'e': do { @@ -529,7 +512,7 @@ getconfig(argc, argv) getauthkeys(ntp_optarg); if ((int)strlen(ntp_optarg) >= MAXFILENAME) { syslog(LOG_ERR, - "key file name too LONG (>%d, sigh), no name resolution possible", + "key file name too long (>%d, sigh), no name resolution possible", MAXFILENAME); } else { have_keyfile = 1; @@ -538,7 +521,7 @@ getconfig(argc, argv) break; case 'm': - proto_config(PROTO_MULTICAST_ADD, INADDR_NTP); + proto_config(PROTO_MULTICAST_ADD, htonl(INADDR_NTP)); break; case 'p': @@ -569,22 +552,23 @@ getconfig(argc, argv) case 't': do { - int tkey; + u_long tkey; - tkey = atoi(ntp_optarg); + tkey = atol(ntp_optarg); if (tkey <= 0 || tkey > NTP_MAXKEY) { syslog(LOG_ERR, "command line trusted key %s is unlikely", ntp_optarg); } else { - authtrust(tkey, (LONG)1); + authtrust(tkey, 1); } } while (0); break; case 'v': case 'V': - set_sys_var(ntp_optarg, strlen(ntp_optarg)+1, RW | ((c == 'V') ? DEF : 0)); + set_sys_var(ntp_optarg, strlen(ntp_optarg)+1, + RW | ((c == 'V') ? DEF : 0)); break; default: @@ -622,8 +606,8 @@ getconfig(argc, argv) if (ntokens < 2) { syslog(LOG_ERR, - "No address for %s, line ignored", - tokens[0]); + "No address for %s, line ignored", + tokens[0]); break; } @@ -638,24 +622,24 @@ getconfig(argc, argv) #endif ISBADADR(&peeraddr)) { syslog(LOG_ERR, - "attempt to configure invalid address %s", - ntoa(&peeraddr)); + "attempt to configure invalid address %s", + ntoa(&peeraddr)); break; } } peerversion = NTP_VERSION; minpoll = NTP_MINDPOLL; - maxpoll = NTP_MAXPOLL; + maxpoll = NTP_MAXDPOLL; peerkey = 0; peerflags = 0; - ttl = 1; + ttl = 0; for (i = 2; i < ntokens; i++) switch (matchkey(tokens[i], mod_keywords)) { case CONF_MOD_VERSION: if (i >= ntokens-1) { syslog(LOG_ERR, - "peer/server version requires an argument"); + "peer/server version requires an argument"); errflg = 1; break; } @@ -663,26 +647,20 @@ getconfig(argc, argv) if ((u_char)peerversion > NTP_VERSION || (u_char)peerversion < NTP_OLDVERSION) { syslog(LOG_ERR, - "inappropriate version number %s, line ignored", - tokens[i]); + "inappropriate version number %s, line ignored", + tokens[i]); errflg = 1; } break; case CONF_MOD_KEY: - /* - * XXX - * This is bad because atoi - * returns 0 on errors. Do - * something later. - */ if (i >= ntokens-1) { syslog(LOG_ERR, - "key: argument required"); + "key: argument required"); errflg = 1; break; } - peerkey = (U_LONG)atoi(tokens[++i]); + peerkey = atol(tokens[++i]); peerflags |= FLAG_AUTHENABLE; break; @@ -725,6 +703,16 @@ getconfig(argc, argv) ttl = atoi(tokens[++i]); break; + case CONF_MOD_MODE: + if (i >= ntokens-1) { + syslog(LOG_ERR, + "mode: argument required"); + errflg = 1; + break; + } + ttl = atoi(tokens[++i]); + break; + case CONFIG_UNKNOWN: errflg = 1; break; @@ -753,10 +741,10 @@ getconfig(argc, argv) i = atoi(tokens[1]); if (i >= 0 || i < -25) syslog(LOG_ERR, - "unlikely precision %s, line ignored", - tokens[1]); + "unlikely precision %s, line ignored", + tokens[1]); else - proto_config(PROTO_PRECISION, (LONG)i); + proto_config(PROTO_PRECISION, i); } break; @@ -774,54 +762,28 @@ getconfig(argc, argv) stats_config(STATS_PID_FILE, (char *)0); break; - case CONFIG_LOGFILE: { -#ifdef SYSLOG_FILE - extern int syslogit; - - syslogit = 0; - if (ntokens >= 2) { - FILE *new_file; - new_file = fopen(tokens[1], "a"); - if (new_file != NULL) { - if (syslog_file != NULL) - (void)fclose(syslog_file); - syslog_file = new_file; - } - else - syslog(LOG_ERR, - "Cannot open log file %s", - tokens[1]); - } - else - syslog(LOG_ERR, "logfile needs one argument"); - -#else - syslog(LOG_ERR, "logging to logfile not compiled into xntpd - logfile \"%s\" ignored", (ntokens == 2) ? tokens[1] : ""); -#endif - } break; - case CONFIG_BROADCASTCLIENT: - proto_config(PROTO_BROADCLIENT, (U_LONG)1); + proto_config(PROTO_BROADCLIENT, 1); break; case CONFIG_MULTICASTCLIENT: if (ntokens > 1) { for (i = 1; i < ntokens; i++) { - if (getnetnum(tokens[i], &peeraddr, 1)); + if (getnetnum(tokens[i], &peeraddr, 1)) proto_config(PROTO_MULTICAST_ADD, peeraddr.sin_addr.s_addr); } } else - proto_config(PROTO_MULTICAST_ADD, INADDR_NTP); + proto_config(PROTO_MULTICAST_ADD, htonl(INADDR_NTP)); break; case CONFIG_AUTHENTICATE: errflg = 0; if (ntokens >= 2) { if (STREQ(tokens[1], "yes")) - proto_config(PROTO_AUTHENTICATE, (LONG)1); + proto_config(PROTO_AUTHENTICATE, 1); else if (STREQ(tokens[1], "no")) - proto_config(PROTO_AUTHENTICATE, (LONG)0); + proto_config(PROTO_AUTHENTICATE, 0); else errflg++; } else { @@ -830,7 +792,7 @@ getconfig(argc, argv) if (errflg) syslog(LOG_ERR, - "should be `authenticate yes|no'"); + "should be `authenticate yes|no'"); break; case CONFIG_KEYS: @@ -838,8 +800,8 @@ getconfig(argc, argv) getauthkeys(tokens[1]); if ((int)strlen(tokens[1]) >= MAXFILENAME) { syslog(LOG_ERR, - "key file name too LONG (>%d, sigh), no name resolution possible", - MAXFILENAME); + "key file name too long (>%d, sigh), no name resolution possible", + MAXFILENAME); } else { have_keyfile = 1; (void)strcpy(keyfile, tokens[1]); @@ -862,7 +824,7 @@ getconfig(argc, argv) if (errflg) syslog(LOG_ERR, - "should be `monitor yes|no'"); + "should be `monitor yes|no'"); break; case CONFIG_AUTHDELAY: @@ -871,74 +833,25 @@ getconfig(argc, argv) if (!atolfp(tokens[1], &tmp)) { syslog(LOG_ERR, - "authdelay value %s undecodable", - tokens[1]); + "authdelay value %s undecodable", + tokens[1]); } else if (tmp.l_ui != 0) { syslog(LOG_ERR, - "authdelay value %s is unlikely", - tokens[1]); + "authdelay value %s is unlikely", + tokens[1]); } else { proto_config(PROTO_AUTHDELAY, tmp.l_f); } } break; - case CONFIG_PPS: - for (i = 1 ; i < ntokens ; i++) { - switch(matchkey(tokens[i],pps_keywords)) { - case CONF_PPS_DELAY: - if (i >= ntokens-1) { - syslog(LOG_ERR, - "pps delay requires an argument"); - errflg = 1; - break; - } - { - l_fp tmp; - - if (!atolfp(tokens[++i],&tmp)) { - syslog(LOG_ERR, - "pps delay value %s undecodable", - tokens[i]); - } else { - loop_config(LOOP_PPSDELAY, &tmp, 0); - } - } - break; - case CONF_PPS_BAUD: - if (i >= ntokens-1) { - syslog(LOG_ERR, - "pps baud requires an argument"); - errflg = 1; - break; - } - { - int tmp; - - if (matchkey(tokens[++i],baud_keywords)) { - tmp = atoi(tokens[i]); - if (tmp < 19200) { - syslog(LOG_WARNING, - "pps baud %d unlikely\n", tmp); - } - loop_config(LOOP_PPSBAUD, NULL, tmp); - } - } - break; - case CONFIG_UNKNOWN: - errflg = 1; - break; - } - } - break; - case CONFIG_RESTRICT: if (ntokens < 2) { syslog(LOG_ERR, "restrict requires an address"); break; } if (STREQ(tokens[1], "default")) - peeraddr.sin_addr.s_addr = INADDR_ANY; + peeraddr.sin_addr.s_addr = htonl(INADDR_ANY); else if (!getnetnum(tokens[1], &peeraddr, 1)) break; @@ -954,7 +867,7 @@ getconfig(argc, argv) case CONF_RES_MASK: if (i >= ntokens-1) { syslog(LOG_ERR, - "mask keyword needs argument"); + "mask keyword needs argument"); errflg++; break; } @@ -1008,11 +921,11 @@ getconfig(argc, argv) break; } } - if (SRCADR(&peeraddr) == INADDR_ANY) + if (SRCADR(&peeraddr) == htonl(INADDR_ANY)) maskaddr.sin_addr.s_addr = 0; if (!errflg) restrict(RESTRICT_FLAGS, &peeraddr, &maskaddr, - (int)peerkey, peerversion); + (int)peerkey, peerversion); break; case CONFIG_BDELAY: @@ -1021,12 +934,12 @@ getconfig(argc, argv) if (!atolfp(tokens[1], &tmp)) { syslog(LOG_ERR, - "broadcastdelay value %s undecodable", - tokens[1]); + "broadcastdelay value %s undecodable", + tokens[1]); } else if (tmp.l_ui != 0) { syslog(LOG_ERR, - "broadcastdelay value %s is unlikely", - tokens[1]); + "broadcastdelay value %s is unlikely", + tokens[1]); } else { proto_config(PROTO_BROADDELAY, tmp.l_f); } @@ -1035,13 +948,13 @@ getconfig(argc, argv) case CONFIG_TRUSTEDKEY: for (i = 1; i < ntokens; i++) { - U_LONG tkey; + u_long tkey; - tkey = (U_LONG) atoi(tokens[i]); + tkey = atol(tokens[i]); if (tkey == 0) { syslog(LOG_ERR, - "trusted key %s unlikely", - tokens[i]); + "trusted key %s unlikely", + tokens[i]); } else { authtrust(tkey, 1); } @@ -1050,21 +963,21 @@ getconfig(argc, argv) case CONFIG_REQUESTKEY: if (ntokens >= 2) { - U_LONG rkey; + u_long rkey; if (!atouint(tokens[1], &rkey)) { syslog(LOG_ERR, - "%s is undecodeable as request key", - tokens[1]); + "%s is undecodeable as request key", + tokens[1]); } else if (rkey == 0) { syslog(LOG_ERR, - "%s makes a poor request keyid", - tokens[1]); + "%s makes a poor request keyid", + tokens[1]); } else { #ifdef DEBUG if (debug > 3) printf( - "set info_auth_key to %lu\n", rkey); + "set info_auth_key to %lu\n", rkey); #endif info_auth_keyid = rkey; } @@ -1073,14 +986,14 @@ getconfig(argc, argv) case CONFIG_CONTROLKEY: if (ntokens >= 2) { - U_LONG ckey; - extern U_LONG ctl_auth_keyid; + u_long ckey; + extern u_long ctl_auth_keyid; - ckey = (U_LONG)atoi(tokens[1]); + ckey = atol(tokens[1]); if (ckey == 0) { syslog(LOG_ERR, - "%s makes a poor control keyid", - tokens[1]); + "%s makes a poor control keyid", + tokens[1]); } else { ctl_auth_keyid = ckey; } @@ -1090,7 +1003,7 @@ getconfig(argc, argv) case CONFIG_TRAP: if (ntokens < 2) { syslog(LOG_ERR, - "no address for trap command, line ignored"); + "no address for trap command, line ignored"); break; } if (!getnetnum(tokens[1], &peeraddr, 1)) @@ -1107,7 +1020,7 @@ getconfig(argc, argv) case CONF_TRAP_PORT: if (i >= ntokens-1) { syslog(LOG_ERR, - "trap port requires an argument"); + "trap port requires an argument"); errflg = 1; break; } @@ -1115,7 +1028,7 @@ getconfig(argc, argv) if (peerversion <= 0 || peerversion > 32767) { syslog(LOG_ERR, - "invalid port number %s, trap ignored", + "invalid port number %s, trap ignored", tokens[i]); errflg = 1; } @@ -1124,13 +1037,13 @@ getconfig(argc, argv) case CONF_TRAP_INTERFACE: if (i >= ntokens-1) { syslog(LOG_ERR, - "trap interface requires an argument"); + "trap interface requires an argument"); errflg = 1; break; } if (!getnetnum(tokens[++i], - &maskaddr, 1)) { + &maskaddr, 1)) { errflg = 1; break; } @@ -1138,8 +1051,8 @@ getconfig(argc, argv) localaddr = findinterface(&maskaddr); if (localaddr == NULL) { syslog(LOG_ERR, - "can't find interface with address %s", - ntoa(&maskaddr)); + "can't find interface with address %s", + ntoa(&maskaddr)); errflg = 1; } break; @@ -1159,17 +1072,17 @@ getconfig(argc, argv) if (localaddr == NULL) localaddr = any_interface; if (!ctlsettrap(&peeraddr, localaddr, 0, - NTP_VERSION)) + NTP_VERSION)) syslog(LOG_ERR, - "can't set trap for %s, no resources", - ntoa(&peeraddr)); + "can't set trap for %s, no resources", + ntoa(&peeraddr)); } break; case CONFIG_FUDGE: if (ntokens < 2) { syslog(LOG_ERR, - "no address for fudge command, line ignored"); + "no address for fudge command, line ignored"); break; } if (!getnetnum(tokens[1], &peeraddr, 1)) @@ -1177,8 +1090,8 @@ getconfig(argc, argv) if (!ISREFCLOCKADR(&peeraddr)) { syslog(LOG_ERR, - "%s is inappropriate address for the fudge command, line ignored", - ntoa(&peeraddr)); + "%s is inappropriate address for the fudge command, line ignored", + ntoa(&peeraddr)); break; } @@ -1186,13 +1099,13 @@ getconfig(argc, argv) errflg = 0; for (i = 2; i < ntokens-1; i++) { switch (c = matchkey(tokens[i], - fudge_keywords)) { + fudge_keywords)) { case CONF_FDG_TIME1: if (!atolfp(tokens[++i], - &clock.fudgetime1)) { + &clock.fudgetime1)) { syslog(LOG_ERR, - "fudge %s time1 value in error", - ntoa(&peeraddr)); + "fudge %s time1 value in error", + ntoa(&peeraddr)); errflg = i; break; } @@ -1201,40 +1114,34 @@ getconfig(argc, argv) case CONF_FDG_TIME2: if (!atolfp(tokens[++i], - &clock.fudgetime2)) { + &clock.fudgetime2)) { syslog(LOG_ERR, - "fudge %s time2 value in error", - ntoa(&peeraddr)); + "fudge %s time2 value in error", + ntoa(&peeraddr)); errflg = i; break; } clock.haveflags |= CLK_HAVETIME2; break; - case CONF_FDG_VALUE1: + case CONF_FDG_STRATUM: if (!atoint(tokens[++i], - &clock.fudgeval1)) { + (long *)&clock.fudgeval1)) { syslog(LOG_ERR, - "fudge %s value1 value in error", - ntoa(&peeraddr)); + "fudge %s stratum value in error", + ntoa(&peeraddr)); errflg = i; break; } clock.haveflags |= CLK_HAVEVAL1; break; - case CONF_FDG_VALUE2: - if (!atoint(tokens[++i], - &clock.fudgeval2)) { - syslog(LOG_ERR, - "fudge %s value2 value in error", - ntoa(&peeraddr)); - errflg = i; - break; - } + case CONF_FDG_REFID: + strncpy((char *)&clock.fudgeval2, + tokens[++i], 4); clock.haveflags |= CLK_HAVEVAL2; break; - + case CONF_FDG_FLAG1: case CONF_FDG_FLAG2: case CONF_FDG_FLAG3: @@ -1242,8 +1149,8 @@ getconfig(argc, argv) if (!atouint(tokens[++i], &peerkey) || peerkey > 1) { syslog(LOG_ERR, - "fudge %s flag value in error", - ntoa(&peeraddr)); + "fudge %s flag value in error", + ntoa(&peeraddr)); errflg = i; break; } @@ -1285,27 +1192,11 @@ getconfig(argc, argv) */ if (!errflg) { refclock_control(&peeraddr, &clock, - (struct refclockstat *)0); + (struct refclockstat *)0); } #endif break; - case CONFIG_RESOLVER: - if (ntokens >= 2) { - if (strlen(tokens[1]) >= (size_t)MAXFILENAME) { - syslog(LOG_ERR, - "resolver path name too LONG (>%d, sigh), no name resolution possible", - MAXFILENAME); - break; - } - strcpy(resolver_name, tokens[1]); - have_resolver = 1; -#ifdef RESOLVE_INTERNAL - resolve_internal = 0; -#endif - } - break; - case CONFIG_STATSDIR: if (ntokens >= 2) { stats_config(STATS_STATSDIR,tokens[1]); @@ -1318,14 +1209,14 @@ getconfig(argc, argv) if (filegen == NULL) { syslog(LOG_ERR, - "no statistics named %s available", - tokens[i]); + "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); + tokens[i], filegen->prefix, filegen->basename); #endif filegen->flag |= FGEN_FLAG_ENABLED; } @@ -1334,15 +1225,15 @@ getconfig(argc, argv) case CONFIG_FILEGEN: if (ntokens < 2) { syslog(LOG_ERR, - "no id for filegen command, line ignored"); + "no id for filegen command, line ignored"); break; } filegen = filegen_get(tokens[1]); if (filegen == NULL) { syslog(LOG_ERR, - "unknown filegen \"%s\" ignored", - tokens[1]); + "unknown filegen \"%s\" ignored", + tokens[1]); break; } /* @@ -1360,8 +1251,8 @@ getconfig(argc, argv) case CONF_FGEN_FILE: if (i >= ntokens - 1) { syslog(LOG_ERR, - "filegen %s file requires argument", - tokens[1]); + "filegen %s file requires argument", + tokens[1]); errflg = i; break; } @@ -1370,16 +1261,16 @@ getconfig(argc, argv) case CONF_FGEN_TYPE: if (i >= ntokens -1) { syslog(LOG_ERR, - "filegen %s type requires argument", - tokens[1]); + "filegen %s type requires argument", + tokens[1]); errflg = i; break; } peerkey = matchkey(tokens[++i], fgen_types); if (peerkey == CONFIG_UNKNOWN) { syslog(LOG_ERR, - "filegen %s unknown type \"%s\"", - tokens[1], tokens[i]); + "filegen %s unknown type \"%s\"", + tokens[1], tokens[i]); errflg = i; break; } @@ -1409,70 +1300,98 @@ getconfig(argc, argv) break; case CONFIG_SETVAR: - if (ntokens < 2) - { - syslog(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)); - } + if (ntokens < 2) { + syslog(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) - { - syslog(LOG_ERR, - "no value for clientlimit command - line ignored"); - } - else - { - U_LONG i; - if (!atouint(tokens[1], &i) || !i) - { + if (ntokens < 2) { syslog(LOG_ERR, - "illegal value for clientlimit command - line ignored"); - } - else - { - extern U_LONG client_limit; - char bp[80]; - - sprintf(bp, "client_limit=%d", i); - set_sys_var(bp, strlen(bp)+1, RO); - - client_limit = i; - } - } + "no value for clientlimit command - line ignored"); + } else { + u_long i; + if (!atouint(tokens[1], &i) || !i) { + syslog(LOG_ERR, + "illegal value for clientlimit command - line ignored"); + } else { + extern u_long client_limit; + char bp[80]; + +#ifdef DEBUG + if (debug) + sprintf(bp, "client_limit=%lu", i); +#endif + set_sys_var(bp, strlen(bp)+1, RO); + client_limit = i; + } + } break; case CONFIG_CLIENTPERIOD: - if (ntokens < 2) - { - syslog(LOG_ERR, - "no value for clientperiod command - line ignored"); - } - else - { - U_LONG i; - if (!atouint(tokens[1], &i) || i < 64) - { + if (ntokens < 2) { syslog(LOG_ERR, - "illegal value for clientperiod command - line ignored"); - } - else - { - extern U_LONG client_limit_period; - char bp[80]; - - sprintf(bp, "client_limit_period=%d", i); - set_sys_var(bp, strlen(bp)+1, RO); - - client_limit_period = i; - } - } + "no value for clientperiod command - line ignored"); + } else { + u_long i; + + if (!atouint(tokens[1], &i) || i < 64) { + syslog(LOG_ERR, + "illegal value for clientperiod command - line ignored"); + } else { + extern u_long client_limit_period; + char bp[80]; + + sprintf(bp, "client_limit_period=%ld", i); + set_sys_var(bp, strlen(bp)+1, RO); + client_limit_period = i; + } + } + break; + + case CONFIG_ENABLE: + for (i = 1; i < ntokens; i++) { + int flag; + + flag = matchkey(tokens[i], flags_keywords); + if (flag == CONFIG_UNKNOWN) { + syslog(LOG_ERR, + "enable unknown flag %s", + tokens[i]); + errflg = 1; + break; + } + proto_config(flag, 1L); + } + break; + + case CONFIG_DISABLE: + for (i = 1; i < ntokens; i++) { + int flag; + + flag = matchkey(tokens[i], flags_keywords); + if (flag == CONFIG_UNKNOWN) { + syslog(LOG_ERR, + "disable unknown flag %s", + tokens[i]); + errflg = 1; + break; + } + proto_config(flag, 0L); + } + 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; } } @@ -1482,38 +1401,7 @@ getconfig(argc, argv) /* * Need name resolution */ - errflg = 0; -#ifdef RESOLVE_INTERNAL - if ( resolve_internal ) - do_resolve_internal(); - else - { -#endif - - if (info_auth_keyid == 0) { - syslog(LOG_ERR, - "no request key defined, peer name resolution not possible"); - errflg++; - } - if (!have_resolver) { - syslog(LOG_ERR, - "no resolver defined, peer name resolution not possible"); - errflg++; - } - if (!have_keyfile) { - syslog(LOG_ERR, - "no key file specified, peer name resolution not possible"); - errflg++; - } - - if (!errflg) - - do_resolve(resolver_name, info_auth_keyid, keyfile); - else - abort_resolve(); -#ifdef RESOLVE_INTERNAL - } -#endif + do_resolve_internal(); } } @@ -1622,7 +1510,7 @@ getnetnum(num, addr, complain) register int i; register int temp; char buf[80]; /* will core dump on really stupid stuff */ - U_LONG netnum; + u_long netnum; /* XXX ELIMINATE replace with decodenetnum */ cp = num; @@ -1648,7 +1536,7 @@ getnetnum(num, addr, complain) netnum += temp; #ifdef DEBUG if (debug > 3) - printf("getnetnum %s step %d buf %s temp %d netnum %d\n", + printf("getnetnum %s step %d buf %s temp %d netnum %lu\n", num, i, buf, temp, netnum); #endif } @@ -1676,7 +1564,7 @@ getnetnum(num, addr, complain) addr->sin_addr.s_addr = htonl(netnum); #ifdef DEBUG if (debug > 1) - printf("getnetnum given %s, got %s (%x)\n", + printf("getnetnum given %s, got %s (%lx)\n", num, ntoa(addr), netnum); #endif return 1; @@ -1711,7 +1599,7 @@ save_resolve(name, mode, version, minpoll, maxpoll, flags, ttl, keyid) int maxpoll; int flags; int ttl; - U_LONG keyid; + u_long keyid; { if (res_fp == NULL) { (void) strcpy(res_file, RES_TEMPFILE); @@ -1754,186 +1642,76 @@ abort_resolve() } +#define KEY_TYPE_ASCII 3 + /* - * do_resolve - start up the resolver program + * do_resolve_internal - start up the resolver function (not program) */ static void -do_resolve(program, auth_keyid, keyfile) - char *program; - U_LONG auth_keyid; - char *keyfile; +do_resolve_internal() { - register LONG i; - register char **ap; - /* 1 progname + 5 -d's + 1 -r + keyid + keyfile + tempfile + 1 */ - char *argv[15]; - char numbuf[15]; - /* - * Clean environment so the resolver is consistant - */ - static char *resenv[] = { - "HOME=/", - "SHELL=/bin/sh", - "TERM=dumb", - "USER=root", - NULL - }; + int i; + + extern u_long req_keyid; /* request keyid */ + extern char *req_file; /* name of the file with config info */ + extern u_long info_auth_keyid; if (res_fp == NULL) { /* belch */ - syslog(LOG_ERR, "internal error in do_resolve: res_fp == NULL"); + syslog(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; - ap = argv; - *ap++ = program; - - /* - * xntpres [-d ...] -r key# keyfile tempfile - */ -#ifdef DEBUG - i = debug; - if (i > 5) - i = 5; - while (i-- > 0) - *ap++ = "-d"; -#endif - *ap++ = "-r"; - - (void) sprintf(numbuf, "%lu", auth_keyid); - *ap++ = numbuf; - *ap++ = keyfile; - *ap++ = res_file; - *ap = NULL; + /* find a keyid */ + if (info_auth_keyid == 0) + req_keyid = 65535; + else + req_keyid = info_auth_keyid; + + /* if doesn't exist, make up one at random */ + if (!authhavekey(req_keyid)) { + char rankey[9]; + struct timeval now; + + /* generate random key */ + GETTIMEOFDAY(&now, (struct timezone *)0); + srand(now.tv_sec * now.tv_usec); + for (i = 0; i < 8; i++) + rankey[i] = (rand() % 255) + 1; + rankey[8] = 0; + authusekey(req_keyid, KEY_TYPE_ASCII, rankey); + } + /* 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 */ (void) signal_no_reset(SIGCHLD, catchchild); i = fork(); if (i == 0) { /* - * In child here, close up all descriptors and - * exec the resolver program. Close the syslog() - * facility gracefully in case we must reopen it. + * this used to close everything + * I don't think this is necessary */ - (void) signal(SIGCHLD, SIG_DFL); - closelog(); -#if defined(NTP_POSIX_SOURCE) && !defined(SYS_386BSD) - i = sysconf(_SC_OPEN_MAX); -#else - i = getdtablesize(); -#endif -#ifdef DEBUG - while (i-- > 2) -#else - while (i-- > 0) -#endif - (void) close(i); - (void) execve(program, argv, resenv); + (void) signal_no_reset(SIGCHLD, SIG_DFL); + ntp_intres(); /* - * If we got here, the exec screwed up. Open the log file - * and print something so we don't die without complaint + * If we got here, the intres code screwed up. + * Print something so we don't die without complaint */ -#ifndef LOG_DAEMON - openlog("xntpd", LOG_PID); -#else -#ifndef LOG_NTP -#define LOG_NTP LOG_DAEMON -#endif - openlog("xntpd", LOG_PID | LOG_NDELAY, LOG_NTP); -#endif /* LOG_DAEMON */ - syslog(LOG_ERR, "exec of resolver %s failed!", program); + syslog(LOG_ERR, "call to ntp_intres lost"); abort_resolve(); exit(1); } - if (i == -1) { - syslog(LOG_ERR, "fork() failed, can't start %s", program); + syslog(LOG_ERR, "fork() failed, can't start ntp_intres"); (void) signal_no_reset(SIGCHLD, SIG_DFL); abort_resolve(); } } - - -#ifdef RESOLVE_INTERNAL - -#define KEY_TYPE_ASCII 3 - -/* - * do_resolve_internal - start up the resolver function (not program) - */ -static void -do_resolve_internal() -{ - int i; - - extern U_LONG req_keyid; /* request keyid */ - extern char *req_file; /* name of the file with configuration info */ - extern U_LONG info_auth_keyid; - - if (res_fp == NULL) { - /* belch */ - syslog(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; - - /* find a keyid */ - if (info_auth_keyid == 0) - req_keyid = 65535; - else - req_keyid = info_auth_keyid; - - /* if doesn't exist, make up one at random */ - if ( ! authhavekey(req_keyid) ) - { - char rankey[9]; - struct timeval now; - - /* generate random key */ - GETTIMEOFDAY(&now, (struct timezone *)0); - srand(now.tv_sec * now.tv_usec); - - for ( i = 0; i < 8; i++ ) - rankey[i] = (rand() % 255) + 1; - rankey[8] = 0; - - authusekey(req_keyid, KEY_TYPE_ASCII, rankey); - } - - /* 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 */ - - (void) signal_no_reset(SIGCHLD, catchchild); - - i = fork(); - if (i == 0) { - /* this used to close everything - * I don't think this is necessary */ - (void) signal_no_reset(SIGCHLD, SIG_DFL); - - ntp_intres(); - - /* - * If we got here, the intres code screwed up. - * Print something so we don't die without complaint - */ - syslog(LOG_ERR, "call to ntp_intres lost"); - abort_resolve(); - exit(1); - } - - if (i == -1) { - syslog(LOG_ERR, "fork() failed, can't start ntp_intres"); - (void) signal_no_reset(SIGCHLD, SIG_DFL); - abort_resolve(); - } -} -#endif diff --git a/usr.sbin/xntpd/xntpd/ntp_control.c b/usr.sbin/xntpd/xntpd/ntp_control.c index ef9c37a6751d..0f1dc2b4d494 100644 --- a/usr.sbin/xntpd/xntpd/ntp_control.c +++ b/usr.sbin/xntpd/xntpd/ntp_control.c @@ -3,11 +3,12 @@ */ #include <stdio.h> #include <ctype.h> -#include <signal.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" @@ -41,18 +42,13 @@ static void ctl_flushpkt P((int)); static void ctl_putdata P((char *, int, int)); static void ctl_putstr P((char *, char *, int)); static void ctl_putlfp P((char *, l_fp *)); - -#ifdef UNUSED -static void ctl_putulfp P((char *, l_fp *)); -#endif /* UNUSED */ - static void ctl_putfp P((char *, s_fp)); static void ctl_putufp P((char *, u_fp)); -static void ctl_putuint P((char *, U_LONG)); -static void ctl_puthex P((char *, U_LONG)); -static void ctl_putint P((char *, LONG)); +static void ctl_putuint P((char *, u_long)); +static void ctl_puthex P((char *, u_long)); +static void ctl_putint P((char *, long)); static void ctl_putts P((char *, l_fp *)); -static void ctl_putadr P((char *, U_LONG)); +static void ctl_putadr P((char *, u_long)); static void ctl_putid P((char *, char *)); static void ctl_putarray P((char *, s_fp *, int)); static void ctl_putsys P((int)); @@ -61,7 +57,7 @@ static void ctl_putpeer P((int, struct peer *)); static void ctl_putclock P((int, struct refclockstat *, int)); #endif /* REFCLOCK */ static struct ctl_var *ctl_getitem P((struct ctl_var *, char **)); -static unsigned long count_var P((struct ctl_var *)); +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)); @@ -101,7 +97,7 @@ static struct ctl_var sys_var[] = { { CS_PEERID, RO, "peer" }, /* 9 */ { CS_OFFSET, RO, "phase" }, /* 10 */ { CS_DRIFT, RO, "freq" }, /* 11 */ - { CS_COMPLIANCE, RO, "compliance" }, /* 12 */ + { CS_COMPLIANCE, RO, "error" }, /* 12 */ { CS_CLOCK, RO, "clock" }, /* 13 */ { CS_LEAPIND, RW, "leapindicator" }, /* 14 */ { CS_LEAPWARNING, RW, "leapwarning" }, /* 15 */ @@ -231,8 +227,8 @@ static struct ctl_var clock_var[] = { { CC_BADDATA, RO, "baddata" }, /* 6 */ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */ - { CC_FUDGEVAL1, RO, "fudgeval1" }, /* 9 */ - { CC_FUDGEVAL2, RO, "fudgeval2" }, /* 10 */ + { 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 */ @@ -304,30 +300,36 @@ static struct utsname utsname; * set peer->sstclktype to something different than CTL_SST_TS_UNSPEC. */ static u_char clocktypes[] = { - CTL_SST_TS_NTP, /* REFCLK_NONE */ - CTL_SST_TS_UNSPEC, /* REFCLK_LOCALCLOCK */ - CTL_SST_TS_UHF, /* REFCLK_GPS_TRAK */ - CTL_SST_TS_HF, /* REFCLK_WWV_PST */ - CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM */ - CTL_SST_TS_UHF, /* REFCLK_GOES_TRUETIME */ - CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK */ - CTL_SST_TS_HF, /* REFCLK_CHU */ + 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_GOES_TRUETIME (5) */ + CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK (6) */ + CTL_SST_TS_HF, /* REFCLK_CHU (7) */ CTL_SST_TS_LF, /* REFCLOCK_PARSE - default value - driver supplies actual value in peer->sstclktype */ - CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM_HP */ - CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 */ - CTL_SST_TS_LF, /* REFCLK_OMEGA_TRUETIME */ - CTL_SST_TS_UNSPEC, /* Future expansion */ - CTL_SST_TS_UNSPEC, /* Future expansion */ - CTL_SST_TS_UNSPEC, /* Future expansion */ - CTL_SST_TS_UNSPEC /* Future expansion */ + CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */ + CTL_SST_TS_LF, /* REFCLK_OMEGA_TRUETIME (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_GPSTM_TRUETIME (15) */ + CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */ + CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACT (18) */ + CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_MOTO (21) */ + CTL_SST_TS_ATOM /* REFCLK_ATOM_PPS (22) */ }; - /* * Keyid used for authenticating write requests. */ -U_LONG ctl_auth_keyid; +u_long ctl_auth_keyid; /* * We keep track of the last error reported by the system internally @@ -339,21 +341,21 @@ 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 */ +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 */ /* * Imported from the I/O module @@ -368,7 +370,7 @@ extern int debug; /* * Imported from the timer module */ -extern U_LONG current_time; +extern u_long current_time; extern struct peer *assoc_hash[]; extern int pps_control; /* flag for 1-pps signal present */ @@ -380,7 +382,7 @@ extern u_char sys_stratum; extern s_char sys_precision; extern s_fp sys_rootdelay; extern u_fp sys_rootdispersion; -extern U_LONG sys_refid; +extern u_long sys_refid; extern l_fp sys_reftime; extern l_fp sys_refskew; extern u_char sys_poll; @@ -390,7 +392,7 @@ extern struct peer *sys_peer; */ extern l_fp last_offset; extern s_fp drift_comp; -extern int time_constant; +extern u_fp sys_maxd; extern int pll_control; /* * Imported from the leap module @@ -419,7 +421,7 @@ static struct interface *lcl_inter; static u_char res_authenticate; static u_char res_authokay; -static U_LONG res_keyid; +static u_long res_keyid; #define MAXDATALINELEN (72) @@ -480,14 +482,14 @@ ctl_error(errcode) if (res_authenticate) { int maclen; - *(U_LONG *)((u_char *)&rpkt + CTL_HEADER_LEN) + *(u_long *)((u_char *)&rpkt + CTL_HEADER_LEN) = htonl(res_keyid); maclen = authencrypt(res_keyid, (U_LONG *)&rpkt, CTL_HEADER_LEN); - sendpkt(rmt_addr, lcl_inter, (struct pkt *)&rpkt, + sendpkt(rmt_addr, lcl_inter, -2, (struct pkt *)&rpkt, CTL_HEADER_LEN + maclen); } else { - sendpkt(rmt_addr, lcl_inter, (struct pkt *)&rpkt, + sendpkt(rmt_addr, lcl_inter, -3, (struct pkt *)&rpkt, CTL_HEADER_LEN); } numctlerrors++; @@ -596,17 +598,17 @@ process_control(rbufp, restrict) properlen = (properlen + 7) & ~7; - if ((rbufp->recv_length & (sizeof(U_LONG)-1)) == 0 + if ((rbufp->recv_length & (sizeof(u_long)-1)) == 0 && (maclen = (rbufp->recv_length - properlen)) >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN) { res_authenticate = 1; - res_keyid = ntohl(*(U_LONG *)((u_char *)pkt + properlen)); + res_keyid = ntohl(*(u_long *)((u_char *)pkt + properlen)); #ifdef DEBUG if (debug >= 3) printf( - "recv_len %d, properlen %d, wants auth with keyid %d, MAC length=%d\n", + "recv_len %d, properlen %d, wants auth with keyid %ld, MAC length=%d\n", rbufp->recv_length, properlen, res_keyid, maclen); #endif if (!authhavekey(res_keyid)) { @@ -728,15 +730,18 @@ ctlsysstatus() register u_char clock; clock = CTL_SST_TS_UNSPEC; - if (sys_peer != 0) - if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) + if (sys_peer != 0) { + if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) { clock = sys_peer->sstclktype; - else { + if (pps_control) + clock |= CTL_SST_TS_PPS; + } else { if (sys_peer->refclktype < sizeof(clocktypes)) clock = clocktypes[sys_peer->refclktype]; if (pps_control) clock |= CTL_SST_TS_PPS; } + } return (u_short)CTL_SYS_STATUS(sys_leap, clock, ctl_sys_num_events, ctl_sys_last_event); } @@ -788,6 +793,7 @@ ctl_flushpkt(more) 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++; @@ -808,14 +814,14 @@ ctl_flushpkt(more) *datapt++ = '\0'; totlen++; } - *(U_LONG *)datapt = htonl(res_keyid); + *(u_long *)datapt = htonl(res_keyid); maclen = authencrypt(res_keyid, (U_LONG *)&rpkt, totlen); - sendpkt(rmt_addr, lcl_inter, (struct pkt *)&rpkt, + sendpkt(rmt_addr, lcl_inter, -5, (struct pkt *)&rpkt, totlen + maclen); } else { - sendpkt(rmt_addr, lcl_inter, (struct pkt *)&rpkt, + sendpkt(rmt_addr, lcl_inter, -6, (struct pkt *)&rpkt, sendlen); } if (more) @@ -935,33 +941,6 @@ ctl_putlfp(tag, ts) } -#ifdef UNUSED -/* - * ctl_putlfp - write a tagged, unsigned l_fp into the response - */ -static void -ctl_putulfp(tag, ts) - char *tag; - l_fp *ts; -{ - register char *cp, *cq; - char buffer[200]; - - cp = buffer; - cq = tag; - while (*cq != '\0') - *cp++ = *cq++; - - *cp++ = '='; - cq = ulfptoms(ts, 3); - while (*cq != '\0') - *cp++ = *cq++; - - ctl_putdata(buffer, cp - buffer, 0); -} -#endif /* UNUSED */ - - /* * ctl_putfp - write a tagged s_fp number into the response */ @@ -1018,7 +997,7 @@ ctl_putufp(tag, ufp) static void ctl_putuint(tag, uval) char *tag; - U_LONG uval; + u_long uval; { register char *cp, *cq; char buffer[200]; @@ -1029,7 +1008,7 @@ ctl_putuint(tag, uval) *cp++ = *cq++; *cp++ = '='; - (void) sprintf(cp, "%u", uval); + (void) sprintf(cp, "%lu", uval); while (*cp != '\0') cp++; @@ -1043,7 +1022,7 @@ ctl_putuint(tag, uval) static void ctl_puthex(tag, uval) char *tag; - U_LONG uval; + u_long uval; { register char *cp, *cq; char buffer[200]; @@ -1068,7 +1047,7 @@ ctl_puthex(tag, uval) static void ctl_putint(tag, ival) char *tag; - LONG ival; + long ival; { register char *cp, *cq; char buffer[200]; @@ -1079,7 +1058,7 @@ ctl_putint(tag, ival) *cp++ = *cq++; *cp++ = '='; - (void) sprintf(cp, "%d", ival); + (void) sprintf(cp, "%ld", ival); while (*cp != '\0') cp++; @@ -1119,7 +1098,7 @@ ctl_putts(tag, ts) static void ctl_putadr(tag, addr) char *tag; - U_LONG addr; + u_long addr; { register char *cp, *cq; char buffer[200]; @@ -1225,13 +1204,13 @@ ctl_putsys(varid) switch (varid) { case CS_LEAP: - ctl_putuint(sys_var[CS_LEAP].text, (U_LONG)sys_leap); + ctl_putuint(sys_var[CS_LEAP].text, sys_leap); break; case CS_STRATUM: - ctl_putuint(sys_var[CS_STRATUM].text, (U_LONG)sys_stratum); + ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum); break; case CS_PRECISION: - ctl_putint(sys_var[CS_PRECISION].text, (LONG)sys_precision); + ctl_putint(sys_var[CS_PRECISION].text, sys_precision); break; case CS_ROOTDELAY: ctl_putfp(sys_var[CS_ROOTDELAY].text, sys_rootdelay); @@ -1241,23 +1220,23 @@ ctl_putsys(varid) sys_rootdispersion); break; case CS_REFID: - if (sys_stratum <= 1) - ctl_putid(sys_var[CS_REFID].text, (char *)&sys_refid); - else + 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, (U_LONG)sys_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, (U_LONG)0); + ctl_putuint(sys_var[CS_PEERID].text, 0); else ctl_putuint(sys_var[CS_PEERID].text, - (U_LONG)sys_peer->associd); + sys_peer->associd); break; case CS_OFFSET: ctl_putlfp(sys_var[CS_OFFSET].text, &last_offset); @@ -1266,17 +1245,17 @@ ctl_putsys(varid) ctl_putfp(sys_var[CS_DRIFT].text, drift_comp); break; case CS_COMPLIANCE: - ctl_putuint(sys_var[CS_COMPLIANCE].text, (U_LONG)time_constant); + ctl_putufp(sys_var[CS_COMPLIANCE].text, sys_maxd); break; case CS_CLOCK: get_systime(&tmp); ctl_putts(sys_var[CS_CLOCK].text, &tmp); break; case CS_LEAPIND: - ctl_putuint(sys_var[CS_LEAPIND].text, (U_LONG)leap_indicator); + ctl_putuint(sys_var[CS_LEAPIND].text, leap_indicator); break; case CS_LEAPWARNING: - ctl_putuint(sys_var[CS_LEAPWARNING].text, (U_LONG)leap_warning); + ctl_putuint(sys_var[CS_LEAPWARNING].text, leap_warning); break; case CS_PROCESSOR: #ifndef HAVE_UNAME @@ -1297,7 +1276,7 @@ ctl_putsys(varid) #endif /* HAVE_UNAME */ break; case CS_KEYID: - ctl_putuint(sys_var[CS_KEYID].text, (U_LONG)0); + ctl_putuint(sys_var[CS_KEYID].text, 0); break; case CS_REFSKEW: ctl_putlfp(sys_var[CS_REFSKEW].text, &sys_refskew); @@ -1378,15 +1357,15 @@ ctl_putpeer(varid, peer) switch (varid) { case CP_CONFIG: ctl_putuint(peer_var[CP_CONFIG].text, - (U_LONG)((peer->flags & FLAG_CONFIG) != 0)); + ((peer->flags & FLAG_CONFIG) != 0)); break; case CP_AUTHENABLE: ctl_putuint(peer_var[CP_AUTHENABLE].text, - (U_LONG)((peer->flags & FLAG_AUTHENABLE) != 0)); + ((peer->flags & FLAG_AUTHENABLE) != 0)); break; case CP_AUTHENTIC: ctl_putuint(peer_var[CP_AUTHENTIC].text, - (U_LONG)((peer->flags & FLAG_AUTHENTIC) != 0)); + ((peer->flags & FLAG_AUTHENTIC) != 0)); break; case CP_SRCADR: ctl_putadr(peer_var[CP_SRCADR].text, @@ -1394,33 +1373,40 @@ ctl_putpeer(varid, peer) break; case CP_SRCPORT: ctl_putuint(peer_var[CP_SRCPORT].text, - (U_LONG)ntohs(peer->srcadr.sin_port)); + ntohs(peer->srcadr.sin_port)); break; case CP_DSTADR: ctl_putadr(peer_var[CP_DSTADR].text, - peer->dstadr->sin.sin_addr.s_addr); + 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)ntohs(peer->dstadr->sin.sin_port)); + ntohs(peer->dstadr->sin.sin_port)); break; case CP_LEAP: - ctl_putuint(peer_var[CP_LEAP].text, (U_LONG)peer->leap); + ctl_putuint(peer_var[CP_LEAP].text, peer->leap); break; case CP_HMODE: - ctl_putuint(peer_var[CP_HMODE].text, (U_LONG)peer->hmode); + ctl_putuint(peer_var[CP_HMODE].text, peer->hmode); break; case CP_STRATUM: - ctl_putuint(peer_var[CP_STRATUM].text, (U_LONG)peer->stratum); + ctl_putuint(peer_var[CP_STRATUM].text, peer->stratum); break; case CP_PPOLL: - ctl_putuint(peer_var[CP_PPOLL].text, (U_LONG)peer->ppoll); + ctl_putuint(peer_var[CP_PPOLL].text, peer->ppoll); break; case CP_HPOLL: - ctl_putuint(peer_var[CP_HPOLL].text, (U_LONG)peer->hpoll); + ctl_putuint(peer_var[CP_HPOLL].text, peer->hpoll); break; case CP_PRECISION: - ctl_putint(peer_var[CP_PRECISION].text, (LONG)peer->precision); + ctl_putint(peer_var[CP_PRECISION].text, peer->precision); break; case CP_ROOTDELAY: ctl_putfp(peer_var[CP_ROOTDELAY].text, peer->rootdelay); @@ -1431,7 +1417,12 @@ ctl_putpeer(varid, peer) break; case CP_REFID: if (peer->stratum > 1) - ctl_putadr(peer_var[CP_REFID].text, peer->refid); + 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); @@ -1449,13 +1440,13 @@ ctl_putpeer(varid, peer) ctl_putts(peer_var[CP_XMT].text, &peer->xmt); break; case CP_REACH: - ctl_puthex(peer_var[CP_REACH].text, (U_LONG)peer->reach); + ctl_puthex(peer_var[CP_REACH].text, peer->reach); break; case CP_FLASH: - ctl_puthex(peer_var[CP_FLASH].text, (U_LONG)peer->flash); + ctl_puthex(peer_var[CP_FLASH].text, peer->flash); break; case CP_VALID: - ctl_putuint(peer_var[CP_VALID].text, (U_LONG)peer->valid); + ctl_putuint(peer_var[CP_VALID].text, peer->valid); break; case CP_TIMER: ctl_putuint(peer_var[CP_TIMER].text, @@ -1486,7 +1477,7 @@ ctl_putpeer(varid, peer) (s_fp *)peer->filter_error, (int)peer->filter_nextpt); break; case CP_PMODE: - ctl_putuint(peer_var[CP_PMODE].text, (U_LONG)peer->pmode); + ctl_putuint(peer_var[CP_PMODE].text, peer->pmode); break; case CP_RECEIVED: ctl_putuint(peer_var[CP_RECEIVED].text, peer->received); @@ -1552,8 +1543,7 @@ ctl_putclock(varid, clock, mustput) case CC_TYPE: if (mustput || clock->clockdesc == NULL || *(clock->clockdesc) == '\0') { - ctl_putuint(clock_var[CC_TYPE].text, - (U_LONG)clock->type); + ctl_putuint(clock_var[CC_TYPE].text, clock->type); } break; case CC_TIMECODE: @@ -1561,7 +1551,7 @@ ctl_putclock(varid, clock, mustput) (int)clock->lencode); break; case CC_POLL: - ctl_putuint(clock_var[CC_POLL].text, (U_LONG)clock->polls); + ctl_putuint(clock_var[CC_POLL].text, clock->polls); break; case CC_NOREPLY: ctl_putuint(clock_var[CC_NOREPLY].text, clock->noresponse); @@ -1589,14 +1579,17 @@ ctl_putclock(varid, clock, mustput) break; case CC_FUDGEVAL2: if (mustput || (clock->haveflags & CLK_HAVEVAL2)) - ctl_putint(clock_var[CC_FUDGEVAL2].text, - clock->fudgeval2); + if (clock->fudgeval1 > 1) + ctl_putadr(clock_var[CC_FUDGEVAL2].text, + clock->fudgeval2); + else + ctl_putid(clock_var[CC_FUDGEVAL2].text, + (char *)&clock->fudgeval2); break; case CC_FLAGS: if (mustput || (clock->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))) - ctl_putuint(clock_var[CC_FLAGS].text, - (U_LONG)clock->flags); + ctl_putuint(clock_var[CC_FLAGS].text, clock->flags); break; case CC_DEVICE: if (clock->clockdesc == NULL || *(clock->clockdesc) == '\0') { @@ -1966,8 +1959,8 @@ write_variables(rbufp, restrict) register struct ctl_var *v; register int ext_var; char *valuep; - LONG val; - u_char leapind, leapwarn; + long val; + int leapind, leapwarn; /* * If he's trying to write into a peer tell him no way @@ -1985,8 +1978,8 @@ write_variables(rbufp, restrict) /* * Set flags to not-in-sync so we can tell when we get something. */ - leapind = (u_char)~0; - leapwarn = (u_char)~0; + leapind = ~0; + leapwarn = ~0; /* * Look through the variables. Dump out at the first sign of trouble. @@ -2037,13 +2030,13 @@ write_variables(rbufp, restrict) switch(v->code) { case CS_LEAP: case CS_LEAPIND: - leapind = (u_char)val; + leapind = val; break; case CS_LEAPWARNING: - leapwarn = (u_char)val; + leapwarn = val; break; default: - ctl_error(CERR_UNSPEC); /* our fault, really */ + ctl_error(CERR_UNSPEC); /* our fault, really */ return; } } @@ -2052,7 +2045,7 @@ write_variables(rbufp, restrict) /* * If we got anything, do it. */ - if (leapind != (u_char)~0 || leapwarn != (u_char)~0) { + if (leapind != ~0 || leapwarn != ~0) { if (!leap_setleap((int)leapind, (int)leapwarn)) { ctl_error(CERR_PERMISSION); return; @@ -2436,18 +2429,32 @@ report_event(err, peer) if (!(err & PEER_EVENT)) { if (ctl_sys_num_events < CTL_SYS_MAXEVENTS) ctl_sys_num_events++; - if (ctl_sys_last_event != (u_char)err) - syslog(LOG_INFO, "system event %x status %x", - err, ctlsysstatus()); - ctl_sys_last_event = (u_char)err; + if (ctl_sys_last_event != (u_char)err) { + syslog(LOG_INFO, "system event %x status %x", + err, ctlsysstatus()); +#ifdef DEBUG + if (debug) + printf("report_event: system event %x status %x\n", + err, ctlsysstatus()); +#endif + ctl_sys_last_event = (u_char)err; + } } else if (peer != 0) { peer->last_event = (u_char)(err & ~PEER_EVENT); if (peer->num_events < CTL_PEER_MAXEVENTS) - peer->num_events++; + peer->num_events++; syslog(LOG_INFO, "peer %s event %x status %x", ntoa(&peer->srcadr), err, ctlpeerstatus(peer)); +#ifdef DEBUG + if (debug) + printf("peer %s event %x status %x\n", + ntoa(&peer->srcadr), err, ctlpeerstatus(peer)); +#endif } else { syslog(LOG_ERR, "report_event: err %x, no peer", err); +#ifdef DEBUG + printf("report_event: err %x, no peer\n", err); +#endif return; } @@ -2489,10 +2496,8 @@ report_event(err, peer) clock.kv_list = (struct ctl_var *)0; refclock_control(&peer->srcadr, - (struct refclockstat *)0, - &clock); - ctl_puthex("refclockstatus", - (U_LONG)ctlclkstatus(&clock)); + (struct refclockstat *)0, &clock); + ctl_puthex("refclockstatus", ctlclkstatus(&clock)); for (i = 1; i <= CC_MAXCODE; i++) ctl_putclock(i, &clock, 0); @@ -2528,7 +2533,7 @@ report_event(err, peer) &clock); ctl_puthex("refclockstatus", - (U_LONG)ctlclkstatus(&clock)); + ctlclkstatus(&clock)); for (i = 1; i <= CC_MAXCODE; i++) ctl_putclock(i, &clock, 0); @@ -2572,11 +2577,11 @@ ctl_clr_stats() numasyncmsgs = 0; } -static unsigned long +static u_long count_var(k) struct ctl_var *k; { - register unsigned long c; + register u_long c; c = 0; while (k && !(k++->flags & EOV)) @@ -2588,10 +2593,10 @@ count_var(k) char * add_var(kv, size, def) struct ctl_var **kv; - unsigned long size; + u_long size; int def; { - register unsigned long c; + register u_long c; register struct ctl_var *k; c = count_var(*kv); @@ -2617,7 +2622,7 @@ void set_var(kv, data, size, def) struct ctl_var **kv; char *data; - unsigned long size; + u_long size; int def; { register struct ctl_var *k; @@ -2665,7 +2670,7 @@ set_var(kv, data, size, def) void set_sys_var(data, size, def) char *data; - unsigned long size; + u_long size; int def; { set_var(&ext_sys_var, data, size, def); diff --git a/usr.sbin/xntpd/xntpd/ntp_filegen.c b/usr.sbin/xntpd/xntpd/ntp_filegen.c index 55cf1aebab13..4c23bed44f06 100644 --- a/usr.sbin/xntpd/xntpd/ntp_filegen.c +++ b/usr.sbin/xntpd/xntpd/ntp_filegen.c @@ -25,7 +25,7 @@ #include "ntp_stdlib.h" /* - * NTP is intended to run LONG periods of time without restart. + * 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 @@ -43,7 +43,7 @@ extern int errno; /* * imported from timer */ -extern U_LONG current_time; +extern u_long current_time; /* * redefine this if your system dislikes filename suffixes like @@ -60,7 +60,7 @@ extern U_LONG current_time; extern int debug; #endif -static void filegen_open P((FILEGEN *, U_LONG)); +static void filegen_open P((FILEGEN *, u_long)); static int valid_fileref P((char *, char *)); #ifdef UNUSED static FILEGEN *filegen_unregister P((char *)); @@ -74,7 +74,7 @@ static FILEGEN *filegen_unregister P((char *)); static void filegen_open(gen, newid) FILEGEN *gen; - U_LONG newid; + u_long newid; { char *filename; char *basename; @@ -100,7 +100,7 @@ filegen_open(gen, newid) case FILEGEN_PID: filename = emalloc(len + 1 + 1 + 10); - sprintf(filename,"%s%c#%d", basename, SUFFIX_SEP, newid); + sprintf(filename,"%s%c#%ld", basename, SUFFIX_SEP, newid); break; case FILEGEN_DAY: @@ -142,7 +142,7 @@ filegen_open(gen, newid) case FILEGEN_AGE: filename = emalloc(len + 1 + 2 + 10); - sprintf(filename, "%s%ca%08d", basename, SUFFIX_SEP, newid); + sprintf(filename, "%s%ca%08ld", basename, SUFFIX_SEP, newid); break; } @@ -156,7 +156,7 @@ filegen_open(gen, newid) /* * try to resolve name collisions */ - static U_LONG conflicts = 0; + static u_long conflicts = 0; #ifndef S_ISREG #define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG) @@ -170,7 +170,8 @@ filegen_open(gen, newid) */ char *savename = emalloc(len + 1 + 1 + 10 + 10); sprintf(savename, "%s%c%dC%lu", - basename, SUFFIX_SEP, getpid(), conflicts++); + basename, SUFFIX_SEP, getpid(), + (u_long)conflicts++); if (rename(basename, savename) != 0) syslog(LOG_ERR," couldn't save %s: %m", basename); free(savename); @@ -207,7 +208,7 @@ filegen_open(gen, newid) #ifdef DEBUG if (debug > 3) printf("opening filegen (type=%d/id=%lu) \"%s\"\n", - gen->type, newid, filename); + gen->type, (u_long)newid, filename); #endif if (fp == NULL) { @@ -265,9 +266,9 @@ filegen_open(gen, newid) void filegen_setup(gen,now) FILEGEN *gen; - U_LONG now; + u_long now; { - U_LONG new_gen = ~0; + u_long new_gen = ~0; struct calendar cal; if (!(gen->flag & FGEN_FLAG_ENABLED)) { @@ -450,9 +451,10 @@ filegen_get(name) while(f) { if (f->name == name || strcmp(name, f->name) == 0) { -#ifdef DEBUG +#ifdef XXX /* this gives the Alpha compiler fits */ if (debug > 3) - printf("filegen_get(\"%s\") = %x\n", name, (u_int)f->filegen); + printf("filegen_get(\"%s\") = %x\n", name, + (u_int)f->filegen); #endif return f->filegen; } @@ -472,13 +474,13 @@ filegen_register(name, filegen) { struct filegen_entry **f = &filegen_registry; -#ifdef DEBUG +#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 DEBUG +#ifdef XXX /* this gives the Alpha compiler fits */ if (debug > 4) { printf("replacing filegen %x\n", (u_int)(*f)->filegen); } diff --git a/usr.sbin/xntpd/xntpd/ntp_intres.c b/usr.sbin/xntpd/xntpd/ntp_intres.c index 5ff4af4d8242..c05354b6837d 100644 --- a/usr.sbin/xntpd/xntpd/ntp_intres.c +++ b/usr.sbin/xntpd/xntpd/ntp_intres.c @@ -1,4 +1,4 @@ -/* ntp_intres.c,v 3.1 1993/07/06 01:11:16 jbj Exp +/* * ripped off from ../xnptres/xntpres.c by Greg Troxel 4/2/92 * routine callable from xntpd, rather than separate program * also, key info passed in via a global, so no key file needed. @@ -118,7 +118,7 @@ static int sockfd = -1; /* stuff to be filled in by caller */ -U_LONG req_keyid; /* request keyid */ +u_long req_keyid; /* request keyid */ char *req_file; /* name of the file with configuration info */ /* end stuff to be filled in */ @@ -130,7 +130,7 @@ extern int errno; 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 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 *)); @@ -147,9 +147,10 @@ ntp_intres() { FILE *in; +#ifdef DEBUG if ( debug ) - syslog(LOG_INFO, "ntp_intres running"); - + syslog(LOG_INFO, "ntp_intres running"); +#endif /* check out auth stuff */ if (!authhavekey(req_keyid)) { @@ -171,7 +172,7 @@ ntp_intres() readconf(in, req_file); (void) fclose(in); - if ( ! debug ) + if (!debug ) (void) unlink(req_file); /* @@ -289,7 +290,7 @@ addentry(name, mode, version, minpoll, maxpoll, flags, ttl, keyid) int maxpoll; int flags; int ttl; - U_LONG keyid; + u_long keyid; { register char *cp; register struct conf_entry *ce; @@ -308,7 +309,7 @@ addentry(name, mode, version, minpoll, maxpoll, flags, ttl, keyid) ce->ce_maxpoll = (u_char)maxpoll; ce->ce_flags = (u_char)flags; ce->ce_ttl = (u_char)ttl; - ce->ce_keyid = htonl(keyid); + ce->ce_keyid = keyid; ce->ce_next = NULL; if (confentries == NULL) { @@ -466,7 +467,7 @@ request(conf) auth1crypt(req_keyid, (U_LONG *)&reqpkt, REQ_LEN_NOMAC); gettstamp(&ts); - M_ADDUF(ts.l_ui, ts.l_uf, SKEWTIME); + L_ADDUF(&ts, SKEWTIME); HTONL_FP(&ts, &reqpkt.tstamp); n = auth2crypt(req_keyid, (U_LONG *)&reqpkt, REQ_LEN_NOMAC); @@ -683,7 +684,7 @@ readconf(fp, name) { register int i; char *token[NUMTOK]; - U_LONG intval[NUMTOK]; + u_long intval[NUMTOK]; int flags; char buf[MAXLINESIZE]; char *bp; diff --git a/usr.sbin/xntpd/xntpd/ntp_io.c b/usr.sbin/xntpd/xntpd/ntp_io.c index 551df510dc37..dcb68efd9089 100644 --- a/usr.sbin/xntpd/xntpd/ntp_io.c +++ b/usr.sbin/xntpd/xntpd/ntp_io.c @@ -1,4 +1,4 @@ -/* ntp_io.c,v 3.1 1993/07/06 01:11:17 jbj Exp +/* * xntp_io.c - input/output routines for xntpd. The socket-opening code * was shamelessly stolen from ntpd. */ @@ -7,12 +7,13 @@ #include <errno.h> #include <sys/types.h> #include <sys/param.h> -#include <sys/ioctl.h> #include <sys/time.h> - -#ifdef MCAST -#include "ntp_in.h" -#endif /* MCAST */ +#ifndef __bsdi__ +#include <netinet/in.h> +#endif +#if defined(__bsdi__) || defined(SYS_NETBSD) || defined(SYS_FREEBSD) || defined(SYS_AIX) +#include <sys/ioctl.h> +#endif #include "ntpd.h" #include "ntp_select.h" @@ -21,6 +22,10 @@ #include "ntp_if.h" #include "ntp_stdlib.h" +#if defined(MCAST) && !defined(IP_ADD_MEMBERSHIP) +#undef MCAST +#endif + #if defined(BSD)&&!defined(sun)&&!defined(SYS_SINIXM) #if BSD >= 199006 #define HAVE_VARIABLE_IFR_LENGTH @@ -61,7 +66,7 @@ #define BLOCKIO() ((void) block_sigio()) #define UNBLOCKIO() ((void) unblock_sigio()) #else -#define BLOCKIO() +#define BLOCKIO() #define UNBLOCKIO() #endif @@ -76,15 +81,15 @@ /* * Memory allocation */ -U_LONG full_recvbufs; /* number of recvbufs on fulllist */ -U_LONG free_recvbufs; /* number of recvbufs on freelist */ +u_long full_recvbufs; /* number of recvbufs on fulllist */ +u_long free_recvbufs; /* number of recvbufs on freelist */ static struct recvbuf *freelist; /* free buffers */ static struct recvbuf *fulllist; /* lifo buffers with data */ static struct recvbuf *beginlist; /* fifo buffers with data */ -U_LONG total_recvbufs; /* total recvbufs currently in use */ -U_LONG lowater_additions; /* number of times we have added memory */ +u_long total_recvbufs; /* total recvbufs currently in use */ +u_long lowater_additions; /* number of times we have added memory */ static struct recvbuf initial_bufs[RECV_INIT]; /* initial allocation */ @@ -92,15 +97,15 @@ static struct recvbuf initial_bufs[RECV_INIT]; /* initial allocation */ /* * Other statistics of possible interest */ -U_LONG packets_dropped; /* total number of packets dropped on reception */ -U_LONG packets_ignored; /* packets received on wild card interface */ -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 */ +u_long packets_dropped; /* total number of packets dropped on reception */ +u_long packets_ignored; /* packets received on wild card interface */ +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 */ -U_LONG handler_calls; /* number of calls to interrupt handler */ -U_LONG handler_pkts; /* number of pkts received by handler */ -U_LONG io_timereset; /* time counters were reset */ +u_long handler_calls; /* number of calls to interrupt handler */ +u_long handler_pkts; /* number of pkts received by handler */ +u_long io_timereset; /* time counters were reset */ /* * Interface stuff @@ -128,16 +133,16 @@ int maxactivefd; /* * Imported from ntp_timer.c */ -extern U_LONG current_time; +extern u_long current_time; extern int errno; extern int debug; -static int create_sockets P((unsigned int)); +static int create_sockets P((u_int)); static int open_socket P((struct sockaddr_in *, int)); static void close_socket P((int)); #ifdef HAVE_SIGNALED_IO -static int init_clock_sig P(()); +static int init_clock_sig P(()); static void init_socket_sig P((int)); static void set_signal P(()); static RETSIGTYPE sigio_handler P((int)); @@ -202,7 +207,7 @@ init_io() */ static int create_sockets(port) - unsigned int port; + u_int port; { #ifdef STREAMS_TLI struct strioctl ioc; @@ -223,7 +228,7 @@ create_sockets(port) */ inter_list[0].sin.sin_family = AF_INET; inter_list[0].sin.sin_port = port; - inter_list[0].sin.sin_addr.s_addr = INADDR_ANY; + 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(~0); @@ -231,9 +236,6 @@ create_sockets(port) inter_list[0].sent = 0; inter_list[0].notsent = 0; inter_list[0].flags = INT_BROADCAST; -#ifdef MCAST - inter_list[0].flags |= INT_MULTICAST; -#endif /* MCAST */ #ifdef USE_STREAMS_DEVICE_FOR_IF_CONFIG if ((vs = open("/dev/ip", O_RDONLY)) < 0) { @@ -246,7 +248,7 @@ create_sockets(port) i = 1; - ifc.ifc_len = sizeof(buf); + ifc.ifc_len = sizeof(buf); #ifdef STREAMS_TLI ioc.ic_cmd = SIOCGIFCONF; ioc.ic_timout = 0; @@ -382,13 +384,13 @@ create_sockets(port) } inter_list[i].mask = *(struct sockaddr_in *)&ifreq.ifr_addr; - /* + /* * look for an already existing source interface address. If - * the machine has multiple point to point interfaces, then + * 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 == + if (inter_list[j].sin.sin_addr.s_addr == inter_list[i].sin.sin_addr.s_addr) { break; } @@ -406,6 +408,15 @@ create_sockets(port) inter_list[i].flags & INT_BROADCAST); } +#if defined(MCAST) && !defined(sun) && !defined(SYS_BSDI) && !defined(SYS_DECOSF1) && !defined(SYS_44BSD) + /* + * 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 */ @@ -419,7 +430,7 @@ create_sockets(port) 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", + printf("interface %d: fd=%d, bfd=%d, name=%.8s, flags=0x%x\n", i, inter_list[i].fd, inter_list[i].bfd, @@ -454,7 +465,7 @@ io_setbclient() if (inter_list[i].flags & INT_BCASTOPEN) continue; #ifdef SOLARIS - inter_list[i].bcast.sin_addr.s_addr = INADDR_ANY; + inter_list[i].bcast.sin_addr.s_addr = htonl(INADDR_ANY); #endif #ifndef SYS_DOMAINOS inter_list[i].bfd = open_socket(&inter_list[i].bcast, 0); @@ -464,30 +475,85 @@ io_setbclient() } -#ifdef MCAST /* * io_multicast_add() - add multicast group address */ void io_multicast_add(addr) - U_LONG addr; + u_long addr; { - int fd = inter_list[0].fd; +#ifdef MCAST struct ip_mreq mreq; - - if (!IN_CLASSD(addr)) + int i = ninterfaces; /* Use the next interface */ + u_long haddr = ntohl(addr); + struct in_addr iaddr; + int s; + struct sockaddr_in *sinp; + + iaddr.s_addr = addr; + + if (!IN_CLASSD(haddr)) + { syslog(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); + /* 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; + syslog(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(~0); + } + /* * enable reception of multicast packets */ - mreq.imr_multiaddr.s_addr = addr; - mreq.imr_interface.s_addr = INADDR_ANY; - if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + 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) - syslog(LOG_ERR, "setsockopt IP_ADD_MEMBERSHIP fails: %m"); -} + syslog(LOG_ERR, + "setsockopt IP_ADD_MEMBERSHIP fails: %m for %x / %x (%s)", + mreq.imr_multiaddr, 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; + syslog(LOG_ERR, "cannot add multicast address %s as no MCAST support", + inet_ntoa(iaddr)); #endif /* MCAST */ - +} /* * io_unsetbclient - close the broadcast client sockets @@ -506,38 +572,66 @@ io_unsetbclient() } -#ifdef MCAST /* * io_multicast_del() - delete multicast group address */ void io_multicast_del(addr) - U_LONG addr; + u_long addr; { - int fd = inter_list[0].fd; +#ifdef MCAST + int i; struct ip_mreq mreq; + struct sockaddr_in sinaddr; - if (!IN_CLASSD(addr)) + if (!IN_CLASSD(addr)) { + sinaddr.sin_addr.s_addr = addr; + syslog(LOG_ERR, + "invalid multicast address %s", ntoa(&sinaddr)); return; + } + /* - * disable reception of multicast packets + * Disable reception of multicast packets */ mreq.imr_multiaddr.s_addr = addr; - mreq.imr_interface.s_addr = INADDR_ANY; - if (setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, - (char *)&mreq, sizeof(mreq)) == -1) - syslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails: %m"); -} + 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 slose 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) + syslog(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 /* MCAST */ + syslog(LOG_ERR, "this function requires multicast kernel"); #endif /* MCAST */ +} /* * open_socket - open a socket, returning the file descriptor */ static int -open_socket(addr, bcast) +open_socket(addr, flags) struct sockaddr_in *addr; - int bcast; + int flags; { int fd; int on = 1, off = 0; @@ -549,10 +643,6 @@ open_socket(addr, bcast) /*NOTREACHED*/ } - if (fd > maxactivefd) - maxactivefd = fd; - FD_SET(fd, &activefds); - /* set SO_REUSEADDR since we will be binding the same port number on each interface */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, @@ -566,15 +656,32 @@ open_socket(addr, bcast) if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) { char buff[160]; sprintf(buff, - "bind() fd %d, family %d, port %d, addr %08x, bcast=%d fails: %%m", - fd, - addr->sin_family, - addr->sin_port, - addr->sin_addr.s_addr, - bcast); + "bind() fd %d, family %d, port %d, addr %08lx, in_classd=%d flags=%d fails: %%m", + fd, addr->sin_family, (int)ntohs(addr->sin_port), + (u_long)ntohl(addr->sin_addr.s_addr), + IN_CLASSD(ntohl(addr->sin_addr.s_addr)), flags); syslog(LOG_ERR, buff); + close(fd); + + /* + * soft fail if opening a class D address + */ + if (IN_CLASSD(ntohl(addr->sin_addr.s_addr))) + return -1; exit(1); } +#ifdef DEBUG + if (debug) + printf("bind() fd %d, family %d, port %d, addr %08lx, flags=%d\n", + fd, + addr->sin_family, + (int)ntohs(addr->sin_port), + (u_long)ntohl(addr->sin_addr.s_addr), + flags); +#endif + if (fd > maxactivefd) + maxactivefd = fd; + FD_SET(fd, &activefds); #ifdef HAVE_SIGNALED_IO init_socket_sig(fd); @@ -613,23 +720,9 @@ Need non blocking I/O syslog(LOG_ERR, "setsockopt SO_REUSEADDR off fails: %m"); } -#ifdef MCAST - /* for the moment we use the bcast option to set multicast ttl */ - - if (bcast) { - unsigned char mttl = 127; - - /* set the multicast ttl for outgoing packets */ - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, - &mttl, sizeof(mttl)) == -1) { - syslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails: %m"); - } - } -#endif /* MCAST */ - #ifdef SO_BROADCAST /* if this interface can support broadcast, set SO_BROADCAST */ - if (bcast) { + if (flags & INT_BROADCAST) { if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on))) { syslog(LOG_ERR, "setsockopt(SO_BROADCAST): %m"); @@ -679,7 +772,7 @@ findbcastinter(addr) { #ifdef SIOCGIFCONF register int i; - register U_LONG netnum; + register u_long netnum; netnum = NSRCADR(addr); for (i = 1; i < ninterfaces; i++) { @@ -709,7 +802,7 @@ getrecvbufs() #ifdef DEBUG if (debug > 4) - printf("getrecvbufs: %d handler interrupts, %d frames\n", + printf("getrecvbufs: %ld handler interrupts, %ld frames\n", handler_calls, handler_pkts); #endif @@ -726,7 +819,7 @@ getrecvbufs() */ #ifdef DEBUG if (debug > 4) - printf("getrecvbufs returning %d buffers\n", full_recvbufs); + printf("getrecvbufs returning %ld buffers\n", full_recvbufs); #endif rb = beginlist; fulllist = 0; @@ -787,9 +880,10 @@ freerecvbuf(rb) * destination is logged. */ void -sendpkt(dest, inter, pkt, len) +sendpkt(dest, inter, ttl, pkt, len) struct sockaddr_in *dest; struct interface *inter; + int ttl; struct pkt *pkt; int len; { @@ -802,6 +896,7 @@ sendpkt(dest, inter, pkt, len) u_short port; struct in_addr addr; }; + #ifndef ERRORCACHESIZE #define ERRORCACHESIZE 8 #endif @@ -813,10 +908,26 @@ sendpkt(dest, inter, pkt, len) #ifdef DEBUG if (debug) - printf("sendpkt(fd=%d %s, %s, %d)\n", inter->fd, ntoa(dest), - ntoa(&inter->sin), len); + printf("%ssendpkt(fd=%d %s, %s, ttl=%d, %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) { + u_char mttl = ttl; + + /* set the multicast ttl for outgoing packets */ + if (setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL, + &mttl, sizeof(mttl)) == -1) { + syslog(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) @@ -924,7 +1035,7 @@ again: read(fd, (char *)&rb->recv_space, i); if (rb->recv_length == -1) { - syslog(LOG_ERR, "clock read: %m"); + syslog(LOG_ERR, "clock read fd %d: %m", fd); rb->next = freelist; freelist = rb; free_recvbufs++; @@ -937,6 +1048,7 @@ again: */ rb->recv_srcclock = rp->srcclock; rb->dstadr = 0; + rb->fd = fd; rb->recv_time = ts; rb->receiver = rp->clock_recv; @@ -969,43 +1081,45 @@ again: 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. + * on the wild card socket, just dump the + * packet. */ - - if (!(free_recvbufs && i == 0 && + if (!(free_recvbufs && i == 0 && inter_list[i].flags & INT_MULTICAST)) { - #ifdef UDP_WILDCARD_DELIVERY -/* - * these guys manage to put properly addressed packets into the wildcard queue - */ + /* + * these guys manage to put properly addressed + * packets into the wildcard queue + */ if (free_recvbufs == 0) { #else - if (i == 0 || free_recvbufs == 0) { + if (i == 0 || free_recvbufs == 0) { #endif - char buf[RX_BUFF_SIZE]; - -#ifndef UDP_WILDCARD_DELIVERY - (void) read(fd, buf, sizeof buf); -#else - fromlen = 0; - (void) recvfrom(fd, buf, sizeof(buf), 0, - (struct sockaddr *)0, - &fromlen); + 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("ignore/drop on %d(%lu) fd=%d from %s\n", + i, free_recvbufs, fd, + inet_ntoa(((struct sockaddr_in *) &from)->sin_addr)); #endif - - if (i == 0) - packets_ignored++; - else - packets_dropped++; - continue; - } + if (i == 0) + packets_ignored++; + else + packets_dropped++; + continue; + } } rb = freelist; @@ -1024,14 +1138,17 @@ again: freelist = rb; free_recvbufs++; #ifdef DEBUG - if (debug) - printf("input_handler: fd=%d dropped (bad recvfrom)\n", fd); + if (debug) + printf("input_handler: fd=%d dropped (bad recvfrom)\n", fd); #endif continue; } #ifdef DEBUG - if (debug) - printf("input_handler: fd=%d length %d\n", fd, rb->recv_length); + if (debug) + 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 /* @@ -1039,6 +1156,7 @@ again: * put it on the full list and do bookkeeping. */ rb->dstadr = &inter_list[i]; + rb->fd = fd; rb->recv_time = ts; rb->receiver = receive; @@ -1074,7 +1192,7 @@ findinterface(addr) struct sockaddr_in *addr; { register int i; - register U_LONG saddr; + register u_long saddr; /* * Just match the address portion. @@ -1107,7 +1225,7 @@ io_clr_stats() #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 @@ -1243,7 +1361,7 @@ io_closeclock(rio) * Spical cases first! */ #if defined(SYS_HPUX) -#define CLOCK_DONE +#define CLOCK_DONE static int init_clock_sig(rio) struct refclockio *rio; @@ -1273,12 +1391,12 @@ init_clock_sig(rio) } return 0; } -#endif /* SYS_HPUX */ +#endif /* SYS_HPUX */ #if defined(SYS_AIX)&&!defined(_BSD) /* * SYSV compatibility mode under AIX. */ -#define CLOCK_DONE +#define CLOCK_DONE static int init_clock_sig(rio) struct refclockio *rio; @@ -1302,7 +1420,7 @@ init_clock_sig(rio) return 0; } #endif /* AIX && !BSD */ -#ifndef CLOCK_DONE +#ifndef CLOCK_DONE static int init_clock_sig(rio) struct refclockio *rio; @@ -1328,7 +1446,7 @@ static init_clock_sig(rio) if (ioctl(rio->fd, I_SETSIG, S_INPUT) < 0) { syslog(LOG_ERR, "ioctl(I_SETSIG, S_INPUT) fails for clock I/O: %m"); - return 1; + return 1; } return 0; } @@ -1342,7 +1460,7 @@ static init_clock_sig(rio) * Special cases first! */ #if defined(SYS_HPUX) || defined(SYS_LINUX) -#define SOCKET_DONE +#define SOCKET_DONE static void init_socket_sig(fd) int fd; @@ -1380,7 +1498,7 @@ init_socket_sig(fd) /* * SYSV compatibility mod under AIX */ -#define SOCKET_DONE +#define SOCKET_DONE static void init_socket_sig(fd) int fd; @@ -1408,9 +1526,9 @@ init_socket_sig(fd) #endif /* AIX && !BSD */ #if defined(UDP_BACKWARDS_SETOWN) /* - * SunOS 3.5 and Ultirx 2.0 + * SunOS 3.5 and Ultirx 2.0 */ -#define SOCKET_DONE +#define SOCKET_DONE static void init_socket_sig(fd) int fd; @@ -1635,8 +1753,8 @@ block_io_and_alarm() { int mask; - mask = sigmask(SIGIO)|sigmask(SIGALRM); - (void)sigblock(mask); + mask = sigmask(SIGIO)|sigmask(SIGALRM); + (void)sigblock(mask); } void @@ -1659,7 +1777,7 @@ unblock_io_and_alarm() { int mask, omask; - mask = sigmask(SIGIO)|sigmask(SIGALRM); + mask = sigmask(SIGIO)|sigmask(SIGALRM); omask = sigblock(0); omask &= ~mask; (void)sigsetmask(omask); @@ -1668,9 +1786,9 @@ unblock_io_and_alarm() void unblock_sigio() { - int mask, omask; - - mask = sigmask(SIGIO); + int mask, omask; + + mask = sigmask(SIGIO); omask = sigblock(0); omask &= ~mask; (void)sigsetmask(omask); @@ -1681,7 +1799,7 @@ wait_for_signal() { int mask, omask; - mask = sigmask(SIGIO)|sigmask(SIGALRM); + mask = sigmask(SIGIO)|sigmask(SIGALRM); omask = sigblock(0); omask &= ~mask; sigpause(omask); diff --git a/usr.sbin/xntpd/xntpd/ntp_leap.c b/usr.sbin/xntpd/xntpd/ntp_leap.c index aadbb09dc346..6473362cf652 100644 --- a/usr.sbin/xntpd/xntpd/ntp_leap.c +++ b/usr.sbin/xntpd/xntpd/ntp_leap.c @@ -1,4 +1,4 @@ -/* ntp_leap.c,v 3.1 1993/07/06 01:11:18 jbj Exp +/* * ntp_leap - maintain leap bits and take action when a leap occurs */ #include <stdio.h> @@ -41,7 +41,7 @@ u_char leap_mask; /* set on day before a potential leap */ * Timer. The timer code imports this so it can call us prior to * calling out any pending transmits. */ -U_LONG leap_timer; +u_long leap_timer; /* * We don't want to do anything drastic if the leap function is handled @@ -67,18 +67,18 @@ u_char leapbits; /* * Imported from the timer module. */ -extern U_LONG current_time; +extern u_long current_time; /* * Some statistics counters */ -U_LONG leap_processcalls; /* calls to leap_process */ -U_LONG leap_notclose; /* leap found to be a LONG time from now */ -U_LONG leap_monthofleap; /* in the month of a leap */ -U_LONG leap_dayofleap; /* This is the day of the leap */ -U_LONG leap_hoursfromleap; /* only 2 hours from leap */ -U_LONG leap_happened; /* leap process saw the leap */ +u_long leap_processcalls; /* calls to leap_process */ +u_long leap_notclose; /* leap found to be a long time from now */ +u_long leap_monthofleap; /* in the month of a leap */ +u_long leap_dayofleap; /* This is the day of the leap */ +u_long leap_hoursfromleap; /* only 2 hours from leap */ +u_long leap_happened; /* leap process saw the leap */ /* * Imported from the main module @@ -86,7 +86,7 @@ U_LONG leap_happened; /* leap process saw the leap */ extern int debug; -static void setnexttimeout P((U_LONG)); +static void setnexttimeout P((u_long)); /* * init_leap - initialize the leap module's data. @@ -114,18 +114,18 @@ init_leap() void leap_process() { - U_LONG leapnext; - U_LONG leaplast; + u_long leapnext; + u_long leaplast; l_fp ts; u_char bits; extern u_char sys_leap; leap_processcalls++; get_systime(&ts); - calleapwhen(ts.l_ui, &leaplast, &leapnext); + calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext); /* - * Figure out what to do based on how LONG to the next leap. + * Figure out what to do based on how long to the next leap. */ if (leapnext > OKAYTOSETWARNING) { if (leaplast < ONEMINUTE) { @@ -189,7 +189,7 @@ leap_process() * recomputed. */ if ((leapnext - DAYBEFORE) >= DAYBEFORE) - setnexttimeout((U_LONG)DAYBEFORE); + setnexttimeout((u_long)DAYBEFORE); else setnexttimeout(leapnext - DAYBEFORE); return; @@ -223,7 +223,7 @@ leap_process() */ static void setnexttimeout(secs) - U_LONG secs; + u_long secs; { /* * We try to aim the time out at between 1 and 1+(1<<EVENT_TIMEOUT) @@ -243,13 +243,13 @@ leap_setleap(indicator, warning) int indicator; int warning; { - U_LONG leapnext; - U_LONG leaplast; + u_long leapnext; + u_long leaplast; l_fp ts; int i; get_systime(&ts); - calleapwhen(ts.l_ui, &leaplast, &leapnext); + calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext); i = 0; if (warning != ~0) diff --git a/usr.sbin/xntpd/xntpd/ntp_loopfilter.c b/usr.sbin/xntpd/xntpd/ntp_loopfilter.c index 61332b9b6384..168ef3289a6e 100644 --- a/usr.sbin/xntpd/xntpd/ntp_loopfilter.c +++ b/usr.sbin/xntpd/xntpd/ntp_loopfilter.c @@ -1,7 +1,6 @@ /* * ntp_loopfilter.c - implements the NTP loop filter algorithm */ - #include <stdio.h> #include <ctype.h> #include <sys/time.h> @@ -10,41 +9,10 @@ #include "ntpd.h" #include "ntp_io.h" #include "ntp_unixtime.h" - -#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) -#include <sys/stat.h> -#include "ntp_refclock.h" -#endif /* PPS || PPSCLK || PPSPPS */ - -#if defined(PPSCLK) || defined(PPSPPS) -#if defined(HAVE_BSD_TTYS) -#include <sgtty.h> -#endif /* HAVE_BSD_TTYS */ - -#if defined(HAVE_SYSV_TTYS) -#include <termio.h> -#endif /* HAVE_SYSV_TTYS */ - -#ifdef HAVE_TERMIOS -#include <termios.h> -#endif -#if defined(STREAM) -#include <stropts.h> -#if defined(PPSCLK) -#include <sys/clkdefs.h> -#endif /* PPSCLK */ -#endif /* STREAM */ - -#endif /* PPSCLK || PPSPPS */ - -#if defined(PPSPPS) -#include <sys/ppsclock.h> -#endif /* PPSPPS */ - #include "ntp_stdlib.h" #ifdef KERNEL_PLL -#include <sys/timex.h> +#include "sys/timex.h" #define ntp_gettime(t) syscall(SYS_ntp_gettime, (t)) #define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t)) #endif /* KERNEL_PLL */ @@ -59,122 +27,83 @@ * seconds. When adjustments are capped inside this range (see * CLOCK_MAX_{I,F}) both the clock_adjust and the compliance * registers should be fine. (When the compliance is above 16, it - * will at most accumulate 2**CLOCK_MULT times the maximum offset, + * will at most accumulate 2 ** CLOCK_MULT times the maximum offset, * which means it fits in a s_fp.) * * The skew compensation is a special case. In version 2, it was - * kept in ms/4s (i.e., CLOCK_FREQ was 10). In version 3 (Section 5) - * it seems to be 2**-16ms/4s in a s_fp for a maximum of +-125ppm + * kept in ms / 4s (i.e., CLOCK_FREQ was 10). In version 3 (Section 5) + * it seems to be 2 ** -16ms / 4s in a s_fp for a maximum of +-125ppm * (stated maximum 100ppm). Since this seems about to change to a - * larger range, it will be kept in units of 2**-20 (CLOCK_DSCALE) + * larger range, it will be kept in units of 2 ** -20 (CLOCK_DSCALE) * in an s_fp (mainly because that's nearly the same as parts per * million). Note that this is ``seconds per second'', whereas a * clock adjustment is a 32-bit fraction of a second to be applied - * every 2**CLOCK_ADJ seconds; to find it, shift the drift right by - * (CLOCK_DSCALE-16-CLOCK_ADJ). When updating the drift, on the other - * hand, the CLOCK_FREQ factor from the spec assumes the value to be - * in ``seconds per 4 seconds''; to get our units, CLOCK_ADJ must be + * every 2 ** CLOCK_ADJ seconds; to find it, shift the drift right by + * (CLOCK_DSCALE - 16 - CLOCK_ADJ). When updating the drift, on the + * other hand, the CLOCK_FREQ factor from the spec assumes the value to + * be in ``seconds per 4 seconds''; to get our units, CLOCK_ADJ must be * added to the shift. + * + * Kernel PLL/PPS state machine + * + * The following state machine is used when the kernel PLL modifications + * described in the README.kernel file are present. The initial + * configuration routine loop_config() sets up the initial frequency + * estimate and tests if the kernel modifications are present. If so and + * the PLL mode bit 1 (STA_PLL) of the mode word in the drift file + * (ntp.drift) is set, pll_control is set true and the kernel pll is + * enabled. If the kernel modifications are present and the PLL mode bit + * 2 (STA_PPSFREQ) is set, the kernel PPS frequency discipline is + * enabled. + * + * Each update to a prefer peer sets pps_update true 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. + * + * The pll_enable switch can be set both at configuration time and at + * run time using xntpdc. If true, the kernel modifications are active + * as described above; if false, the kernel is bypassed entirely (except + * for the PPS frequency update, if enabled) and the daemon PLL used + * instead. */ - -/* - * Macro to compute log2(). We don't want to to this very often, but - * needs what must. - */ -#define LOG2(r, t) \ - do { \ - LONG x = t; \ - r = 0; \ - while(x >> 1) \ - r++; \ - } - #define RSH_DRIFT_TO_FRAC (CLOCK_DSCALE - 16) #define RSH_DRIFT_TO_ADJ (RSH_DRIFT_TO_FRAC - CLOCK_ADJ) #define RSH_FRAC_TO_FREQ (CLOCK_FREQ + CLOCK_ADJ - RSH_DRIFT_TO_FRAC) -#define PPS_MAXAGE 120 /* pps signal timeout (s) */ -#define PPS_MAXUPDATE 600 /* pps update timeout (s) */ +#define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */ /* * Program variables */ - l_fp last_offset; /* last adjustment done */ -static LONG clock_adjust; /* clock adjust (fraction only) */ - - s_fp drift_comp; /* drift compensation register */ -static s_fp max_comp; /* drift limit imposed by max host clock slew */ - - int time_constant; /* log2 of time constant (0 .. 4) */ -static U_LONG tcadj_time; /* last time-constant adjust time */ - - U_LONG watchdog_timer; /* watchdog timer, in seconds */ -static int first_adjustment; /* 1 if waiting for first adjustment */ -static int tc_counter; /* time-constant hold counter */ - -static l_fp pps_offset; /* filtered pps offset */ -static u_fp pps_dispersion; /* pps dispersion */ -static U_LONG pps_time; /* last pps sample time */ - - int pps_control; /* true if working pps signal */ + l_fp last_offset; /* last clock offset */ + u_long last_time; /* time of last clock update (s) */ + u_fp clock_stability; /* clock stability (ppm) */ + s_fp clock_frequency; /* clock frequency error (ppm) */ + s_fp drift_comp; /* pll frequency (ppm) */ +static long clock_adjust; /* clock adjust (fraction only) */ +static s_fp max_comp; /* max frequency offset (ppm) */ + int tc_counter; /* poll-adjust counter */ + int pll_status; /* status bits for kernel pll */ int pll_control; /* true if working kernel pll */ -static l_fp pps_delay; /* pps tuning offset */ - U_LONG pps_update; /* last pps update time */ + int pll_enable; /* true if pll enabled */ + u_long pps_control; /* last pps sample time */ + int pps_update; /* pps update valid */ int fdpps = -1; /* pps file descriptor */ -#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) -/* - * This module has support for a 1-pps signal to fine-tune the local - * clock. The signal is optional; when present and operating within - * given tolerances in frequency and jitter, it is used to discipline - * the local clock. In order for this 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 and EIA level-converter. When - * operated at 38.4 kbps with a SPARCstation IPC, this arrangement has a - * worst-case jitter less than 26 us. The pps delay configuration - * declaration can be used to compensate for miscellaneous uart and - * os delays. Allow about 247 us for uart delays at 38400 bps and - * -1 ms for SunOS streams nonsense. - */ - -/* - * A really messy way to map an integer baud rate to the system baud rate. - * There should be a better way. - */ -#define SYS_BAUD(i) \ - ( i == 38400 ? B38400 : \ - ( i == 19200 ? B19200 : \ - ( i == 9600 ? B9600 : \ - ( i == 4800 ? B4800 : \ - ( i == 2400 ? B2400 : \ - ( i == 1200 ? B1200 : \ - ( i == 600 ? B600 : \ - ( i == 300 ? B300 : 0 )))))))) - -#define PPS_BAUD B38400 /* default serial port speed */ -timecode */ -#define PPS_DEV "/dev/pps" /* pps port */ -#define PPS_FAC 3 /* pps shift (log2 trimmed samples) */ -#define PPS_TRIM 6 /* samples trimmed from median filter */ -#define NPPS ((1 << PPS_FAC) + 2 * PPS_TRIM) /* pps filter size */ -#define PPS_XCPT "\377" /* intercept character */ - -#if defined(PPSCLK) -static struct refclockio io; /* given to the I/O handler */ -static int pps_baud; /* baud rate of PPS line */ -#endif /* PPSCLK */ -static U_LONG nsamples; /* number of pps samples collected */ -static LONG samples[NPPS]; /* median filter for pps samples */ - -#endif /* PPS || PPSCLK || PPSPPS */ - /* * Imported from the ntp_proto module */ -extern u_char sys_stratum; -extern s_fp sys_rootdelay; -extern u_fp sys_rootdispersion; -extern s_char sys_precision; +extern s_fp sys_rootdelay; /* root delay */ +extern u_fp sys_rootdispersion; /* root dispersion */ +extern struct peer *sys_peer; /* system peer pointer */ +extern u_char sys_poll; /* log2 of system poll interval */ +extern u_char sys_leap; /* system leap bits */ +extern l_fp sys_refskew; /* accumulated skew since last update */ +extern u_fp sys_maxd; /* max dispersion of survivor list */ /* * Imported from ntp_io.c @@ -189,31 +118,13 @@ extern int debug; /* global debug flag */ /* * Imported from timer module */ -extern U_LONG current_time; /* like it says, in seconds */ - -/* - * sys_poll and sys_refskew are set here - */ -extern u_char sys_poll; /* log2 of system poll interval */ -extern u_char sys_leap; /* system leap bits */ -extern l_fp sys_refskew; /* accumulated skew since last update */ -extern u_fp sys_maxd; /* max dispersion of survivor list */ +extern u_long current_time; /* like it says, in seconds */ /* * Imported from leap module */ extern u_char leapbits; /* sanitized leap bits */ -/* - * Function prototypes - */ -#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) -int pps_sample P((l_fp *)); -#if defined(PPSCLK) -static void pps_receive P((struct recvbuf *)); -#endif /* PPSCLK */ -#endif /* PPS || PPSCLK || PPSPPS */ - #if defined(KERNEL_PLL) #define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \ MOD_STATUS | MOD_TIMECONST) @@ -221,7 +132,6 @@ extern int sigvec P((int, struct sigvec *, struct sigvec *)); extern int syscall P((int, void *, ...)); void pll_trap P((void)); -static int pll_status; /* status bits for kernel pll */ static struct sigvec sigsys; /* current sigvec status */ static struct sigvec newsigsys; /* new sigvec status */ #endif /* KERNEL_PLL */ @@ -232,299 +142,164 @@ static struct sigvec newsigsys; /* new sigvec status */ void init_loopfilter() { - extern U_LONG tsf_maxslew; - U_LONG tsf_limit; -#if defined(PPSCLK) - int fd232; -#endif /* PPSCLK */ - clock_adjust = 0; - drift_comp = 0; - time_constant = 0; - tcadj_time = 0; - watchdog_timer = 0; - tc_counter = 0; - last_offset.l_i = 0; - last_offset.l_f = 0; - first_adjustment = 1; + extern u_long tsf_maxslew; + u_long tsf_limit; -/* - * Limit for drift_comp, minimum of two values. The first is to avoid - * signed overflow, the second to keep within 75% of the maximum - * adjustment possible in adj_systime(). - */ + /* + * Limit for drift_comp, minimum of two values. The first is to + * avoid signed overflow, the second to keep within 75% of the + * maximum adjustment possible in adj_systime(). + */ max_comp = 0x7fff0000; tsf_limit = ((tsf_maxslew >> 1) + (tsf_maxslew >> 2)); if ((max_comp >> RSH_DRIFT_TO_ADJ) > tsf_limit) max_comp = tsf_limit << RSH_DRIFT_TO_ADJ; - pps_control = 0; -#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) -#if defined(PPSCLK) - pps_baud = PPS_BAUD; -#endif /* PPSCLK */ - pps_delay.l_i = 0; - pps_delay.l_f = 0; - pps_time = pps_update = 0; - nsamples = 0; -#if defined(PPSCLK) - - /* - * Open pps serial port. We don't care if the serial port comes - * up; if not, we just use the timecode. Therefore, if anything - * goes wrong, just reclaim the resources and continue. - */ - fd232 = open(PPS_DEV, O_RDONLY); - if (fd232 == -1) { - syslog(LOG_ERR, "loopfilter: open of %s: %m", PPS_DEV); - return; - } - -#if !defined(HPUXGADGET) /* dedicated to Ken Stone */ -#if defined(HAVE_SYSV_TTYS) - /* - * System V serial line parameters (termio interface) - */ - PPSCLK SUPPORT NOT AVAILABLE IN TERMIO INTERFACE -#endif /* HAVE_SYSV_TTYS */ -#if defined(HAVE_TERMIOS) - /* - * POSIX serial line parameters (termios interface) - * - * The PPSCLK option provides timestamping at the driver level. - * It uses a 1-pps signal and level converter (gadget box) and - * requires the tty_clk streams module and SunOS 4.1.1 or - * later. - */ - { struct termios ttyb, *ttyp; - - ttyp = &ttyb; - if (tcgetattr(fd232, ttyp) < 0) { - syslog(LOG_ERR, - "loopfilter: tcgetattr(%s): %m", PPS_DEV); - goto screwed; - } - ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL; - ttyp->c_oflag = 0; - ttyp->c_cflag = PPS_BAUD|CS8|CLOCAL|CREAD; - ttyp->c_lflag = ICANON; - ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; - if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { - syslog(LOG_ERR, - "loopfilter: tcsetattr(%s): %m", PPS_DEV); - goto screwed; - } - if (tcflush(fd232, TCIOFLUSH) < 0) { - syslog(LOG_ERR, - "loopfilter: tcflush(%s): %m", PPS_DEV); - goto screwed; - } - } -#endif /* HAVE_TERMIOS */ -#if defined(STREAM) - while (ioctl(fd232, I_POP, 0 ) >= 0) ; - if (ioctl(fd232, I_PUSH, "clk") < 0) { - syslog(LOG_ERR, - "loopfilter: ioctl(%s, I_PUSH, clk): %m", PPS_DEV); - goto screwed; - } - if (ioctl(fd232, CLK_SETSTR, PPS_XCPT) < 0) { - syslog(LOG_ERR, - "loopfilter: ioctl(%s, CLK_SETSTR, PPS_XCPT): %m", PPS_DEV); - goto screwed; - } -#endif /* STREAM */ -#if defined(HAVE_BSD_TTYS) - /* - * 4.3bsd serial line parameters (sgttyb interface) - * - * The PPSCLK option provides timestamping at the driver level. - * It uses a 1-pps signal and level converter (gadget box) and - * requires the tty_clk line discipline and 4.3bsd or later. - */ - { struct sgttyb ttyb; - int ldisc = CLKLDISC; - - if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { - syslog(LOG_ERR, - "loopfilter: ioctl(%s, TIOCGETP): %m", PPS_DEV); - goto screwed; - } - ttyb.sg_ispeed = ttyb.sg_ospeed = PPS_BAUD; - ttyb.sg_erase = ttyb.sg_kill = '\r'; - ttyb.sg_flags = RAW; - if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { - syslog(LOG_ERR, - "loopfilter: ioctl(%s, TIOCSETP): %m", PPS_DEV); - goto screwed; - } - if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { - syslog(LOG_ERR, - "loopfilter: ioctl(%s, TIOCSETD): %m", PPS_DEV); - goto screwed; - } - } -#endif /* HAVE_BSD_TTYS */ -#endif /* HPUXGADGET */ - fdpps = fd232; - /* - * Insert in device list. + * Reset clockworks */ - io.clock_recv = pps_receive; - io.srcclock = (caddr_t)NULL; - io.datalen = 0; - io.fd = fdpps; -#if defined(HPUXGADGET) - if (!io_addclock_simple(&io)) -#else - if (!io_addclock(&io)) -#endif /* HPUXGADGET */ - goto screwed; - return; + drift_comp = 0; + clock_adjust = 0; + tc_counter = 0; + sys_poll = NTP_MINPOLL; - /* - * Something broke. Reclaim resources. - */ -screwed: - (void)close(fdpps); - return; -#endif /* PPSCLK */ -#endif /* PPS || PPSCLK || PPSPPS */ + L_CLR(&last_offset); + last_time = 0; + clock_frequency = 0; + clock_stability = 0; + pps_update = pps_control = 0; } /* * local_clock - the NTP logical clock loop filter. Returns 1 if the - * clock was stepped, 0 if it was slewed and -1 if it is - * hopeless. + * clock was stepped, 0 if it was slewed and -1 if it is hopeless. */ int local_clock(fp_offset, peer) l_fp *fp_offset; /* best offset estimate */ struct peer *peer; /* from peer - for messages */ { - register LONG offset; - register U_LONG tmp_ui; - register U_LONG tmp_uf; - register LONG tmp; - int isneg; + long offset; + long tmp; + int return_code; + l_fp ftmp; + s_fp stmp; + u_fp smax; + long allan; + long interval; #if defined(KERNEL_PLL) struct timex ntv; #endif /* KERNEL_PLL */ + /* + * Initialize estimated measurement error and Allan variance + * intercept point. The measurement error is assumed the sum of + * the peer dispersion plus select dispersion, which seems + * reasonable. The Allan variance intercept point is assumed + * at MAXSEC for reference clocks and twice that for peer + * clocks, which seems cowardly. + */ + if (peer->refclktype) + allan = CLOCK_MAXSEC; + else + allan = CLOCK_MAXSEC << 1; + + if (!last_time) + last_time = current_time; + interval = (long)(current_time - last_time); + clock_adjust = 0; + offset = fp_offset->l_f; + smax = peer->dispersion + peer->selectdisp; + return_code = 0; + #ifdef DEBUG if (debug > 1) - printf("local_clock(%s, %s)\n", lfptoa(fp_offset, 6), - ntoa(&peer->srcadr)); + printf( + "local_clock: offset %s peer %s watch %ld)\n", + lfptoa(fp_offset, 6), ntoa(&peer->srcadr), + interval); #endif /* - * Take the absolute value of the offset - */ - tmp_ui = fp_offset->l_ui; - tmp_uf = fp_offset->l_uf; - if (M_ISNEG(tmp_ui, tmp_uf)) { - M_NEG(tmp_ui, tmp_uf); - isneg = 1; - } else - isneg = 0; - - /* * If the clock is way off, don't tempt fate by correcting it. */ - if (tmp_ui >= CLOCK_WAYTOOBIG) { + ftmp = *fp_offset; + if (L_ISNEG(&ftmp)) + L_NEG(&ftmp); + if (ftmp.l_ui >= CLOCK_WAYTOOBIG) { syslog(LOG_ERR, - "Clock appears to be %u seconds %s, something may be wrong", - tmp_ui, isneg>0?"fast":"slow"); + "time error %s is way too large (set clock manually)", + lfptoa(fp_offset, 6)); #ifndef BIGTIMESTEP return (-1); #endif /* BIGTIMESTEP */ - } /* - * Save this offset for later perusal + * If the magnitude of the offset is greater than CLOCK.MAX + * (128 ms), reset the time and frequency. We are quite + * aggresive here, since the intrinsic clock oscillator + * frequency error can be quite large, sometimes over +-300 ppm. + * With something this large and a noisy peer, the casual time + * updates wander right through the acceptable range, causing + * this section to trigger. */ - last_offset = *fp_offset; + } else if (ftmp.l_ui > CLOCK_MAX_I || (ftmp.l_ui == CLOCK_MAX_I + && ftmp.l_uf >= CLOCK_MAX_F)) { + tc_counter = 0; + sys_poll = peer->minpoll; - /* - * If the magnitude of the offset is greater than CLOCK.MAX, - * step the time and reset the registers. - */ - if (tmp_ui > CLOCK_MAX_I || (tmp_ui == CLOCK_MAX_I - && (U_LONG)tmp_uf >= (U_LONG)CLOCK_MAX_F)) { - if (watchdog_timer < CLOCK_MINSTEP) { - /* Mustn't step yet, pretend we adjusted. */ - syslog(LOG_INFO, - "clock correction %s too large (ignored)\n", - lfptoa(fp_offset, 6)); - return (0); - } - syslog(LOG_NOTICE, "clock reset (%s) %s\n", + /* + * Either we are not in synchronization, or we have gone + * CLOCK_MINSTEP (900 s) since the last acceptable + * update. We step the clock and leave the frequency + * alone. Since the clock filter has been reset, the + * dispersions will be high upon recovery and the quick- + * march code below will trigger to keep the clock in + * bounds. + */ + if (sys_leap == LEAP_NOTINSYNC || interval > + CLOCK_MINSTEP) { + step_systime(fp_offset); + syslog(LOG_NOTICE, + + "time reset (%s) %s s", #ifdef SLEWALWAYS - "slew", + "slew", #else - "step", + "step", #endif - lfptoa(fp_offset, 6)); - step_systime(fp_offset); - clock_adjust = 0; - watchdog_timer = 0; - first_adjustment = 1; - pps_update = 0; - return (1); - } + lfptoa(fp_offset, 6)); + return_code = 1; + + /* + * The local clock is out of range, but we haven't + * allowed enough time for the peer (usually a radio + * clock) to recover after a leap second. Pretend we wuz + * never here. + */ + } else + return (return_code); /* - * Here we've got an offset small enough to slew. Note that - * since the offset is small we don't have to carry the damned - * high order longword in our calculations. - * - * The time constant and sample interval are approximated with - * shifts, as in Section 5 of the v3 spec. The spec procedure - * looks strange, as an interval of 64 to 127 seconds seems to - * cause multiplication with 128 (and so on). This code lowers - * the multiplier by one bit. - * - * The time constant update goes after adjust and skew updates, - * as in appendix G. - */ - /* - * If pps samples are valid, update offset, root delay and - * root dispersion. Also, set the system stratum to 1, even if - * the source of approximate time runs at a higher stratum. This - * may be a dramatic surprise to high-stratum clients, since all - * of a sudden this server looks like a stratum-1 clock. - */ - if (pps_control) { - last_offset = pps_offset; - sys_maxd = pps_dispersion; - sys_stratum = 1; - sys_rootdelay = 0; - offset = LFPTOFP(&pps_offset); - if (offset < 0) - offset = -offset; - sys_rootdispersion = offset + pps_dispersion; - } - offset = last_offset.l_f; - - /* - * The pll_control is active when the phase-lock code is + * This code segment works when the clock-adjustment code is * implemented in the kernel, which at present is only in the - * (modified) SunOS 4.1.x, Ultrix 4.3 and OSF/1 kernels. In the + * (modified) SunOS 4.1, Ultrix 4.3 and OSF/1 kernels. In the * case of the DECstation 5000/240 and Alpha AXP, additional - * kernal modifications provide a true microsecond clock. We - * know the scaling of the frequency variable (s_fp) is the - * same as the kernel variable (1 << SHIFT_USEC = 16). - * - * For kernels with the PPS discipline, the current offset and - * dispersion are set from kernel variables to maintain - * beauteous displays, but don't do much of anything. - * - * In the case of stock kernels the phase-lock loop is - * implemented the hard way and the clock_adjust and drift_comp - * computed as required. - */ - if (pll_control) { + * kernel modifications provide a true microsecond clock. We + * know the scaling of the frequency variable (s_fp) is the same + * as the kernel variable (1 << SHIFT_USEC = 16). + */ #if defined(KERNEL_PLL) + } else if (pll_control && pll_enable) { + l_fp pps_offset; + u_fp pps_dispersion; + + /* + * We initialize the structure for the ntp_adjtime() + * system call. We have to convert everything to + * microseconds first. Afterwards, remember the + * frequency offset for the drift file. + */ memset((char *)&ntv, 0, sizeof ntv); ntv.modes = MOD_BITS; if (offset >= 0) { @@ -533,210 +308,310 @@ local_clock(fp_offset, peer) TSFTOTVU(-offset, ntv.offset); ntv.offset = -ntv.offset; } - TSFTOTVU(sys_rootdispersion + sys_rootdelay / 2, ntv.maxerror); + TSFTOTVU(sys_rootdispersion + sys_rootdelay / 2, + ntv.maxerror); TSFTOTVU(sys_rootdispersion, ntv.esterror); - ntv.status = pll_status; - if (pps_update) + ntv.status = pll_status & (STA_PLL | STA_PPSFREQ); + if (pps_update && pll_status & STA_PPSFREQ) ntv.status |= STA_PPSTIME; if (sys_leap & LEAP_ADDSECOND && - sys_leap & LEAP_DELSECOND) + sys_leap & LEAP_DELSECOND) ntv.status |= STA_UNSYNC; else if (sys_leap & LEAP_ADDSECOND) ntv.status |= STA_INS; else if (sys_leap & LEAP_DELSECOND) ntv.status |= STA_DEL; - ntv.constant = time_constant; + ntv.constant = min(peer->ppoll, sys_poll) - NTP_MINPOLL; (void)ntp_adjtime(&ntv); drift_comp = ntv.freq; - if (ntv.status & STA_PPSTIME && ntv.status & STA_PPSSIGNAL - && ntv.shift) { - if (ntv.offset >= 0) { + pll_status = ntv.status; + + /* + * If the kernel pps discipline is working, monitor its + * performance. + */ + if (pll_status & STA_PPSTIME && pll_status & + STA_PPSSIGNAL && ntv.shift) { + if (ntv.offset >= 0) TVUTOTSF(ntv.offset, offset); - } else { + else { TVUTOTSF(-ntv.offset, offset); offset = -offset; } - pps_offset.l_i = pps_offset.l_f = 0; - M_ADDF(pps_offset.l_i, pps_offset.l_f, offset); + L_CLR(&pps_offset); + L_ADDF(&pps_offset, offset); TVUTOTSF(ntv.jitter, tmp); pps_dispersion = (tmp >> 16) & 0xffff; - pps_time = current_time; + if (!pps_control) + syslog(LOG_INFO, + "kernel pps sync enabled"); + pps_control = current_time; record_peer_stats(&loopback_interface->sin, - ctlsysstatus(), &pps_offset, 0, pps_dispersion); - } else - pps_time = 0; -#endif /* KERNEL_PLL */ - } else { - if (offset < 0) { - clock_adjust = -((-offset) >> time_constant); - } else { - clock_adjust = offset >> time_constant; + ctlsysstatus(), fp_offset, 0, + pps_dispersion); } +#endif /* KERNEL_PLL */ - /* - * Calculate the new frequency error. The factor given - * in the spec gives the adjustment per 2**CLOCK_ADJ - * seconds, but we want it as a (scaled) pure ratio, so - * we include that factor now and remove it later. - */ - if (first_adjustment) { - first_adjustment = 0; - } else { + /* + * If the dispersion exceeds 128 ms, we need to quick-march it + * to nominal zero offset and wait for the next update. This is + * necessary when the intrinsic frequency error is large and the + * clock has drifted during the interval the clock filter was + * stabilizing. Note that, if unsynchronized, the dispersion is + * always greater than 128 ms, so we don't need a check for + * that. + */ + } else if (smax > CLOCK_MAX_FP) { + clock_adjust = offset; + + /* + * If the dispersion has increased substantially over the + * previous value, we have a spike which probably should be + * suppressed. A factor of eight has been found reasonable by + * simulation. + */ + } else if (smax > sys_maxd << 3) { + return (0); + + /* + * If the interval between corrections is less than the Allan + * variance intercept point, we use a phase-lock loop to compute + * new values of time and frequency. The bandwidth is controlled + * by the time constant, which is adjusted in response to the + * phase error and dispersion. Note the frequency is not changed + * if the local clock driver is in control. + */ + } else if (interval < allan) { + int time_constant = min(peer->ppoll, sys_poll) - + NTP_MINPOLL; + int ltmp = interval; + + if (offset < 0) + clock_adjust = -(-offset >> time_constant); + else + clock_adjust = offset >> time_constant; + if (interval && !(peer->refclktype == + REFCLK_LOCALCLOCK)) { tmp = peer->maxpoll; - tmp_uf = watchdog_timer; - if (tmp_uf == 0) - tmp_uf = 1; - while (tmp_uf < (1 << peer->maxpoll)) { + while (ltmp < (1 << peer->maxpoll)) { tmp--; - tmp_uf <<= 1; + ltmp <<= 1; } - - /* - * We apply the frequency scaling at the same - * time as the sample interval; this ensures a - * safe right-shift. (as long as it keeps below - * 31 bits, which current parameters should - * ensure. - */ - tmp = (RSH_FRAC_TO_FREQ - tmp) + - time_constant + time_constant; + tmp = (RSH_FRAC_TO_FREQ - tmp) + time_constant + + time_constant; if (offset < 0) - tmp = -((-offset) >> tmp); + tmp = -(-offset >> tmp); else tmp = offset >> tmp; drift_comp += tmp; - if (drift_comp > max_comp) - drift_comp = max_comp; - else if (drift_comp < -max_comp) - drift_comp = -max_comp; } + + /* + * If the interval between corrections is greater than the Allan + * variance intercept point, we use a hybrid frequency-lock loop + * to compute new values of phase and frequency. The following + * code is based on ideas suggested by Judah Levine of NIST and + * used in his "lockclock" implementation of ACTS. The magic + * factor of 4 in the left shift is to convert from s_fp to ppm. + */ + } else { + clock_adjust = offset; + stmp = (offset / interval) << 4; + if (stmp < 0) + drift_comp -= -stmp >> CLOCK_G; + else + drift_comp += stmp >> CLOCK_G; } - watchdog_timer = 0; /* - * Determine when to adjust the time constant and poll interval. + * As a sanity check, we clamp the frequency not to exceed the + * slew rate of the stock Unix adjtime() system call. Finally, + * do a little housekeeping. */ - if (pps_control) - time_constant = 0; - else if (current_time - tcadj_time >= (1 << sys_poll) && !pps_control) { - tcadj_time = current_time; - tmp = offset; - if (tmp < 0) - tmp = -tmp; - tmp = tmp >> (16 + CLOCK_WEIGHTTC - time_constant); - if (tmp > sys_maxd) { - tc_counter = 0; - time_constant--; + if (drift_comp > max_comp) + drift_comp = max_comp; + else if (drift_comp < -max_comp) + drift_comp = -max_comp; + if (interval > (1 << (peer->minpoll - 1))) { + + /* + * Determine when to adjust the poll interval. We do + * this regardless of what source controls the loop, + * since we might flap back and forth between sources. + */ + stmp = LFPTOFP(fp_offset); + if (stmp < 0) + stmp = -stmp; + if (stmp > smax) { + tc_counter -= (int)sys_poll << 1; + if (tc_counter < -CLOCK_LIMIT) { + tc_counter = -CLOCK_LIMIT; + if (sys_poll > peer->minpoll) { + sys_poll--; + tc_counter = 0; + } + } } else { - tc_counter++; - if (tc_counter > CLOCK_HOLDTC) { - tc_counter = 0; - time_constant++; + tc_counter += (int)sys_poll; + if (tc_counter > CLOCK_LIMIT) { + tc_counter = CLOCK_LIMIT; + if (sys_poll < peer->maxpoll) { + sys_poll++; + tc_counter = 0; + } } } - if (time_constant < (int)(peer->minpoll - NTP_MINPOLL)) - time_constant = peer->minpoll - NTP_MINPOLL; - if (time_constant > (int)(peer->maxpoll - NTP_MINPOLL)) - time_constant = peer->maxpoll - NTP_MINPOLL; + + /* + * Calculate the frequency offset and frequency + * stability. These are useful for performance + * monitoring, but do not affect the loop variables. The + * results are scaled as a s_fp in ppm, because we know + * more than we should. + */ + ftmp = *fp_offset; + L_SUB(&ftmp, &last_offset); + clock_frequency = (LFPTOFP(&ftmp) / interval) << 20; + if (clock_frequency < -max_comp) + clock_frequency = -max_comp; + else if (clock_frequency > max_comp) + clock_frequency = max_comp; + stmp = clock_frequency; + if (stmp < 0) + stmp = -stmp; + stmp -= clock_stability; + if (stmp < 0) + clock_stability -= -stmp >> NTP_MAXD; + else + clock_stability += stmp >> NTP_MAXD; } - sys_poll = (u_char)(NTP_MINPOLL + time_constant); + last_offset = *fp_offset; + last_time = current_time; #ifdef DEBUG if (debug > 1) - printf("adj %s, drft %s, tau %3i\n", - mfptoa((clock_adjust<0?-1:0), clock_adjust, 6), - fptoa(drift_comp, 6), time_constant); + printf( + "local_clock: phase %s freq %s err %s allan %ld poll %d\n", + mfptoa((clock_adjust < 0 ? -1 : 0), clock_adjust, + 6), fptoa(drift_comp, 3), fptoa(smax, 6), allan, + sys_poll); #endif /* DEBUG */ - (void) record_loop_stats(&last_offset, &drift_comp, time_constant); + (void) record_loop_stats(fp_offset, drift_comp, sys_poll); /* * Whew. I've had enough. */ - return (0); + return (return_code); } /* - * adj_host_clock - Called every 2**CLOCK_ADJ seconds to update host clock + * adj_host_clock - Called every 1 << CLOCK_ADJ seconds to update host + * clock */ void adj_host_clock() { - register LONG adjustment; -#if defined(PPSPPS) - struct ppsclockev ev; - l_fp ts; -#endif /* PPSPPS */ - - watchdog_timer += (1 << CLOCK_ADJ); - if (watchdog_timer >= NTP_MAXAGE) { - first_adjustment = 1; /* don't use next offset for freq */ - } - if (sys_refskew.l_i >= NTP_MAXSKEW) - sys_refskew.l_f = 0; /* clamp it */ - else - L_ADDUF(&sys_refskew, NTP_SKEWINC); + register long adjustment; + l_fp offset; -#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) -#if defined(PPSPPS) /* - * Note that nothing ugly happens even if the CIOGETEV ioctl is - * not configured. Correct for signal delays (!) for ultimate - * finick. + * Update the dispersion since the last update. Don't allow + * frequency measurements over periods longer than NTP_MAXAGE + * (86400 s = one day). */ - if (fdpps != -1 && ioctl(fdpps, CIOGETEV, (caddr_t)&ev) >= 0) { - static int last_serial = 0; /* avoid stale events */ - - if (last_serial != ev.serial) { - TVUTOTSF(ev.tv.tv_usec, ts.l_uf); - ts.l_ui = 0; /* seconds don't matter here */ - L_SUB(&ts, &pps_delay); - ts.l_uf = ~ts.l_uf; /* map [0.5..1[ into [-0.5..0[ */ - ts.l_ui = (ts.l_f < 0) ? ~0 : 0; /* sign extension */ - (void)pps_sample(&ts); - last_serial = ev.serial; - } - } -#endif /* PPSPPS */ -#endif /* PPS || PPSCLK || PPSPPS */ - if (pps_time && current_time - pps_time > PPS_MAXAGE) - pps_time = 0; - if (pps_update && current_time - pps_update > PPS_MAXUPDATE) - pps_update = 0; - if (pps_time && pps_update) { - if (!pps_control) - syslog(LOG_INFO, "PPS synch"); - pps_control = 1; - } else { + if (current_time - last_time > NTP_MAXAGE) + last_time = 0; + L_ADDUF(&sys_refskew, NTP_SKEWINC); + + /* + * Declare PPS kernel unsync if the pps signal has been heard + * during the last few minutes. + */ + if (pps_control && current_time - pps_control > PPS_MAXAGE) { if (pps_control) - syslog(LOG_INFO, "PPS synch lost"); + syslog(LOG_INFO, "kernel pps sync disabled"); pps_control = 0; } /* - * Resist the following code if the phase-lock loop has been - * implemented in the kernel. + * If the phase-lock loop is not implemented in the kernel, we + * do it the hard way using incremental adjustments and the + * adjtime() system call. */ - if (pll_control) + if (pll_control && pll_enable) return; adjustment = clock_adjust; if (adjustment < 0) - adjustment = -((-adjustment) >> CLOCK_PHASE); + adjustment = -(-adjustment >> CLOCK_PHASE); else adjustment >>= CLOCK_PHASE; clock_adjust -= adjustment; if (drift_comp < 0) - adjustment -= ((-drift_comp) >> RSH_DRIFT_TO_ADJ); + adjustment -= -drift_comp >> RSH_DRIFT_TO_ADJ; else adjustment += drift_comp >> RSH_DRIFT_TO_ADJ; - { l_fp offset; - offset.l_i = 0; - offset.l_f = adjustment; - adj_systime(&offset); + /* + * Intricate wrinkle. If the local clock driver is in use and + * selected for synchronization, somebody else may be tinker the + * adjtime() syscall. In this case we have to avoid calling + * adjtime(), since that may truncate the other guy's requests. + * That means the local clock fudge time and frequency + * adjustments don't work in that case. Caveat empty. + */ + if (sys_peer) { + if (sys_peer->refclktype == REFCLK_LOCALCLOCK && + sys_peer->flags & FLAG_PREFER) + return; } + L_CLR(&offset); + L_ADDF(&offset, adjustment); + adj_systime(&offset); +} + + +/* + * adj_frequency - adjust local clock frequency + */ +void +adj_frequency(freq) + s_fp freq; /* frequency (ppm) */ +{ +#if defined(KERNEL_PLL) + struct timex ntv; +#endif /* KERNEL_PLL */ + + /* + * This routine adjusts the frequency offset. It is used by the + * local clock driver to adjust frequency when no external + * discipline source is available and by the acts driver when + * the interval between updates is greater than 1 << + * NTP_MAXPOLL. Note that the maximum offset is limited by + * max_comp when the daemon pll is used, but the maximum may be + * different when the kernel pll is used. + */ + drift_comp += freq; + if (drift_comp > max_comp) + drift_comp = max_comp; + else if (drift_comp < -max_comp) + drift_comp = -max_comp; +#if defined(KERNEL_PLL) + /* + * If the phase-lock code is implemented in the kernel, set the + * kernel frequency as well, but be sure to set drift_comp to + * the actual frequency. + */ + if (!(pll_control && pll_enable)) + return; + memset((char *)&ntv, 0, sizeof ntv); + ntv.modes = MOD_FREQUENCY; + ntv.freq = freq + drift_comp; + (void)ntp_adjtime(&ntv); + drift_comp = ntv.freq; +#endif /* KERNEL_PLL */ } @@ -749,106 +624,76 @@ loop_config(item, lfp_value, int_value) l_fp *lfp_value; int int_value; { - s_fp tmp; #if defined(KERNEL_PLL) struct timex ntv; #endif /* KERNEL_PLL */ +#ifdef DEBUG + if (debug) { + printf("loop_config %d %s %x\n", item, + lfptoa(lfp_value, 3), int_value); + } +#endif switch (item) { - case LOOP_DRIFTCOMP: - tmp = LFPTOFP(lfp_value); - if (tmp >= max_comp || tmp <= -max_comp) { - syslog(LOG_ERR, - "loop_config: frequency offset %s in ntp.conf file is too large", - fptoa(tmp, 5)); - } else { - drift_comp = tmp; + + case LOOP_DRIFTCOMP: + drift_comp = LFPTOFP(lfp_value); + if (drift_comp > max_comp) + drift_comp = max_comp; + if (drift_comp < -max_comp) + drift_comp = -max_comp; #if defined(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. - */ + /* + * 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. We do this + * whether or not the use of the kernel mods is + * requested, in order to clear out the trash from + * possible prior customers. + */ + memset((char *)&ntv, 0, sizeof ntv); + pll_status = int_value & (STA_PLL | STA_PPSFREQ); + if (pll_status & STA_PLL) pll_control = 1; - pll_status = STA_PLL | STA_PPSFREQ; - ntv.modes = MOD_BITS | MOD_FREQUENCY; - ntv.offset = 0; + else + pll_control = 0; + ntv.modes = MOD_BITS | MOD_FREQUENCY; + if (pll_status) { ntv.freq = drift_comp; ntv.maxerror = NTP_MAXDISPERSE; ntv.esterror = NTP_MAXDISPERSE; ntv.status = pll_status | STA_UNSYNC; - ntv.constant = time_constant; - newsigsys.sv_handler = pll_trap; - newsigsys.sv_mask = 0; - newsigsys.sv_flags = 0; - if ((sigvec(SIGSYS, &newsigsys, &sigsys))) - syslog(LOG_ERR, - "sigvec() fails to save SIGSYS trap: %m\n"); - (void)ntp_adjtime(&ntv); - if ((sigvec(SIGSYS, &sigsys, (struct sigvec *)NULL))) - syslog(LOG_ERR, - "sigvec() fails to restore SIGSYS trap: %m\n"); - if (pll_control) - syslog(LOG_NOTICE, - "using kernel phase-lock loop %04x", - ntv.status); - else - syslog(LOG_NOTICE, - "using xntpd phase-lock loop"); -#endif /* KERNEL_PLL */ - + ntv.constant = sys_poll - NTP_MINPOLL; } + newsigsys.sv_handler = pll_trap; + newsigsys.sv_mask = 0; + newsigsys.sv_flags = 0; + if ((sigvec(SIGSYS, &newsigsys, &sigsys))) + syslog(LOG_ERR, + "sigvec() fails to save SIGSYS trap: %m"); + (void)ntp_adjtime(&ntv); + if ((sigvec(SIGSYS, &sigsys, + (struct sigvec *)NULL))) + syslog(LOG_ERR, + "sigvec() fails to restore SIGSYS trap: %m"); + if (pll_control) + syslog(LOG_NOTICE, + "using kernel phase-lock loop %04x", + ntv.status); + else + syslog(LOG_NOTICE, + "using xntpd phase-lock loop"); +#endif /* KERNEL_PLL */ break; - - case LOOP_PPSDELAY: - pps_delay = *lfp_value; - break; - -#if defined(PPSCLK) - case LOOP_PPSBAUD: -#if defined(HAVE_TERMIOS) - /* - * System V TERMIOS serial line parameters - * (termios interface) - */ - { struct termios ttyb, *ttyp; - if (fdpps == -1) - return; - - ttyp = &ttyb; - if (tcgetattr(fdpps, ttyp) < 0) - return; - ttyp->c_cflag = CS8|CLOCAL|CREAD | int_value; - if (tcsetattr(fdpps, TCSANOW, ttyp) < 0) - return; - } -#endif /* HAVE_TERMIOS */ -#if defined(HAVE_BSD_TTYS) - - /* - * 4.3bsd serial line parameters (sgttyb interface) - */ - { struct sgttyb ttyb; - - if (fdpps == -1 || ioctl(fdpps, TIOCGETP, &ttyb) < 0) - return; - ttyb.sg_ispeed = ttyb.sg_ospeed = SYS_BAUD(int_value); - if (ioctl(fdpps, TIOCSETP, &ttyb) < 0) - return; - } -#endif /* HAVE_BSD_TTYS */ - pps_baud = int_value; - break; -#endif /* PPSCLK */ - default: + default: /* sigh */ break; } } + #if defined(KERNEL_PLL) /* * _trap - trap processor for undefined syscalls @@ -865,128 +710,3 @@ pll_trap() } #endif /* KERNEL_PLL */ -#if defined(PPSCLK) -/* - * pps_receive - compute and store 1-pps signal offset - * - * This routine is called once per second when the 1-pps signal is - * present. It calculates the offset of the local clock relative to the - * 1-pps signal and saves in a circular buffer for later use. If the - * clock line discipline is active, its timestamp is used; otherwise, - * the buffer timestamp is used. - */ -static void -pps_receive(rbufp) - struct recvbuf *rbufp; -{ - u_char *dpt; /* buffer pointer */ - l_fp ts; /* l_fp temps */ - int dpend; /* buffer length */ - - /* - * Set up pointers, check the buffer length, discard intercept - * character and convert unix timeval to timestamp format. - */ - dpt = (u_char *)&rbufp->recv_space; - dpend = rbufp->recv_length; -#if !defined(HPUXGADGET) - dpt++; - dpend--; -#endif /* HPUXGADGET */ - if (dpend != sizeof(struct timeval) || !buftvtots((char *)dpt, &ts)) - ts = rbufp->recv_time; - - /* - * Correct for uart and os delay and process sample offset. - */ - L_SUB(&ts, &pps_delay); - L_NEG(&ts); - (void)pps_sample(&ts); -} -#endif /* PPSCLK */ - -#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) -/* - * pps_sample - process pps sample offset - */ -int pps_sample(tsr) - l_fp *tsr; -{ - int i, j; /* temp ints */ - LONG sort[NPPS]; /* temp array for sorting */ - l_fp lftemp, ts; /* l_fp temps */ - u_fp utemp; /* u_fp temp */ - LONG ltemp; /* long temp */ - - /* - * Note the seconds offset is already in the low-order timestamp - * doubleword, so all we have to do is sign-extend and invert - * it. The resulting offset is believed only if within - * CLOCK_MAX. - */ - ts = *tsr; - lftemp.l_i = lftemp.l_f = 0; - M_ADDF(lftemp.l_i, lftemp.l_f, ts.l_f); - if (ts.l_f <= -CLOCK_MAX_F || ts.l_f >= CLOCK_MAX_F) { - pps_time = 0; - return (-1); - } - - /* - * Save the sample in a circular buffer for later processing. - */ - nsamples++; - i = ((int)(nsamples)) % NPPS; - samples[i] = lftemp.l_f; - if (i != NPPS-1) - return (0); - - /* - * When the buffer fills up, construct an array of sorted - * samples. - */ - pps_time = current_time; - for (i = 0; i < NPPS; i++) { - sort[i] = samples[i]; - for (j = 0; j < i; j++) { - if (sort[j] > sort[i]) { - ltemp = sort[j]; - sort[j] = sort[i]; - sort[i] = ltemp; - } - } - } - - /* - * Compute offset as the average of all samples in the filter - * less PPS_TRIM samples trimmed from the beginning and end, - * dispersion as the difference between max and min of samples - * retained. The system stratum, root delay and root dispersion - * are also set here. - */ - pps_offset.l_i = pps_offset.l_f = 0; - for (i = PPS_TRIM; i < NPPS - PPS_TRIM; i++) - M_ADDF(pps_offset.l_i, pps_offset.l_f, sort[i]); - if (L_ISNEG(&pps_offset)) { - L_NEG(&pps_offset); - for (i = 0; i < PPS_FAC; i++) - L_RSHIFT(&pps_offset); - L_NEG(&pps_offset); - } else { - for (i = 0; i < PPS_FAC; i++) - L_RSHIFT(&pps_offset); - } - lftemp.l_i = 0; - lftemp.l_f = sort[NPPS-1-PPS_TRIM] - sort[PPS_TRIM]; - pps_dispersion = LFPTOFP(&lftemp); -#ifdef DEBUG - if (debug) - printf("pps_filter: %s %s %s\n", lfptoa(&pps_delay, 6), - lfptoa(&pps_offset, 6), lfptoa(&lftemp, 5)); -#endif /* DEBUG */ - record_peer_stats(&loopback_interface->sin, ctlsysstatus(), - &pps_offset, 0, pps_dispersion); - return (0); -} -#endif /* PPS || PPSCLK || PPSPPS */ - diff --git a/usr.sbin/xntpd/xntpd/ntp_monitor.c b/usr.sbin/xntpd/xntpd/ntp_monitor.c index e2d2eb51adac..55bd5d542b90 100644 --- a/usr.sbin/xntpd/xntpd/ntp_monitor.c +++ b/usr.sbin/xntpd/xntpd/ntp_monitor.c @@ -1,6 +1,7 @@ -/* ntp_monitor.c,v 3.1 1993/07/06 01:11:21 jbj Exp +/* * ntp_monitor.c - monitor who is using the xntpd server */ +#include <stdio.h> #include <sys/types.h> #include <signal.h> #include <errno.h> @@ -33,6 +34,8 @@ * 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 */ /* @@ -40,9 +43,15 @@ * 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 */ -#define MAXMONMEM 400 /* we allocate up to 400 structures */ +#ifndef MAXMONMEM +#define MAXMONMEM 600 /* we allocate up to 400 structures */ +#endif +#ifndef MONMEMINC #define MONMEMINC 40 /* allocate them 40 at a time */ +#endif /* * Hashing stuff @@ -55,17 +64,15 @@ * 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; /* Pointer to array of hash buckets */ -static int *mon_hash_count; /* Point to hash count stats keeper */ +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; +static struct mon_data *mon_free; /* the free list or null if none */ -static int mon_free_mem; /* number of structures on free list */ static int mon_total_mem; /* total number of structures allocated */ static int mon_mem_increments; /* number of times we've called malloc() */ @@ -79,9 +86,10 @@ static int mon_have_memory; /* * Imported from the timer module */ -extern U_LONG current_time; +extern u_long current_time; static void mon_getmoremem P((void)); +static void remove_from_hash P((struct mon_data *)); /* * init_mon - initialize monitoring global data @@ -96,12 +104,10 @@ init_mon() mon_enabled = MON_OFF; mon_have_memory = 0; - mon_free_mem = 0; mon_total_mem = 0; mon_mem_increments = 0; - mon_free = 0; - mon_hash = 0; - mon_hash_count = 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); } @@ -114,8 +120,6 @@ void mon_start(mode) int mode; { - register struct mon_data *md; - register int i; if (mon_enabled != MON_OFF) { mon_enabled |= mode; @@ -125,26 +129,13 @@ mon_start(mode) return; /* Ooops.. */ if (!mon_have_memory) { - mon_hash = (struct mon_data *) - emalloc(MON_HASH_SIZE * sizeof(struct mon_data)); - memset((char *)mon_hash, 0, - MON_HASH_SIZE*sizeof(struct mon_data)); - mon_hash_count = (int *)emalloc(MON_HASH_SIZE * sizeof(int)); - mon_free_mem = 0; mon_total_mem = 0; mon_mem_increments = 0; - mon_free = 0; + mon_free = NULL; mon_getmoremem(); mon_have_memory = 1; } - md = mon_hash; - for (i = 0; i < MON_HASH_SIZE; i++, md++) { - md->hash_next = md; - md->hash_prev = md; - *(mon_hash_count + i) = 0; - } - mon_mru_list.mru_next = &mon_mru_list; mon_mru_list.mru_prev = &mon_mru_list; @@ -162,7 +153,7 @@ void mon_stop(mode) int mode; { - register struct mon_data *md; + register struct mon_data *md, *md_next; register int i; if (mon_enabled == MON_OFF) @@ -177,15 +168,14 @@ mon_stop(mode) /* * Put everything back on the free list */ - md = mon_hash; - for (i = 0; i < MON_HASH_SIZE; i++, md++) { - if (md->hash_next != md) { - md->hash_prev->hash_next = mon_free; - mon_free = md->hash_next; - mon_free_mem += *(mon_hash_count + i); - md->hash_next = md; - md->hash_prev = md; - *(mon_hash_count + i) = 0; + 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; } } @@ -206,10 +196,9 @@ monitor(rbufp) { register struct pkt *pkt; register struct mon_data *md; - register U_LONG netnum; + register u_long netnum; register int hash; register int mode; - register struct mon_data *mdhash; if (mon_enabled == MON_OFF) return; @@ -219,9 +208,11 @@ monitor(rbufp) hash = MON_HASH(netnum); mode = PKT_MODE(pkt->li_vn_mode); - md = (mon_hash + hash)->hash_next; - while (md != (mon_hash + hash)) { - if (md->rmtadr == netnum && md->mode == (u_char)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); @@ -248,16 +239,16 @@ monitor(rbufp) * guy. Get him some memory, either from the free list * or from the tail of the MRU list. */ - if (mon_free_mem == 0 && mon_total_mem >= MAXMONMEM) { + 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; - md->hash_next->hash_prev = md->hash_prev; - md->hash_prev->hash_next = md->hash_next; - *(mon_hash_count + MON_HASH(md->rmtadr)) -= 1; + + remove_from_hash(md); + /* * Get it from FIFO list */ @@ -265,11 +256,10 @@ monitor(rbufp) md->fifo_next->fifo_prev = md->fifo_prev; } else { - if (mon_free_mem == 0) - mon_getmoremem(); + if (mon_free == NULL) /* if free list empty */ + mon_getmoremem(); /* then get more */ md = mon_free; mon_free = md->hash_next; - mon_free_mem--; } /* @@ -282,18 +272,19 @@ monitor(rbufp) 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; /* - * Shuffle him into the hash table, inserting him at the - * end. Also put him on top of the MRU list + * Drop him into front of the hash table. + * Also put him on top of the MRU list * and at bottom of FIFO list */ - mdhash = mon_hash + MON_HASH(netnum); - md->hash_next = mdhash; - md->hash_prev = mdhash->hash_prev; - mdhash->hash_prev->hash_next = md; - mdhash->hash_prev = md; - *(mon_hash_count + MON_HASH(netnum)) += 1; + + md->hash_next = mon_hash[hash]; + mon_hash[hash] = md; md->mru_next = mon_mru_list.mru_next; md->mru_prev = &mon_mru_list; @@ -315,7 +306,7 @@ mon_getmoremem() { register struct mon_data *md; register int i; - struct mon_data *freedata; + struct mon_data *freedata; /* 'old' free list (null) */ md = (struct mon_data *)emalloc(MONMEMINC * sizeof(struct mon_data)); freedata = mon_free; @@ -331,7 +322,28 @@ mon_getmoremem() */ md->hash_next = freedata; - mon_free_mem += MONMEMINC; mon_total_mem += MONMEMINC; mon_mem_increments++; } + +static void +remove_from_hash(md) +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/usr.sbin/xntpd/xntpd/ntp_peer.c b/usr.sbin/xntpd/xntpd/ntp_peer.c index 9d8ec35dee78..389cc2c75671 100644 --- a/usr.sbin/xntpd/xntpd/ntp_peer.c +++ b/usr.sbin/xntpd/xntpd/ntp_peer.c @@ -1,4 +1,4 @@ -/* ntp_peer.c,v 3.1 1993/07/06 01:11:22 jbj Exp +/* * ntp_peer.c - management of data maintained for peer associations */ #include <stdio.h> @@ -49,11 +49,11 @@ u_short current_association_ID; /* * 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 */ +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 */ /* @@ -64,7 +64,7 @@ extern struct interface *any_interface; /* * Timer queue and current time. Imported from the timer module. */ -extern U_LONG current_time; +extern u_long current_time; extern struct event timerqueue[]; /* @@ -77,7 +77,7 @@ static struct peer init_peer_alloc[INIT_PEER_ALLOC]; * we try to get their poll update timers initialized to different values * to prevent us from sending big clumps of data all at once. */ -U_LONG init_peer_starttime; +u_long init_peer_starttime; extern int initializing; extern int debug; @@ -192,12 +192,14 @@ findexistingpeer(addr, start_peer) * findpeer - find and return a peer in the hash table. */ struct peer * -findpeer(srcadr, dstadr) +findpeer(srcadr, dstadr, fd) struct sockaddr_in *srcadr; struct interface *dstadr; + int fd; { register struct peer *any_inter_peer; register struct peer *peer; + register struct peer *best = (struct peer *) 0; int hash; findpeer_calls++; @@ -207,9 +209,16 @@ findpeer(srcadr, dstadr) for (peer = peer_hash[hash]; peer != 0; peer = peer->next) { if (NSRCADR(srcadr) == NSRCADR(&peer->srcadr) && NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) { - if (peer->dstadr == dstadr) - return peer; /* got it! */ + if (peer->dstadr == dstadr) { + int rfd = (peer->cast_flags & MDF_BCAST) ? + dstadr->bfd : dstadr->fd; + + if (rfd == fd) + return peer; /* got it! */ + best = peer; + } if (peer->dstadr == any_interface) { + /* * We shouldn't have more than one * instance of the peer in the table, @@ -223,9 +232,22 @@ findpeer(srcadr, dstadr) "two instances of default interface for %s in hash table", ntoa(srcadr)); } + + /* + * Multicast hacks to determine peer when a + * packet arrives and there exists an assoc. + * with src in client/server mode + */ + if (((dstadr == any_interface) || (peer->cast_flags & + MDF_MCAST)) && peer->flags & FLAG_MCAST2) + return peer; } } + if(best) { + return best; + } + /* * If we didn't find the specific peer but found a wild card, * modify the interface and return him. @@ -349,13 +371,13 @@ peer_config(srcadr, dstadr, hmode, version, minpoll, maxpoll, flags, ttl, key) int maxpoll; int flags; int ttl; - U_LONG key; + u_long key; { register struct peer *peer; #ifdef DEBUG if (debug) - printf("peer_config: addr %s mode %d version %d minpoll %d maxpoll %d flags %d ttl %d key %u\n", + printf("peer_config: addr %s mode %d version %d minpoll %d maxpoll %d flags %d ttl %d key %lu\n", ntoa(srcadr), hmode, version, minpoll, maxpoll, flags, ttl, key); #endif @@ -387,8 +409,10 @@ peer_config(srcadr, dstadr, hmode, version, minpoll, maxpoll, flags, ttl, key) peer->maxpoll = (u_char)maxpoll; peer->hpoll = peer->minpoll; peer->ppoll = peer->minpoll; - peer->flags = ((u_char)(flags|FLAG_CONFIG)) - |(peer->flags & (FLAG_REFCLOCK|FLAG_DEFBDELAY)); + peer->flags = ((u_char)(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; return peer; @@ -418,7 +442,7 @@ newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll, ttl, key) int minpoll; int maxpoll; int ttl; - U_LONG key; + u_long key; { register struct peer *peer; register int i; @@ -427,8 +451,8 @@ newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll, ttl, key) * Some dirt here. Some of the initialization requires * knowlege of our system state. */ - extern U_LONG sys_bdelay; - extern LONG sys_clock; + extern s_fp sys_bdelay; + extern long sys_clock; if (peer_free_count == 0) getmorepeermem(); @@ -453,6 +477,11 @@ newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll, ttl, key) 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; peer->hmode = (u_char)hmode; peer->version = (u_char)version; peer->minpoll = (u_char)minpoll; @@ -462,7 +491,6 @@ newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll, ttl, key) peer->ttl = ttl; peer->keyid = key; peer->estbdelay = sys_bdelay; - peer->flags |= FLAG_DEFBDELAY; peer->leap = LEAP_NOTINSYNC; peer->precision = DEFPRECISION; peer->dispersion = NTP_MAXDISPERSE; @@ -616,8 +644,6 @@ peer_reset(peer) peer->processed = 0; peer->badauth = 0; peer->bogusorg = 0; - peer->bogusrec = 0; - peer->bogusdelay = 0; peer->oldpkt = 0; peer->seldisptoolarge = 0; peer->selbroken = 0; diff --git a/usr.sbin/xntpd/xntpd/ntp_proto.c b/usr.sbin/xntpd/xntpd/ntp_proto.c index a3f744eae881..7889d9315e47 100644 --- a/usr.sbin/xntpd/xntpd/ntp_proto.c +++ b/usr.sbin/xntpd/xntpd/ntp_proto.c @@ -1,4 +1,4 @@ -/* ntp_proto.c,v 3.1 1993/07/06 01:11:23 jbj Exp +/* * ntp_proto.c - NTP version 3 protocol machinery */ #include <stdio.h> @@ -10,50 +10,51 @@ #include "ntp_unixtime.h" /* - * System variables are declared here. See Section 3.2 of - * the specification. + * 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 */ s_fp sys_rootdelay; /* distance to current sync source */ u_fp sys_rootdispersion; /* dispersion of system clock */ -U_LONG sys_refid; /* reference source for local clock */ +u_long sys_refid; /* reference source for local clock */ l_fp sys_offset; /* combined offset from clock_select */ u_fp sys_maxd; /* dispersion of selected peer */ l_fp sys_reftime; /* time we were last updated */ l_fp sys_refskew; /* accumulated skew since last update */ -struct peer *sys_peer; /* our current peer */ -u_char sys_poll; /* log2 of desired system poll interval */ -extern LONG sys_clock; /* second part of current time - now in systime.c */ -LONG sys_lastselect; /* sys_clock at last synch-dist update */ +struct peer *sys_peer; /* our current peer */ +u_char sys_poll; /* log2 of system poll interval */ +extern long sys_clock; /* second part of current time */ +long sys_lastselect; /* sys_clock at last synch update */ /* - * Non-specified system state variables. + * Nonspecified system state variables. */ int sys_bclient; /* we set our time to broadcasts */ -U_LONG sys_bdelay; /* default delay to use for broadcasting */ +s_fp sys_bdelay; /* broadcast client default delay */ int sys_authenticate; /* authenticate time used for syncing */ - -U_LONG sys_authdelay; /* ts fraction, time it takes for encrypt() */ +u_char consensus_leap; /* mitigated leap bits */ +u_long sys_authdelay; /* encryption time (l_fp fraction) */ +u_char leap_consensus; /* consensus of survivor leap bits */ /* * Statistics counters */ -U_LONG sys_stattime; /* time when we started recording */ -U_LONG sys_badstratum; /* packets with invalid incoming 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 authorization */ -U_LONG sys_limitrejected; /* pkts rejected due toclient count per net */ +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 toclient count per net */ /* * Imported from ntp_timer.c */ -extern U_LONG current_time; +extern u_long current_time; extern struct event timerqueue[]; /* @@ -64,11 +65,16 @@ extern struct interface *any_interface; /* * Imported from ntp_loopfilter.c */ -extern int pps_control; -extern U_LONG pps_update; +extern int pll_enable; +extern int pps_update; + +/* + * Imported from ntp_util.c + */ +extern int stats_control; /* - * The peer hash table. Imported from ntp_peer.c + * The peer hash table. Imported from ntp_peer.c */ extern struct peer *peer_hash[]; extern int peer_hash_count[]; @@ -81,18 +87,40 @@ extern int debug; static void clear_all P((void)); /* - * transmit - Transmit Procedure. See Section 3.4.1 of the specification. + * transmit - Transmit Procedure. See Section 3.4.1 of the + * specification. */ void transmit(peer) register struct peer *peer; { struct pkt xpkt; /* packet to send */ - U_LONG peer_timer; + u_long peer_timer; + u_fp precision; + int bool; - if ((peer->hmode != MODE_BROADCAST && peer->hmode != MODE_BCLIENT) || - (peer->hmode == MODE_BROADCAST && sys_leap != LEAP_NOTINSYNC)) { - U_LONG xkeyid; + /* + * 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. + */ + bool = 0; + if (peer->hmode != MODE_BROADCAST) { + if (peer->hmode != MODE_BCLIENT) + bool = 1; + } else if (sys_peer != 0 && sys_leap != LEAP_NOTINSYNC) { + if (!(sys_peer->refclktype == REFCLK_LOCALCLOCK && + !(sys_peer->flags & FLAG_PREFER))) + bool = 1; + } + if (bool) { + u_long xkeyid; + int find_rtt = (peer->cast_flags & MDF_MCAST) && + peer->hmode != MODE_BROADCAST; /* * Figure out which keyid to include in the packet @@ -108,38 +136,43 @@ transmit(peer) /* * Make up a packet to send. */ - xpkt.li_vn_mode - = PKT_LI_VN_MODE(sys_leap, peer->version, peer->hmode); + 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(sys_rootdelay); - xpkt.rootdispersion = - HTONS_FP(sys_rootdispersion + - (FP_SECOND >> (-(int)sys_precision)) + - LFPTOFP(&sys_refskew)); + precision = FP_SECOND >> -(int)sys_precision; + if (precision == 0) + precision = 1; + xpkt.rootdispersion = HTONS_FP(sys_rootdispersion + + precision + LFPTOFP(&sys_refskew)); xpkt.refid = sys_refid; HTONL_FP(&sys_reftime, &xpkt.reftime); HTONL_FP(&peer->org, &xpkt.org); HTONL_FP(&peer->rec, &xpkt.rec); /* - * Decide whether to authenticate or not. If so, call encrypt() - * to fill in the rest of the frame. If not, just add in the - * xmt timestamp and send it quick. + * Decide whether to authenticate or not. If so, call + * encrypt() to fill in the rest of the frame. If not, + * just add in the xmt timestamp and send it quick. */ if (peer->flags & FLAG_AUTHENABLE) { int sendlen; xpkt.keyid = htonl(xkeyid); - auth1crypt(xkeyid, (U_LONG *)&xpkt, LEN_PKT_NOMAC); + auth1crypt(xkeyid, (U_LONG *)&xpkt, + LEN_PKT_NOMAC); get_systime(&peer->xmt); L_ADDUF(&peer->xmt, sys_authdelay); HTONL_FP(&peer->xmt, &xpkt.xmt); sendlen = auth2crypt(xkeyid, (U_LONG *)&xpkt, LEN_PKT_NOMAC); - sendpkt(&(peer->srcadr), peer->dstadr, &xpkt, - sendlen + LEN_PKT_NOMAC); + sendpkt(&peer->srcadr, find_rtt ? + any_interface : peer->dstadr, + ((peer->cast_flags & MDF_MCAST) && !find_rtt) ? + peer->ttl : -7, &xpkt, sendlen + + LEN_PKT_NOMAC); #ifdef DEBUG if (debug > 1) printf("transmit auth to %s\n", @@ -148,15 +181,21 @@ transmit(peer) peer->sent++; } else { /* - * Get xmt timestamp, then send it without mac field + * Get xmt timestamp, then send it without mac + * field */ + int find_rtt = (peer->cast_flags & MDF_MCAST) && + peer->dstadr != any_interface; get_systime(&(peer->xmt)); HTONL_FP(&peer->xmt, &xpkt.xmt); - sendpkt(&(peer->srcadr), peer->dstadr, &xpkt, - LEN_PKT_NOMAC); + sendpkt(&(peer->srcadr), find_rtt ? + any_interface : peer->dstadr, + ((peer->cast_flags & MDF_MCAST) && !find_rtt) ? + peer->ttl : -8, &xpkt, LEN_PKT_NOMAC); #ifdef DEBUG if (debug > 1) - printf("transmit to %s\n", ntoa(&(peer->srcadr))); + printf("transmit to %s\n", + ntoa(&(peer->srcadr))); #endif peer->sent++; } @@ -166,40 +205,49 @@ transmit(peer) u_char opeer_reach; /* * Determine reachability and diddle things if we - * haven't heard from the host for a while. + * haven't heard from the host for a while. If we are + * about to become unreachable and are a + * broadcast/multicast client, the server has refused to + * boogie in client/server mode, so we switch to + * MODE_BCLIENT anyway and wait for subsequent + * broadcasts. */ opeer_reach = peer->reach; + if (opeer_reach & 0x80 && peer->flags & FLAG_MCAST2) { + peer->hmode = MODE_BCLIENT; + } peer->reach <<= 1; if (peer->reach == 0) { if (opeer_reach != 0) report_event(EVNT_UNREACH, peer); /* * Clear this guy out. No need to redo clock - * selection since by now this guy won't be a player + * selection since by now this guy won't be a + * player */ if (peer->flags & FLAG_CONFIG) { if (opeer_reach != 0) { peer_clear(peer); - peer->timereachable = current_time; + peer->timereachable = + current_time; } - } else { - unpeer(peer); - return; } /* - * While we have a chance, if our system peer - * is zero or his stratum is greater than the - * last known stratum of this guy, make sure - * hpoll is clamped to the minimum before - * resetting the timer. - * If the peer has been unreachable for a while - * and we have a system peer who is at least his - * equal, we may want to ramp his polling interval - * up to avoid the useless traffic. + * While we have a chance, if our system peer is + * zero or his stratum is greater than the last + * known stratum of this guy, make sure hpoll is + * clamped to the minimum before resetting the + * timer. If the peer has been unreachable for a + * while and we have a system peer who is at + * least his equal, we may want to ramp his + * polling interval up to avoid the useless + * traffic. */ - if (sys_peer == 0 - || sys_peer->stratum > peer->stratum) { + if (sys_peer == 0) { + peer->hpoll = peer->minpoll; + peer->unreach = 0; + } else if (sys_peer->stratum > peer->stratum) { peer->hpoll = peer->minpoll; peer->unreach = 0; } else { @@ -223,8 +271,9 @@ transmit(peer) peer->valid--; if (peer->hpoll > peer->minpoll) peer->hpoll--; - off.l_ui = off.l_uf = 0; - clock_filter(peer, &off, (s_fp)0, (u_fp)NTP_MAXDISPERSE); + L_CLR(&off); + clock_filter(peer, &off, (s_fp)0, + (u_fp)NTP_MAXDISPERSE); if (peer->flags & FLAG_SYSPEER) clock_select(); } else { @@ -238,24 +287,34 @@ transmit(peer) } /* - * Finally, adjust the hpoll variable for special conditions. + * Finally, adjust the hpoll variable for special conditions. If + * we are a broadcast/multicast client, we use the server poll + * interval if listening for broadcasts and one-eighth this + * interval if in client/server mode. The following clamp + * prevents madness. If this is the system poll, sys_poll + * controls hpoll. */ - if (peer->hmode == MODE_BCLIENT) - peer->hpoll = peer->ppoll; - else if (peer->flags & FLAG_SYSPEER && - peer->hpoll > sys_poll) - peer->hpoll = max(peer->minpoll, sys_poll); + if (peer->flags & FLAG_MCAST2) { + if (peer->hmode == MODE_BCLIENT) + peer->hpoll = peer->ppoll; + else + peer->hpoll = peer->ppoll - 3; + } else if (peer->flags & FLAG_SYSPEER) + peer->hpoll = sys_poll; + if (peer->hpoll < peer->minpoll) + peer->hpoll = peer->minpoll; /* - * Arrange for our next timeout. hpoll will be less than - * maxpoll for sure. + * Arrange for our next timeout. hpoll will be less than maxpoll + * for sure. */ if (peer->event_timer.next != 0) /* * Oops, someone did already. */ TIMER_DEQUEUE(&peer->event_timer); - peer_timer = 1 << (int)max((u_char)min(peer->ppoll, peer->hpoll), peer->minpoll); + peer_timer = 1 << (int)max((u_char)min(peer->ppoll, + peer->hpoll), peer->minpoll); peer->event_timer.event_time = current_time + peer_timer; TIMER_ENQUEUE(timerqueue, &peer->event_timer); } @@ -274,7 +333,7 @@ receive(rbufp) int has_mac; int trustable; int is_authentic; - U_LONG hiskeyid; + u_long hiskeyid; struct peer *peer2; #ifdef DEBUG @@ -313,7 +372,7 @@ receive(rbufp) } /* - * Catch private mode packets. Dump it if queries not allowed. + * Catch private mode packets. Dump it if queries not allowed. */ if (PKT_MODE(pkt->li_vn_mode) == MODE_PRIVATE) { if (restrict & RES_NOQUERY) @@ -340,16 +399,16 @@ receive(rbufp) 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() + * 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 & RES_LIMITED) { - extern U_LONG client_limit; + extern u_long client_limit; sys_limitrejected++; syslog(LOG_NOTICE, - "rejected mode %d request from %s - per net client limit (%d) exceeded", + "rejected mode %d request from %s - per net client limit (%d) exceeded", PKT_MODE(pkt->li_vn_mode), ntoa(&rbufp->recv_srcadr), client_limit); return; @@ -364,36 +423,34 @@ receive(rbufp) } /* - * Find the peer. This will return a null if this guy - * isn't in the database. + * Find the peer. This will return a null if this guy isn't in + * the database. */ - peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr); + peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, rbufp->fd); /* * Check the length for validity, drop the packet if it is - * not as expected. - * - * If this is a client mode poll, go no further. Send back - * his time and drop it. + * not as expected. If this is a client mode poll, go no + * further. Send back his time and drop it. * * The scheme we use for authentication is this. If we are * running in non-authenticated mode, we accept both frames * which are authenticated and frames which aren't, but don't - * authenticate. We do record whether the frame had a mac field + * authenticate. We do record whether the frame had a mac field * or not so we know what to do on output. * * If we are running in authenticated mode, we only trust frames * which have authentication attached, which are validated and - * which are using one of our trusted keys. We respond to all - * other pollers without saving any state. If a host we are + * which are using one of our trusted keys. We respond to all + * other pollers without saving any state. If a host we are * passively peering with changes his key from a trusted one to * an untrusted one, we immediately unpeer with him, reselect * the clock and treat him as an unmemorable client (this is * a small denial-of-service hole I'll have to think about). * If a similar event occurs with a configured peer we drop the - * frame and hope he'll revert to our key again. If we get a + * frame and hope he'll revert to our key again. If we get a * frame which can't be authenticated with the given key, we - * drop it. Either we disagree on the keys or someone is trying + * drop it. Either we disagree on the keys or someone is trying * some funny stuff. */ @@ -405,9 +462,10 @@ receive(rbufp) has_mac = rbufp->recv_length - LEN_PKT_NOMAC; hiskeyid = ntohl(pkt->keyid); #ifdef DEBUG - if (debug > 3) - printf("receive: pkt is %d octets, mac %d octets long, keyid %d\n", - rbufp->recv_length, has_mac, hiskeyid); + if (debug > 2) + printf( + "receive: pkt is %d octets, mac %d octets long, keyid %ld\n", + rbufp->recv_length, has_mac, hiskeyid); #endif } else if (rbufp->recv_length == LEN_PKT_NOMAC) { hiskeyid = 0; @@ -415,15 +473,14 @@ receive(rbufp) } else { #ifdef DEBUG if (debug > 2) - printf("receive: bad length %d (not > %d or == %d)\n", - rbufp->recv_length, LEN_PKT_MAC, LEN_PKT_NOMAC); + printf("receive: bad length %d %ld\n", + rbufp->recv_length, sizeof(struct pkt)); #endif sys_badlength++; return; } - /* * Figure out his mode and validate it. */ @@ -432,7 +489,8 @@ receive(rbufp) if (debug > 2) printf("receive: his mode %d\n", hismode); #endif - if (PKT_VERSION(pkt->li_vn_mode) == NTP_OLDVERSION && hismode == 0) { + 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. @@ -452,34 +510,43 @@ receive(rbufp) } } - /* - * If he included a mac field, decrypt it to see if it is authentic. + * If he included a mac field, decrypt it to see if it is + * authentic. */ is_authentic = 0; if (has_mac) { if (authhavekey(hiskeyid)) { - if (authdecrypt(hiskeyid, (U_LONG *)pkt, LEN_PKT_NOMAC)) { + if (!authistrusted(hiskeyid)) { + sys_badauth++; +#ifdef DEBUG + if (debug > 3) + printf("receive: untrusted keyid\n"); +#endif + return; + } + if (authdecrypt(hiskeyid, (U_LONG *)pkt, + LEN_PKT_NOMAC)) { is_authentic = 1; #ifdef DEBUG if (debug > 3) - printf("receive: authdecrypt succeeds\n"); + printf("receive: authdecrypt succeeds\n"); #endif } else { sys_badauth++; #ifdef DEBUG if (debug > 3) - printf("receive: authdecrypt fails\n"); + printf("receive: authdecrypt fails\n"); #endif } } } /* - * If this is someone we don't remember from a previous association, - * dispatch him now. Either we send something back quick, we - * ignore him, or we allocate some memory for him and let - * him continue. + * If this is someone we don't remember from a previous + * association, dispatch him now. Either we send something back + * quick, we ignore him, or we allocate some memory for him and + * let him continue. */ if (peer == 0) { int mymode; @@ -493,15 +560,13 @@ receive(rbufp) * later. If not, send his time quick. */ if (restrict & RES_NOPEER) { - fast_xmit(rbufp, (int)hismode, is_authentic); + fast_xmit(rbufp, (int)hismode, + is_authentic); return; } break; case MODE_PASSIVE: -#ifdef MCAST - /* process the packet to determine the rt-delay */ -#endif /* MCAST */ case MODE_SERVER: /* * These are obvious errors. Ignore. @@ -519,11 +584,9 @@ receive(rbufp) /* * Sort of a repeat of the above... */ -/* if ((restrict & RES_NOPEER) || !sys_bclient) return; -*/ - mymode = MODE_BCLIENT; + mymode = MODE_MCLIENT; break; } @@ -531,9 +594,10 @@ receive(rbufp) * Okay, we're going to keep him around. Allocate him * some memory. */ - peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, mymode, - PKT_VERSION(pkt->li_vn_mode), NTP_MINDPOLL, - NTP_MAXPOLL, 0, hiskeyid); + peer = newpeer(&rbufp->recv_srcadr, + rbufp->dstadr, mymode, PKT_VERSION(pkt->li_vn_mode), + NTP_MINDPOLL, NTP_MAXDPOLL, 0, hiskeyid); + if (peer == 0) { /* * The only way this can happen is if the @@ -559,8 +623,10 @@ receive(rbufp) */ if (!(peer->flags & FLAG_CONFIG)) { if (has_mac) { + if (!(peer->reach && peer->keyid != hiskeyid)) { peer->keyid = hiskeyid; peer->flags |= FLAG_AUTHENABLE; + } } else { peer->keyid = 0; peer->flags &= ~FLAG_AUTHENABLE; @@ -577,8 +643,8 @@ receive(rbufp) } else { /* * If this guy is authenable, and has been authenticated - * in the past, but just failed the authentic test, report - * the event. + * in the past, but just failed the authentic test, + * report the event. */ if (peer->flags & FLAG_AUTHENABLE && peer->flags & FLAG_AUTHENTIC) @@ -595,38 +661,33 @@ receive(rbufp) trustable = 1; if (sys_authenticate && trustable) { - if (!(peer->flags & FLAG_CONFIG) - || (peer->flags & FLAG_AUTHENABLE)) - trustable = 0; - - if (has_mac) { - if (authistrusted(hiskeyid)) { - if (is_authentic) { - trustable = 1; - } else { - trustable = 0; - peer->badauth++; - } + if (!(peer->flags & FLAG_CONFIG) || + (peer->flags & FLAG_AUTHENABLE)) { + if (has_mac && is_authentic) + trustable = 1; + else + trustable = 0; } - } } /* - * Dispose of the packet based on our respective modes. We + * Dispose of the packet based on our respective modes. We * don't drive this with a table, though we probably could. */ switch (peer->hmode) { case MODE_ACTIVE: case MODE_CLIENT: /* - * Active mode associations are configured. If the data - * isn't trustable, ignore it and hope this guy brightens - * up. Else accept any data we get and process it. + * Active mode associations are configured. If the data + * isn't trustable, ignore it and hope this guy + * brightens up. Else accept any data we get and process + * it. */ switch (hismode) { case MODE_ACTIVE: case MODE_PASSIVE: case MODE_SERVER: + case MODE_BROADCAST: process_packet(peer, pkt, &(rbufp->recv_time), has_mac, trustable); break; @@ -635,25 +696,20 @@ receive(rbufp) if (peer->hmode == MODE_ACTIVE) fast_xmit(rbufp, hismode, is_authentic); return; - - case MODE_BROADCAST: - /* - * No good for us, we want real time. - */ - break; } break; case MODE_PASSIVE: /* * Passive mode associations are (in the current - * implementation) always dynamic. If we get an - * invalid header, break the connection. I hate - * doing this since it seems like a waste. Oh, well. + * implementation) always dynamic. If we get an invalid + * header, break the connection. I hate doing this since + * it seems like a waste. Oh, well. */ switch (hismode) { case MODE_ACTIVE: - if (process_packet(peer, pkt, &(rbufp->recv_time), + if (process_packet(peer, pkt, + &(rbufp->recv_time), has_mac, trustable) == 0) { unpeer(peer); clock_select(); @@ -680,34 +736,35 @@ receive(rbufp) case MODE_BCLIENT: /* - * Broadcast client pseudo-mode. We accept both server - * and broadcast data. Passive mode data is an error. + * Broadcast client pseudo-mode. We accept both server + * and broadcast data. Passive mode data is an error. */ switch (hismode) { case MODE_ACTIVE: /* - * This guy wants to give us real time when we've - * been existing on lousy broadcasts! Create a - * passive mode association and do it that way, - * but keep the old one in case the packet turns - * out to be bad. + * This guy wants to give us real time when + * we've been existing on lousy broadcasts! + * Create a passive mode association and do it + * that way, but keep the old one in case the + * packet turns out to be bad. */ peer2 = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, MODE_PASSIVE, PKT_VERSION(pkt->li_vn_mode), NTP_MINDPOLL, NTP_MAXPOLL, 0, hiskeyid); - if (process_packet(peer2, pkt, &rbufp->recv_time, - has_mac, trustable) == 0) { + if (process_packet(peer2, pkt, + &rbufp->recv_time, has_mac, trustable) == 0) { /* - * Strange situation. We've been receiving - * broadcasts from him which we liked, but - * we don't like his active mode stuff. - * Keep his old peer structure and send - * him some time quickly, we'll figure it - * out later. + * Strange situation. We've been + * receiving broadcasts from him which + * we liked, but we don't like his + * active mode stuff. Keep his old peer + * structure and send him some time + * quickly, we'll figure it out later. */ unpeer(peer2); - fast_xmit(rbufp, (int)hismode, is_authentic); + fast_xmit(rbufp, (int)hismode, + is_authentic); } else /* * Drop the old association @@ -728,15 +785,40 @@ receive(rbufp) */ break; } + break; + + case MODE_MCLIENT: + /* + * This mode is temporary and does not appear outside + * this routine. It lasts only from the time the + * broadcast/multicast is recognized until the + * association is instantiated. Note that we start up in + * client/server mode to initially synchronize the + * clock. + */ + switch (hismode) { + case MODE_BROADCAST: + peer->flags |= FLAG_MCAST1 | FLAG_MCAST2; + peer->hmode = MODE_CLIENT; + process_packet(peer, pkt, &rbufp->recv_time, + has_mac, trustable); + break; + + case MODE_SERVER: + case MODE_PASSIVE: + case MODE_ACTIVE: + case MODE_CLIENT: + break; + } } } /* - * process_packet - Packet Procedure, a la Section 3.4.3 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. + * process_packet - Packet Procedure, a la Section 3.4.3 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(peer, pkt, recv_ts, has_mac, trustable) @@ -746,12 +828,13 @@ process_packet(peer, pkt, recv_ts, has_mac, trustable) int has_mac; int trustable; /* used as "valid header" */ { - U_LONG t23_ui = 0, t23_uf = 0; - U_LONG t10_ui, t10_uf; + l_fp t10, t23; s_fp di, ei, p_dist, p_disp; l_fp ci, p_rec, p_xmt, p_org; int randomize; u_char ostratum, oreach; + U_LONG temp; + u_fp precision; sys_processed++; peer->processed++; @@ -780,49 +863,44 @@ process_packet(peer, pkt, recv_ts, has_mac, trustable) peer->bogusorg++; peer->flash |= TEST2; /* bogus packet */ } - if ((p_rec.l_ui == 0 && p_rec.l_uf == 0) || - (p_org.l_ui == 0 && p_org.l_uf == 0)) + if (L_ISZERO(&p_rec) || L_ISZERO(&p_org)) peer->flash |= TEST3; /* unsynchronized */ } else { - if (p_org.l_ui == 0 && p_org.l_uf == 0) + if (L_ISZERO(&p_org)) peer->flash |= TEST3; /* unsynchronized */ } peer->org = p_xmt; /* reuse byte-swapped pkt->xmt */ peer->ppoll = pkt->ppoll; /* - * Call poll_update(). This will either start us, if the + * Call poll_update(). This will either start us, if the * association is new, or drop the polling interval if the * association is existing and ppoll has been reduced. */ poll_update(peer, peer->hpoll, randomize); + /* * Test for valid header (tests 5 through 8) */ if (trustable == 0) /* test 5 */ peer->flash |= TEST5; /* authentication failed */ + temp = ntohl(pkt->reftime.l_ui); if (PKT_LEAP(pkt->li_vn_mode) == LEAP_NOTINSYNC || /* test 6 */ - p_xmt.l_ui < ntohl(pkt->reftime.l_ui) || - p_xmt.l_ui >= (ntohl(pkt->reftime.l_ui) + NTP_MAXAGE)) { - peer->seltooold++; /* test 6 */ + p_xmt.l_ui < temp || p_xmt.l_ui >= temp + NTP_MAXAGE) peer->flash |= TEST6; /* peer clock unsynchronized */ - } if (!(peer->flags & FLAG_CONFIG) && /* test 7 */ (PKT_TO_STRATUM(pkt->stratum) >= NTP_MAXSTRATUM || PKT_TO_STRATUM(pkt->stratum) > sys_stratum)) peer->flash |= TEST7; /* peer stratum out of bounds */ if (p_dist >= NTP_MAXDISPERSE /* test 8 */ || p_dist <= (-NTP_MAXDISPERSE) - || p_disp >= NTP_MAXDISPERSE) { - peer->disttoolarge++; + || p_disp >= NTP_MAXDISPERSE) peer->flash |= TEST8; /* delay/dispersion too big */ - } /* * If the packet header is invalid (tests 5 through 8), exit */ - if (peer->flash & (TEST5 | TEST6 | TEST7 | TEST8)) { #ifdef DEBUG @@ -864,65 +942,78 @@ process_packet(peer, pkt, recv_ts, has_mac, trustable) peer->reach |= 1; /* - * If running in a normal polled association, calculate the round - * trip delay (di) and the clock offset (ci). We use the equations - * (reordered from those in the spec): + * 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. * - * d = (t2 - t3) - (t1 - t0) * 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; + precision = FP_SECOND >> -(int)sys_precision; + if (precision == 0) + precision = 1; + ei = precision + peer->rec.l_ui - p_org.l_ui; + + /* + * If running in a broacast 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 running as a broadcast client, these change. di becomes - * equal to two times our broadcast delay, while the offset - * becomes equal to: - * - * c = (t1 - t0) + estbdelay - */ - t10_ui = p_xmt.l_ui; /* pkt->xmt == t1 */ - t10_uf = p_xmt.l_uf; - M_SUB(t10_ui, t10_uf, peer->rec.l_ui, peer->rec.l_uf); /*peer->rec==t0*/ - - if (PKT_MODE(pkt->li_vn_mode) != MODE_BROADCAST) { - t23_ui = p_rec.l_ui; /* pkt->rec == t2 */ - t23_uf = p_rec.l_uf; - M_SUB(t23_ui, t23_uf, p_org.l_ui, p_org.l_uf); /*pkt->org==t3*/ - } + * 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 (peer->pmode == MODE_BROADCAST) { + if (peer->flags & FLAG_MCAST1) { + if (peer->hmode == MODE_BCLIENT) + peer->flags &= ~FLAG_MCAST1; + L_SUB(&ci, &peer->offset); + L_NEG(&ci); + peer->estbdelay = LFPTOFP(&ci); + return (1); - /* now have (t2 - t3) and (t0 - t1). Calculate (ci), (di) and (ei) */ - ci.l_ui = t10_ui; - ci.l_uf = t10_uf; - ei = (FP_SECOND >> (-(int)sys_precision)); + } + FPTOLFP(peer->estbdelay, &t10); + L_ADD(&ci, &t10); + di = peer->delay; - /* - * If broadcast mode, time of last reception has been fiddled - * to p_org, rather than originate timestamp. We use this to - * augment dispersion and previously calcuated estbdelay as - * the delay. We know NTP_SKEWFACTOR == 16, which accounts for - * the simplified ei calculation. - */ - if (PKT_MODE(pkt->li_vn_mode) == MODE_BROADCAST) { - M_ADDUF(ci.l_ui, ci.l_uf, peer->estbdelay >> 1); - di = MFPTOFP(0, peer->estbdelay); - ei += peer->rec.l_ui - p_org.l_ui; } else { - M_ADD(ci.l_ui, ci.l_uf, t23_ui, t23_uf); - M_RSHIFT(ci.l_i, ci.l_uf); - M_SUB(t23_ui, t23_uf, t10_ui, t10_uf); - di = MFPTOFP(t23_ui, t23_uf); - ei += peer->rec.l_ui - p_org.l_ui; + L_ADD(&ci, &t23); + L_RSHIFT(&ci); + L_SUB(&t23, &t10); + di = LFPTOFP(&t23); } + #ifdef DEBUG if (debug > 3) printf("offset: %s, delay %s, error %s\n", - lfptoa(&ci, 9), fptoa(di, 4), fptoa(ei, 4)); + lfptoa(&ci, 6), fptoa(di, 5), fptoa(ei, 5)); #endif if (di >= NTP_MAXDISPERSE || di <= (-NTP_MAXDISPERSE) - || ei >= NTP_MAXDISPERSE) { /* test 4 */ - peer->bogusdelay++; + || ei >= NTP_MAXDISPERSE) /* test 4 */ peer->flash |= TEST4; /* delay/dispersion too big */ - } /* - * If the packet data is invalid (tests 1 through 4), exit + * If the packet data is invalid (tests 1 through 4), exit. */ if (peer->flash) { @@ -942,28 +1033,25 @@ process_packet(peer, pkt, recv_ts, has_mac, trustable) } /* - * This one is valid. Mark it so, give it to clock_filter(), + * This one is valid. Mark it so, give it to clock_filter(). */ clock_filter(peer, &ci, di, (u_fp)ei); /* - * If this guy was previously unreachable, report him - * reachable. + * If this guy was previously unreachable, report him reachable. * Note we do this here so that the peer values we return are * the updated ones. */ - if (oreach == 0) { + if (oreach == 0) report_event(EVNT_REACH, peer); -#ifdef DEBUG - if (debug) - printf("proto: peer reach %d\n", peer->minpoll); -#endif /* DEBUG */ - } /* - * Now update the clock. + * Now update the clock. If we have found a system peer and this + * is a broadcast/multicast client, switch to listen mode. */ clock_update(peer); + if (sys_peer && peer->flags & FLAG_MCAST2) + peer->hmode = MODE_BCLIENT; return(1); } @@ -985,103 +1073,67 @@ clock_update(peer) printf("clock_update(%s)\n", ntoa(&peer->srcadr)); #endif - record_peer_stats(&peer->srcadr, ctlpeerstatus(peer), &peer->offset, - peer->delay, peer->dispersion); + record_peer_stats(&peer->srcadr, ctlpeerstatus(peer), + &peer->offset, peer->delay, peer->dispersion); /* - * Call the clock selection algorithm to see - * if this update causes the peer to change. + * Call the clock selection algorithm to see if this update + * causes the peer to change. If this is not the system peer, + * quit now. */ clock_select(); - - /* - * Quit if this peer isn't the system peer. Other peers - * used in the combined offset are not allowed to set - * system variables or update the clock. - */ if (peer != sys_peer) return; /* - * Quit if the sys_peer is too far away. - */ - if (peer->synch >= NTP_MAXDISTANCE) - return; - - /* - * Update the system state + * Update the system state. This updates the system stratum, + * leap bits, root delay, root dispersion, reference ID and + * reference time. We also update select dispersion and max + * frequency error. */ oleap = sys_leap; ostratum = sys_stratum; - /* - * get leap value (usually the peer leap unless overridden by local configuration) - */ - sys_leap = leap_actual(peer->leap & leap_mask); - /* - * N.B. peer->stratum was guaranteed to be less than - * NTP_MAXSTRATUM by the receive procedure. - * We assume peer->update == sys_clock because - * clock_filter was called right before this function. - * If the pps signal is in control, the system variables are - * set in the ntp_loopfilter.c module. - */ - if (!pps_control) { - sys_stratum = peer->stratum + 1; - d = peer->delay; - if (d < 0) - d = -d; - sys_rootdelay = peer->rootdelay + d; - sys_maxd = peer->dispersion + peer->selectdisp; - d = peer->soffset; - if (d < 0) - d = -d; - d += sys_maxd; - if (!peer->flags & FLAG_REFCLOCK && d < NTP_MINDISPERSE) - d = NTP_MINDISPERSE; - sys_rootdispersion = peer->rootdispersion + d; - } - - /* - * Hack for reference clocks. Sigh. This is the - * only real silly part, though, so the analogy isn't - * bad. - */ - if (peer->flags & FLAG_REFCLOCK && peer->stratum == STRATUM_REFCLOCK) - sys_refid = peer->refid; - else { - if (pps_control) - memmove((char *)&sys_refid, PPSREFID, 4); - else - sys_refid = peer->srcadr.sin_addr.s_addr; - } + sys_stratum = peer->stratum + 1; + if (sys_stratum == 1) + sys_refid = peer->refid; + else + sys_refid = peer->srcadr.sin_addr.s_addr; + sys_reftime = peer->rec; + d = peer->delay; + if (d < 0) + d = -d; + sys_rootdelay = peer->rootdelay + d; + d = peer->soffset; + if (d < 0) + d = -d; + d += peer->dispersion + peer->selectdisp; + if (!peer->flags & FLAG_REFCLOCK && d < NTP_MINDISPERSE) + d = NTP_MINDISPERSE; + sys_rootdispersion = peer->rootdispersion + d; /* - * Report changes. Note that we never sync to - * an unsynchronized host. + * Reset/adjust the system clock. Watch for timewarps here. */ - if (oleap == LEAP_NOTINSYNC) - report_event(EVNT_SYNCCHG, (struct peer *)0); - else if (ostratum != sys_stratum) - report_event(EVNT_PEERSTCHG, (struct peer *)0); - - sys_reftime = peer->rec; - sys_refskew.l_i = 0; sys_refskew.l_f = NTP_SKEWINC; - switch (local_clock(&sys_offset, peer)) { case -1: + /* - * Clock is too screwed up. Just exit for now. + * Clock is too screwed up. Just exit for now. */ report_event(EVNT_SYSFAULT, (struct peer *)0); exit(1); /*NOTREACHED*/ case 0: + /* * Clock was slewed. Continue on normally. */ + sys_leap = leap_consensus & leap_mask; + L_CLR(&sys_refskew); break; case 1: + /* * Clock was stepped. Clear filter registers * of all peers. @@ -1093,20 +1145,17 @@ clock_update(peer) report_event(EVNT_CLOCKRESET, (struct peer *)0); break; } - if (sys_stratum > 1) - sys_refid = peer->srcadr.sin_addr.s_addr; - else { - if (peer->flags & FLAG_REFCLOCK) - sys_refid = peer->refid; - else - memmove((char *)&sys_refid, PPSREFID, 4); - } + sys_maxd = peer->dispersion + peer->selectdisp; + 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.8 of the spec. + * poll_update - update peer poll interval. See Section 3.4.8 of the + * spec. */ void poll_update(peer, new_hpoll, randomize) @@ -1115,7 +1164,7 @@ poll_update(peer, new_hpoll, randomize) int randomize; { register struct event *evp; - register U_LONG new_timer; + register u_long new_timer; u_char newpoll, oldpoll; #ifdef DEBUG @@ -1127,7 +1176,8 @@ poll_update(peer, new_hpoll, randomize) * Catch reference clocks here. The polling interval for a * reference clock is fixed and needn't be maintained by us. */ - if (peer->flags & FLAG_REFCLOCK || peer->hmode == MODE_BROADCAST) + if (peer->flags & FLAG_REFCLOCK || peer->hmode == + MODE_BROADCAST) return; /* @@ -1163,9 +1213,10 @@ poll_update(peer, new_hpoll, randomize) } /* hpoll <= maxpoll for sure */ - newpoll = max((u_char)min(peer->ppoll, peer->hpoll), peer->minpoll); - if (randomize == POLL_MAKERANDOM || - (randomize == POLL_RANDOMCHANGE && newpoll != oldpoll)) + newpoll = max((u_char)min(peer->ppoll, peer->hpoll), + peer->minpoll); + if (randomize == POLL_MAKERANDOM || (randomize == + POLL_RANDOMCHANGE && newpoll != oldpoll)) new_timer = (1 << (newpoll - 1)) + ranp2(newpoll - 1) + current_time; else @@ -1190,24 +1241,14 @@ clear_all() for (i = 0; i < HASH_SIZE; i++) for (peer = peer_hash[i]; peer != 0; peer = peer->next) { - /* - * We used to drop all unconfigured pollers here. - * The problem with doing this is that if your best - * time source is unconfigured (there are reasons - * for doing this) and you drop him, he may not - * get around to polling you for a long time. Hang - * on to everyone, dropping their polling intervals - * to the minimum. - */ peer_clear(peer); } /* - * Clear sys_peer. We'll sync to one later. + * Clear sys_peer. We'll sync to one later. */ sys_peer = 0; sys_stratum = STRATUM_UNSPEC; - } @@ -1260,10 +1301,10 @@ clock_filter(peer, sample_offset, sample_delay, sample_error) s_fp sample_delay; u_fp sample_error; { - register int i; + register int i, j, k, n; register u_char *ord; - register s_fp sample_distance, sample_soffset, skew; s_fp distance[NTP_SHIFT]; + long skew, skewmax; #ifdef DEBUG if (debug) @@ -1273,92 +1314,87 @@ clock_filter(peer, sample_offset, sample_delay, sample_error) #endif /* - * Update sample errors and calculate distances. - * We know NTP_SKEWFACTOR == 16 + * Update sample errors and calculate distances. Also initialize + * sort index vector. We know NTP_SKEWFACTOR == 16 */ skew = sys_clock - peer->update; peer->update = sys_clock; - for (i = 0; i < NTP_SHIFT; i++) { - distance[i] = peer->filter_error[i]; - if (peer->filter_error[i] < NTP_MAXDISPERSE) { - peer->filter_error[i] += skew; - distance[i] += (peer->filter_delay[i] >> 1); - } - } - - /* - * We keep a sort by distance of the current contents of the - * shift registers. We update this by (1) removing the - * register we are going to be replacing from the sort, and - * (2) reinserting it based on the new distance value. - */ ord = peer->filter_order; - sample_distance = sample_error + (sample_delay >> 1); - sample_soffset = LFPTOFP(sample_offset); - - for (i = 0; i < NTP_SHIFT-1; i++) /* find old value */ - if (ord[i] == peer->filter_nextpt) - break; - for ( ; i < NTP_SHIFT-1; i++) /* i is current, move everything up */ - ord[i] = ord[i+1]; - /* Here, last slot in ord[] is empty */ - - if (sample_error >= NTP_MAXDISPERSE) - /* - * Last slot for this guy. - */ - i = NTP_SHIFT-1; - else { - register int j; - register u_fp *errorp; - - errorp = peer->filter_error; - /* - * Find where he goes in, then shift everyone else down - */ - for (i = 0; i < NTP_SHIFT-1; i++) - if (errorp[ord[i]] >= NTP_MAXDISPERSE - || sample_distance <= distance[ord[i]]) - break; - - for (j = NTP_SHIFT-1; j > i; j--) - ord[j] = ord[j-1]; + j = peer->filter_nextpt; + for (i = 0; i < NTP_SHIFT; i++) { + peer->filter_error[j] += (u_fp)skew; + if (peer->filter_error[j] > NTP_MAXDISPERSE) + peer->filter_error[j] = NTP_MAXDISPERSE; + distance[i] = peer->filter_error[j] + + (peer->filter_delay[j] >> 1); + ord[i] = j; + if (--j < 0) + j += NTP_SHIFT; } - ord[i] = peer->filter_nextpt; /* - * Got everything in order. Insert sample in current register - * and increment nextpt. + * Insert the new sample at the beginning of the register. */ peer->filter_delay[peer->filter_nextpt] = sample_delay; peer->filter_offset[peer->filter_nextpt] = *sample_offset; - peer->filter_soffset[peer->filter_nextpt] = sample_soffset; + peer->filter_soffset[peer->filter_nextpt] = + LFPTOFP(sample_offset); peer->filter_error[peer->filter_nextpt] = sample_error; - distance[peer->filter_nextpt] = sample_distance; + distance[0] = sample_error + (sample_delay >> 1); + + /* + * Sort the samples in the register by distance. The winning + * sample will be in ord[0]. Sort the samples only if the + * samples are not too old and the delay is meaningful. + */ + skewmax = 0; + for (n = 0; n < NTP_SHIFT && sample_delay; n++) { + for (j = 0; j < n && skewmax < + CLOCK_MAXSEC; j++) { + if (distance[j] > distance[n]) { + s_fp ftmp; + + ftmp = distance[n]; + k = ord[n]; + distance[n] = distance[j]; + ord[n] = ord[j]; + distance[j] = ftmp; + ord[j] = k; + } + } + skewmax += (1 << peer->hpoll); + } peer->filter_nextpt++; if (peer->filter_nextpt >= NTP_SHIFT) peer->filter_nextpt = 0; /* - * Now compute the dispersion, and assign values to delay and - * offset. If there are no samples in the register, delay and - * offset are not touched and dispersion is set to the maximum. + * We compute the dispersion as per the spec. Note that, to make + * things simple, both the l_fp and s_fp offsets are retained + * and that the s_fp could be nonsense if the l_fp is greater + * than about 32000 s. However, the sanity checks in + * ntp_loopfilter() require the l_fp offset to be less than 1000 + * s anyway, so not to worry. */ if (peer->filter_error[ord[0]] >= NTP_MAXDISPERSE) { peer->dispersion = NTP_MAXDISPERSE; } else { - register s_fp d; + s_fp d; + u_fp y; peer->delay = peer->filter_delay[ord[0]]; peer->offset = peer->filter_offset[ord[0]]; peer->soffset = LFPTOFP(&peer->offset); peer->dispersion = peer->filter_error[ord[0]]; - for (i = 1; i < NTP_SHIFT; i++) { - if (peer->filter_error[ord[i]] >= NTP_MAXDISPERSE) + + y = 0; + for (i = NTP_SHIFT - 1; i > 0; i--) { + if (peer->filter_error[ord[i]] >= + NTP_MAXDISPERSE) d = NTP_MAXDISPERSE; else { - d = peer->filter_soffset[ord[i]] - - peer->filter_soffset[ord[0]]; + d = peer->filter_soffset[ord[i]] - + peer->filter_soffset[ord[0]]; if (d < 0) d = -d; if (d > NTP_MAXDISPERSE) @@ -1367,24 +1403,22 @@ clock_filter(peer, sample_offset, sample_delay, sample_error) /* * XXX This *knows* NTP_FILTER is 1/2 */ - peer->dispersion += (u_fp)(d) >> i; + y = ((u_fp)d + y) >> 1; } + peer->dispersion += y; + /* * Calculate synchronization distance backdated to - * sys_lastselect (clock_select will fix it). - * We know NTP_SKEWFACTOR == 16 + * sys_lastselect (clock_select will fix it). We know + * NTP_SKEWFACTOR == 16. */ d = peer->delay; if (d < 0) d = -d; d += peer->rootdelay; - peer->synch = (d>>1) - + peer->rootdispersion + peer->dispersion - - (sys_clock - sys_lastselect); + peer->synch = (d >> 1) + peer->rootdispersion + + peer->dispersion - (sys_clock - sys_lastselect); } - /* - * We're done - */ } @@ -1401,11 +1435,16 @@ clock_select() register int j; register int n; register int allow, found, k; - /* XXX correct? */ - s_fp low = 0x7ffffff; - s_fp high = 0x00000000; + s_fp low = 0x7fffffff; + s_fp high = -0x7ffffff; u_fp 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; static int *index; @@ -1417,6 +1456,11 @@ clock_select() printf("clock_select()\n"); #endif + /* + * Initizialize. If a prefer peer does not survive this thing, + * the pps_update switch will remain zero. + */ + pps_update = 0; nlist = 0; for (n = 0; n < HASH_SIZE; n++) nlist += peer_hash_count[n]; @@ -1459,22 +1503,24 @@ clock_select() peer->flags &= ~FLAG_SYSPEER; /* - * Update synch distance (NTP_SKEWFACTOR == 16) + * Update synch distance (NTP_SKEWFACTOR == 16). + * Note synch distance check instead of spec + * dispersion check. Naughty. */ peer->synch += (sys_clock - sys_lastselect); if (peer->reach == 0) continue; /* unreachable */ - if (peer->stratum > 1 && - peer->refid == peer->dstadr->sin.sin_addr.s_addr) + if (peer->stratum > 1 && peer->refid == + peer->dstadr->sin.sin_addr.s_addr) continue; /* sync loop */ if (peer->stratum >= NTP_MAXSTRATUM || peer->stratum > sys_stratum) continue; /* bad stratum */ - if (peer->dispersion >= NTP_MAXDISPERSE) { + if (peer->dispersion >= NTP_MAXDISTANCE) { peer->seldisptoolarge++; - continue; /* too noisy or broken */ + continue; /* too noisy or broken */ } if (peer->org.l_ui < peer->reftime.l_ui) { peer->selbroken++; @@ -1482,15 +1528,34 @@ clock_select() } /* - * This one seems sane. + * 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) { + typelocal = peer; + if (!(peer->flags & FLAG_PREFER)) + continue; /* no local clock */ + } + if (peer->refclktype == REFCLK_NIST_ACTS) { + typeacts = peer; + if (!(peer->flags & FLAG_PREFER)) + continue; /* no acts */ + } + + /* + * If we get this far, we assume the peer is + * acceptable. */ peer->was_sane = 1; peer_list[nlist++] = peer; /* - * Insert each interval endpoint on the sorted list. + * Insert each interval endpoint on the sorted + * list. */ - e = peer->soffset + peer->synch; /* Upper end */ + e = peer->soffset + peer->synch; /* Upper end */ for (i = nl3 - 1; i >= 0; i--) { if (e >= endpoint[index[i]].val) break; @@ -1533,7 +1598,7 @@ clock_select() i = 0; j = nl3 - 1; - allow = nlist; /* falsetickers assumed */ + allow = nlist; /* falsetickers assumed */ found = 0; while (allow > 0) { allow--; @@ -1556,14 +1621,30 @@ clock_select() 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 (debug) - printf("clock_select: no intersection\n"); - if (sys_peer != 0) - report_event(EVNT_PEERSTCHG, (struct peer *)0); - sys_peer = 0; - sys_stratum = STRATUM_UNSPEC; - return; + if (typeacts != 0) { + typeacts->was_sane = 1; + peer_list[0] = typeacts; + nlist = 1; + } else if (typelocal != 0) { + typelocal->was_sane = 1; + peer_list[0] = typelocal; + nlist = 1; + } else { + if (sys_peer != 0) + report_event(EVNT_PEERSTCHG, + (struct peer *)0); + sys_peer = 0; + sys_stratum = STRATUM_UNSPEC; + return; + } } #ifdef DEBUG @@ -1574,18 +1655,19 @@ clock_select() /* * Clustering algorithm. Process intersection list to discard - * outlyers. First, construct candidate list in cluster order. - * Cluster order is determined by the sum of peer - * synchronization distance plus scaled stratum. + * 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 (peer->soffset < low || high < peer->soffset) + if (nlist > 1 && (peer->soffset < low || high < + peer->soffset)) continue; peer->correct = 1; - d = peer->synch + ((U_LONG)peer->stratum << NTP_DISPFACTOR); + d = peer->synch + ((u_long)peer->stratum << + NTP_DISPFACTOR); if (j >= NTP_MAXCLOCK) { if (d >= synch[j - 1]) continue; @@ -1603,6 +1685,7 @@ clock_select() j++; } nlist = j; + #ifdef DEBUG if (debug > 2) for (i = 0; i < nlist; i++) @@ -1612,9 +1695,11 @@ clock_select() #endif /* - * Now, prune outlyers by root dispersion. + * 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 preferred peer. */ - for (i = 0; i < nlist; i++) { peer = peer_list[i]; peer->candidate = i + 1; @@ -1628,7 +1713,7 @@ clock_select() for (k = i = nlist - 1; i >= 0; i--) { u_fp sdisp = 0; - for (j = nlist - 1; j >= 0; j--) { + for (j = nlist - 1; j > 0; j--) { d = peer_list[i]->soffset - peer_list[j]->soffset; if (d < 0) @@ -1665,43 +1750,96 @@ clock_select() #endif /* - * What remains is a list of less than NTP_MINCLOCK peers. - * First record their order, then choose a peer. If the - * head of the list has a lower stratum than sys_peer - * choose him right off. If not, see if sys_peer is in - * the list. If so, keep him. If not, take the top of - * the list anyway. Also, clamp the polling intervals. - */ - osys_peer = sys_peer; + * 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--) { - if (peer_list[i]->flags & FLAG_PREFER) - sys_peer = peer_list[i]; peer_list[i]->select = i + 1; peer_list[i]->flags |= FLAG_SYSPEER; poll_update(peer_list[i], peer_list[i]->hpoll, POLL_RANDOMCHANGE); - } - if (sys_peer == 0 || sys_peer->stratum > peer_list[0]->stratum) { - sys_peer = peer_list[0]; - } else { - for (i = 1; i < nlist; i++) + 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) - break; - if (i >= nlist) - sys_peer = peer_list[0]; + typesystem = peer_list[i]; + if (peer_list[i]->flags & FLAG_PREFER) { + typeprefer = peer_list[i]; + if (typeprefer->soffset >= -CLOCK_MAX_FP && + typeprefer->soffset < CLOCK_MAX_FP) + pps_update = 1; + } + } else { + if (peer_list[i] == sys_peer) + sys_peer = 0; + } } /* - * If we got a new system peer from all of this, report the event. + * 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 acts + * peers (type REFCLK_NIST_ATOM with FLAG_PREFER), (2) pps peers + * (type REFCLK_ATOM_PPS), (3) remaining prefer peers (flag + * FLAG_PREFER), (4) the existing system peer, if any, (5) the + * head of the survivor list. Note that only one peer can be + * declared prefer. The order of preference is in the order + * stated. Note that all of these must be at the lowest stratum, + * i.e., the stratum of the head of the survivor list. */ - if (osys_peer != sys_peer) - report_event(EVNT_PEERSTCHG, (struct peer *)0); + osys_peer = sys_peer; + if (typeprefer && (typeprefer == typelocal || typeprefer == + typeacts || !typepps)) { + sys_peer = typeprefer; + sys_peer->selectdisp = 0; + sys_offset = sys_peer->offset; +#ifdef DEBUG + if (debug) + printf("select: prefer offset %s\n", + lfptoa(&sys_offset, 6)); +#endif + } else if (typepps) { + sys_peer = typepps; + sys_peer->selectdisp = 0; + sys_offset = sys_peer->offset; +#ifdef DEBUG + if (debug) + printf("select: pps offset %s\n", + lfptoa(&sys_offset, 6)); +#endif + } else { + if (!typesystem) + sys_peer = peer_list[0]; + clock_combine(peer_list, nlist); +#ifdef DEBUG + if (debug) + printf("select: combine offset %s\n", + lfptoa(&sys_offset, 6)); +#endif + } /* - * Combine the offsets of the survivors to form a weighted - * offset. + * If we got a new system peer from all of this, report the + * event and clamp the system poll interval. */ - clock_combine(peer_list, nlist); + if (osys_peer != sys_peer) { + sys_poll = sys_peer->minpoll; + report_event(EVNT_PEERSTCHG, (struct peer *)0); + } } /* @@ -1722,21 +1860,12 @@ clock_combine(peers, npeers) l_fp diff; /* - * Sort peers by cluster distance as in the outlyer algorithm. If - * the preferred peer is found, use its offset only. + * Sort the offsets by synch distance. */ k = 0; for (i = 0; i < npeers; i++) { - if (peers[i]->stratum > sys_peer->stratum) continue; - if (peers[i]->flags & FLAG_PREFER) { - sys_offset = peers[i]->offset; - pps_update = current_time; -#ifdef DEBUG - printf("combine: prefer offset %s\n", - lfptoa(&sys_offset, 6)); -#endif - return; - } + if (peers[i]->stratum > sys_peer->stratum) + continue; d = peers[i]->synch; for (j = k; j > 0; j--) { if (synch[j - 1] <= d) @@ -1748,6 +1877,7 @@ clock_combine(peers, npeers) coffset[j] = peers[i]->offset; k++; } + /* * Succesively combine the two offsets with the highest * distance and enter the result into the sorted list. @@ -1774,6 +1904,7 @@ clock_combine(peers, npeers) * we just drop the distant offset. */ continue; + /* * The offsets are combined by shifting their * difference the appropriate number of times and @@ -1823,21 +1954,14 @@ clock_combine(peers, npeers) synch[j] = d; coffset[j] = diff; } + /* * The result is put where clock_update() can find it. */ sys_offset = coffset[0]; - -#ifdef DEBUG - if (debug) { - printf("combine: offset %s\n", lfptoa(&sys_offset, 6)); - } -#endif - } - /* * fast_xmit - fast path send for stateless (non-)associations */ @@ -1853,6 +1977,7 @@ fast_xmit(rbufp, rmode, authentic) u_short xkey = 0; int docrypt = 0; l_fp xmt_ts; + u_fp precision; #ifdef DEBUG if (debug > 1) @@ -1880,9 +2005,11 @@ fast_xmit(rbufp, rmode, authentic) xpkt.ppoll = max(NTP_MINPOLL, rpkt->ppoll); xpkt.precision = sys_precision; xpkt.rootdelay = HTONS_FP(sys_rootdelay); + precision = FP_SECOND >> -(int)sys_precision; + if (precision == 0) + precision = 1; xpkt.rootdispersion = HTONS_FP(sys_rootdispersion + - (FP_SECOND >> (-(int)sys_precision)) + - LFPTOFP(&sys_refskew)); + precision + LFPTOFP(&sys_refskew)); xpkt.refid = sys_refid; HTONL_FP(&sys_reftime, &xpkt.reftime); xpkt.org = rpkt->xmt; @@ -1900,7 +2027,7 @@ fast_xmit(rbufp, rmode, authentic) L_ADDUF(&xmt_ts, sys_authdelay); HTONL_FP(&xmt_ts, &xpkt.xmt); maclen = auth2crypt(xkey, (U_LONG *)&xpkt, LEN_PKT_NOMAC); - sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, &xpkt, + sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -9, &xpkt, LEN_PKT_NOMAC + maclen); } else { /* @@ -1908,50 +2035,32 @@ fast_xmit(rbufp, rmode, authentic) */ get_systime(&xmt_ts); HTONL_FP(&xmt_ts, &xpkt.xmt); - sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, &xpkt, + sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -10, &xpkt, LEN_PKT_NOMAC); } } -/* Find the precision of the system clock by watching how the current time - * changes as we read it repeatedly. - * - * struct timeval is only good to 1us, which may cause problems as machines - * get faster, but until then the logic goes: - * - * If a machine has precision (i.e. accurate timing info) > 1us, then it will - * probably use the "unused" low order bits as a counter (to force time to be - * a strictly increaing variable), incrementing it each time any process - * requests the time [[ or maybe time will stand still ? ]]. - * - * SO: the logic goes: - * - * IF the difference from the last time is "small" (< MINSTEP) - * THEN this machine is "counting" with the low order bits - * ELIF this is not the first time round the loop - * THEN this machine *WAS* counting, and has now stepped - * ELSE this machine has precision < time to read clock - * - * SO: if it exits on the first loop, assume "full accuracy" (1us) - * otherwise, take the log2(observered difference, rounded UP) - * - * MINLOOPS > 1 ensures that even if there is a STEP between the initial call - * and the first loop, it doesn't stop too early. - * Making it even greater allows MINSTEP to be reduced, assuming that the - * chance of MINSTEP-1 other processes getting in and calling gettimeofday - * between this processes's calls. - * Reducing MINSTEP may be necessary as this sets an upper bound for the time - * to actually call gettimeofday. +/* + * 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 (ys) */ +#define MAXSTEP 20000 /* maximum clock increment (us) */ +#define MINLOOPS 5 /* minimum number of step samples */ -#define DUSECS 1000000 /* us's as returned by gettime */ -#define HUSECS (1024*1024) /* Hex us's -- used when shifting etc */ -#define MINSTEP 5 /* some systems increment uS on each call */ - /* Don't use "1" as some *other* process may read too*/ - /*We assume no system actually *ANSWERS* in this time*/ -#define MAXLOOPS DUSECS /* if no STEP in a complete second, then FAST machine*/ -#define MINLOOPS 2 /* ensure at least this many loops */ - +/* + * 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() { struct timeval tp; @@ -1960,26 +2069,32 @@ int default_get_precision() int i; long diff; long val; - int minsteps = 2; /* need at least this many steps */ + long usec; + usec = 0; + val = MAXSTEP; GETTIMEOFDAY(&tp, &tzp); last = tp.tv_usec; - for (i = - --minsteps; i< MAXLOOPS; i++) { - gettimeofday(&tp, &tzp); + for (i = 0; i < MINLOOPS && usec < HUSECS;) { + GETTIMEOFDAY(&tp, &tzp); diff = tp.tv_usec - last; - if (diff < 0) diff += DUSECS; - if (diff > MINSTEP) if (minsteps-- <= 0) break; last = tp.tv_usec; + if (diff < 0) + diff += DUSECS; + usec += diff; + if (diff > MINSTEP) { + i++; + if (diff < val) + val = diff; + } } - - syslog(LOG_INFO, "precision calculation given %dus after %d loop%s", - diff, i, (i==1) ? "" : "s"); - - diff = (diff*3) / 2; /* round it up 1.5 is approx sqrt(2) */ - if (i >= MAXLOOPS) diff = 1; /* No STEP, so FAST machine */ - if (i == 0) diff = 1; /* time to read clock >= precision */ - for (i=0, val=HUSECS; val>0; i--, val >>= 1) if (diff >= val) return i; - return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */; + syslog(LOG_INFO, "precision = %d usec", val); + if (usec >= HUSECS) + val = MINSTEP; /* val <= MINSTEP; fast machine */ + diff = HUSECS; + for (i = 0; diff > val; i--) + diff >>= 1; + return (i); } /* @@ -1991,8 +2106,8 @@ init_proto() l_fp dummy; /* - * Fill in the sys_* stuff. Default is don't listen - * to broadcasting, don't authenticate. + * Fill in the sys_* stuff. Default is don't listen to + * broadcasting, don't authenticate. */ sys_leap = LEAP_NOTINSYNC; sys_stratum = STRATUM_UNSPEC; @@ -2000,7 +2115,7 @@ init_proto() sys_rootdelay = 0; sys_rootdispersion = 0; sys_refid = 0; - sys_reftime.l_ui = sys_reftime.l_uf = 0; + L_CLR(&sys_reftime); sys_refskew.l_i = NTP_MAXSKEW; sys_refskew.l_f = 0; sys_peer = 0; sys_poll = NTP_MINPOLL; @@ -2009,8 +2124,8 @@ init_proto() sys_bclient = 0; sys_bdelay = DEFBROADDELAY; - sys_authenticate = 0; + sys_authdelay = DEFAUTHDELAY; sys_stattime = 0; sys_badstratum = 0; @@ -2021,7 +2136,11 @@ init_proto() sys_processed = 0; sys_badauth = 0; - syslog(LOG_NOTICE, "default precision is initialized to 2**%d", sys_precision); + /* + * Default these to enable + */ + pll_enable = 1; + stats_control = 1; } @@ -2031,12 +2150,36 @@ init_proto() void proto_config(item, value) int item; - U_LONG value; + u_long value; { /* * Figure out what he wants to change, then do it */ switch (item) { + case PROTO_PLL: + /* + * Turn on/off pll clock correction + */ + pll_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 @@ -2049,25 +2192,19 @@ proto_config(item, value) break; case PROTO_MULTICAST_ADD: - /* - * Add multicast group address - */ - if (!sys_bclient) { - sys_bclient = 1; - io_setbclient(); - } -#ifdef MCAST + /* + * Add muliticast group address + */ + sys_bclient = 1; io_multicast_add(value); -#endif /* MCAST */ break; case PROTO_MULTICAST_DEL: /* * Delete multicast group address */ -#ifdef MCAST + sys_bclient = 1; io_multicast_del(value); -#endif /* MCAST */ break; case PROTO_PRECISION: @@ -2079,9 +2216,12 @@ proto_config(item, value) case PROTO_BROADDELAY: /* - * Set default broadcast delay + * Set default broadcast delay (s_fp) */ - sys_bdelay = ((value) + 0x00000800) & 0xfffff000; + if (sys_bdelay < 0) + sys_bdelay = -(-value >> 16); + else + sys_bdelay = value >> 16; break; case PROTO_AUTHENTICATE: @@ -2094,10 +2234,9 @@ proto_config(item, value) case PROTO_AUTHDELAY: /* - * Provide an authentication delay value. Round it to - * the microsecond. This is crude. + * Set authentication delay (l_fp fraction) */ - sys_authdelay = ((value) + 0x00000800) & 0xfffff000; + sys_authdelay = value; break; default: diff --git a/usr.sbin/xntpd/xntpd/ntp_refclock.c b/usr.sbin/xntpd/xntpd/ntp_refclock.c index 2cb7cc29d9a4..b0248fb80e12 100644 --- a/usr.sbin/xntpd/xntpd/ntp_refclock.c +++ b/usr.sbin/xntpd/xntpd/ntp_refclock.c @@ -1,112 +1,246 @@ -/* ntp_refclock.c,v 3.1 1993/07/06 01:11:25 jbj Exp +/* * ntp_refclock - processing support for reference clocks */ +#ifdef REFCLOCK + #include <stdio.h> #include <sys/types.h> #include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" -#ifdef REFCLOCK +#ifdef PPS +#include <sys/ppsclock.h> +#endif /* PPS */ + /* - * 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. 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. Refclock_peer and refclock_unpeer are called to - * initialize and terminate reference clock associations. + * 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. In addition, some routines expect an + * address in the dotted quad form 127.127.t.u, where t is the clock + * type and u the unit. A table typeunit[type][unit] contains the peer + * structure pointer for each configured clock type and unit. + * + * Most drivers support 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 separate, dedicated serial port and + * the tty_clk line discipline/streams modules described in the kernel + * directory. For the highest precision, the signal is captured using + * the carrier-detect line of the same serial port using the ppsclock + * streams module described in the ppsclock directory. */ +#define REFCLOCKMAXDISPERSE (FP_SECOND/4) /* max sample dispersion */ +#define MAXUNIT 4 /* max units */ +#ifndef CLKLDISC +#define CLKLDISC 10 /* XXX temp tty_clk line discipline */ +#endif +#ifndef CHULDISC +#define CHULDISC 10 /* XXX temp tty_chu line discipline */ +#endif /* - * The refclock configuration table. Imported from refclock_conf.c + * The refclock configuration table. Imported from refclock_conf */ -extern struct refclock *refclock_conf[]; -extern u_char num_refclock_conf; +extern struct refclock *refclock_conf[]; +extern u_char num_refclock_conf; /* * Imported from the I/O module */ -extern struct interface *any_interface; -extern struct interface *loopback_interface; +extern struct interface *any_interface; +extern struct interface *loopback_interface; + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ /* * Imported from the timer module */ -extern U_LONG current_time; -extern struct event timerqueue[]; +extern u_long current_time; +extern struct event timerqueue[]; /* - * Imported from the main and peer modules. We use the same - * algorithm for spacing out timers at configuration time that - * the peer module does. + * Imported from the main and peer modules. We use the same algorithm + * for spacing out timers at configuration time that the peer module + * does. */ -extern U_LONG init_peer_starttime; -extern int initializing; -extern int debug; +extern u_long init_peer_starttime; +extern int initializing; +extern int debug; + +/* + * 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]; + + +/* + * 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(peer, code) + struct peer *peer; + u_char 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; + if (code == CEVNT_NOMINAL) + return; + pp->lastevent = code; + if (code == CEVNT_FAULT) + syslog(LOG_ERR, + "clock %s fault %x", ntoa(&peer->srcadr), code); + else { + syslog(LOG_INFO, + "clock %s event %x", ntoa(&peer->srcadr), code); + } + } +} + + +/* + * 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() +{ + int i, j; + + for (i = 0; i < 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; + } +} -static void refclock_transmit P((struct peer *)); /* * 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(peer) - struct peer *peer; + struct peer *peer; /* peer structure pointer */ { + struct refclockproc *pp; u_char clktype; int unit; /* - * Sanity... + * Check for valid clock address. If already running, shut it * down first. */ if (!ISREFCLOCKADR(&peer->srcadr)) { syslog(LOG_ERR, - "Internal error: attempting to initialize %s as reference clock", + "refclock_newpeer: clock address %s invalid", ntoa(&peer->srcadr)); - return 0; + return (0); } - clktype = REFCLOCKTYPE(&peer->srcadr); unit = REFCLOCKUNIT(&peer->srcadr); + if (clktype >= num_refclock_conf || unit > MAXUNIT || + refclock_conf[clktype]->clock_start == noentry) { + syslog(LOG_ERR, + "refclock_newpeer: clock type %d invalid\n", + clktype); + return (0); + } + refclock_unpeer(peer); /* - * If clktype is invalid, return + * Allocate and initialize interface structure */ - if (clktype >= num_refclock_conf - || refclock_conf[clktype]->clock_start == noentry) { - syslog(LOG_ERR, - "Can't initialize %s, no support for clock type %d\n", - ntoa(&peer->srcadr), clktype); - return 0; - } + if (!(pp = (struct refclockproc *) + emalloc(sizeof(struct refclockproc)))) + return (0); + memset((char *)pp, 0, sizeof(struct refclockproc)); + typeunit[clktype][unit] = peer; + peer->procptr = pp; /* - * Complete initialization of peer structure. + * Initialize structures */ peer->refclktype = clktype; peer->refclkunit = unit; peer->flags |= FLAG_REFCLOCK; - peer->stratum = STRATUM_REFCLOCK; - peer->ppoll = peer->minpoll; - peer->hpoll = peer->minpoll; peer->event_timer.peer = peer; peer->event_timer.event_handler = refclock_transmit; + pp->type = clktype; + pp->timestarted = current_time; + peer->stratum = STRATUM_REFCLOCK; + peer->refid = peer->srcadr.sin_addr.s_addr; + peer->maxpoll = peer->minpoll; /* * Do driver dependent initialization */ if (!((refclock_conf[clktype]->clock_start)(unit, peer))) { - syslog(LOG_ERR, "Clock dependent initialization of %s failed", - ntoa(&peer->srcadr)); - return 0; + 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; /* - * Set up the timeout for reachability determination. + * Set up the timeout for polling and reachability determination */ if (initializing) { init_peer_starttime += (1 << EVENT_TIMEOUT); @@ -114,10 +248,11 @@ refclock_newpeer(peer) init_peer_starttime = (1 << EVENT_TIMEOUT); peer->event_timer.event_time = init_peer_starttime; } else { - peer->event_timer.event_time = current_time + (1 << peer->hpoll); + peer->event_timer.event_time = current_time + + (1 << peer->hpoll); } TIMER_ENQUEUE(timerqueue, &peer->event_timer); - return 1; + return (1); } @@ -126,42 +261,50 @@ refclock_newpeer(peer) */ void refclock_unpeer(peer) - struct peer *peer; + struct peer *peer; /* peer structure pointer */ { - /* - * Do sanity checks. I know who programmed the calling routine! - */ - if (peer->refclktype >= num_refclock_conf - || refclock_conf[peer->refclktype]->clock_shutdown == noentry) { - syslog(LOG_ERR, "Attempting to shutdown %s: no such clock!", - ntoa(&peer->srcadr)); - return; - } + u_char clktype; + int unit; /* - * Tell the driver we're finished. + * Wiggle the driver to release its resources, then give back + * the interface structure. */ - (refclock_conf[peer->refclktype]->clock_shutdown)(peer->refclkunit); + 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 - replacement transmit procedure for reference clocks + * 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. */ -static void +void refclock_transmit(peer) - struct peer *peer; + struct peer *peer; /* peer structure pointer */ { - u_char opeer_reach; - int clktype; + struct refclockproc *pp; + u_char clktype; int unit; + u_char opeer_reach; + pp = peer->procptr; clktype = peer->refclktype; unit = peer->refclkunit; peer->sent++; /* - * The transmit procedure is supposed to freeze a time stamp. + * The transmit procedure is supposed to freeze a timestamp. * Get one just for fun, and to tell when we last were here. */ get_systime(&peer->xmt); @@ -173,9 +316,9 @@ refclock_transmit(peer) peer->reach <<= 1; if (peer->reach == 0) { /* - * Clear this one out. No need to redo - * selection since this fellow will definitely - * be suffering from dispersion madness. + * Clear this one out. No need to redo selection since + * this fellow will definitely be suffering from + * dispersion madness. */ if (opeer_reach != 0) { peer_clear(peer); @@ -187,12 +330,11 @@ refclock_transmit(peer) * Update reachability and poll variables */ } else if ((opeer_reach & 3) == 0) { - l_fp off; if (peer->valid > 0) peer->valid--; - off.l_ui = off.l_uf = 0; + L_CLR(&off); clock_filter(peer, &off, 0, NTP_MAXDISPERSE); if (peer->flags & FLAG_SYSPEER) clock_select(); @@ -200,11 +342,13 @@ refclock_transmit(peer) peer->valid++; /* - * If he wants to be polled, do it. + * If he wants to be polled, do it. New style drivers do not use + * the unit argument, since the fudge stuff is in the + * refclockproc structure. */ if (refclock_conf[clktype]->clock_poll != noentry) (refclock_conf[clktype]->clock_poll)(unit, peer); - + /* * Finally, reset the timer */ @@ -214,22 +358,150 @@ refclock_transmit(peer) /* - * refclock_receive - simulate the receive and packet procedures for clocks + * Compare two l_fp's - used with qsort() + */ +static int +refclock_cmpl_fp(p1, p2) + register void *p1, *p2; /* l_fp to compare */ +{ + + if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2)) + return (-1); + if (L_ISEQU((l_fp *)p1, (l_fp *)p2)) + return (0); + return (1); +} + + +/* + * refclock_process - process a pile of samples from the clock + * + * This routine converts the timecode in the form days, hours, miinutes, + * seconds, milliseconds/microseconds to internal timestamp format. It + * then calculates the difference from the receive timestamp and + * assembles the samples in a shift register. It implements a recursive + * median filter to suppress spikes in the data, as well as determine a + * rough dispersion estimate. A configuration constant time adjustment + * fudgetime1 can be added to the final offset to compensate for various + * systematic errors. The routine returns one if success and zero if + * failure due to invalid timecode data or very noisy offsets. + */ +int +refclock_process(pp, nstart, nskeep) + struct refclockproc *pp; /* peer structure pointer */ + int nstart; /* stages of median filter */ + int nskeep; /* stages after outlyer trim */ +{ + int i, n; + l_fp offset, median, lftmp; + l_fp off[MAXSTAGE]; + u_fp disp; + + /* + * 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. + */ + pp->nstages = nstart; + if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT, + pp->lastrec.l_ui, &pp->yearstart, &pp->lastref.l_ui)) + return (0); + if (pp->usec) { + TVUTOTSF(pp->usec, pp->lastref.l_uf); + } else { + MSUTOTSF(pp->msec, pp->lastref.l_uf); + } + + /* + * Subtract the receive timestamp from the timecode timestamp + * to form the raw offset. Insert in the median filter shift + * register. + */ + i = ((int)(pp->coderecv)) % pp->nstages; + offset = pp->lastref; + L_SUB(&offset, &pp->lastrec); + pp->filter[i] = offset; + if (pp->coderecv == 0) + for (i = 1; i < pp->nstages; i++) + pp->filter[i] = pp->filter[0]; + pp->coderecv++; + + /* + * Copy the raw offsets and sort into ascending order + */ + for (i = 0; i < pp->nstages; i++) + off[i] = pp->filter[i]; + qsort((char *)off, pp->nstages, sizeof(l_fp), refclock_cmpl_fp); + + /* + * Reject the furthest from the median of nstages samples until + * nskeep samples remain. + */ + i = 0; + n = pp->nstages; + while ((n - i) > nskeep) { + 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--; + } + } + + /* + * 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. In either case include + * the configured fudgetime1 adjustment. + */ + lftmp = off[n - 1]; + L_SUB(&lftmp, &off[i]); + disp = LFPTOFP(&lftmp) + current_time - pp->lasttime; + if (disp > REFCLOCKMAXDISPERSE) + return (0); + pp->offset = offset; + L_ADD(&pp->offset, &pp->fudgetime1); + pp->dispersion = disp; + return (1); +} + + +/* + * 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(peer, offset, delay, dispersion, reftime, rectime, leap) - struct peer *peer; - l_fp *offset; - s_fp delay; - u_fp dispersion; - l_fp *reftime; - l_fp *rectime; - int leap; + struct peer *peer; /* peer structure pointer */ + l_fp *offset; /* computed offset (s) */ + s_fp delay; /* computed delay to peer */ + u_fp dispersion; /* computed dispersion to peer */ + l_fp *reftime; /* time at last clock update */ + l_fp *rectime; /* time at last peer update */ + int leap; /* synchronization/leap code */ { int restrict; int trustable; - extern u_char leap_indicator; + u_fp precision; + peer->received++; #ifdef DEBUG if (debug) printf("refclock_receive: %s %s %s %s)\n", @@ -238,20 +510,12 @@ refclock_receive(peer, offset, delay, dispersion, reftime, rectime, leap) #endif /* - * The name of this routine is actually a misnomer since - * we mostly simulate the variable setting of the packet - * procedure. We do check the flag values, though, and - * set the trust bits based on this. + * The authentication and access-control machinery works, but + * its utility may be questionable. */ restrict = restrictions(&peer->srcadr); - if (restrict & (RES_IGNORE|RES_DONTSERVE)) { - /* - * Ours is not to reason why... - */ + if (restrict & (RES_IGNORE|RES_DONTSERVE)) return; - } - - peer->received++; peer->processed++; peer->timereceived = current_time; if (restrict & RES_DONTTRUST) @@ -265,155 +529,758 @@ refclock_receive(peer, offset, delay, dispersion, reftime, rectime, leap) else peer->flags &= ~FLAG_AUTHENTIC; } - if (leap == 0) - peer->leap = leap_indicator; - else - peer->leap = leap; + peer->leap = leap; /* - * Set the timestamps. rec and org are in local time, - * while ref is in timecode time. + * Set the timestamps. rec and org are in local time, while ref + * is in timecode time. */ peer->rec = peer->org = *rectime; peer->reftime = *reftime; /* - * If the interface has been set to any_interface, set it - * to the loop back address if we have one. This is so - * that peers which are unreachable are easy to see in - * peer display. + * 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. + * Set peer.pmode based on the hmode. For appearances only. */ switch (peer->hmode) { + case MODE_ACTIVE: peer->pmode = MODE_PASSIVE; break; - case MODE_CLIENT: + + default: peer->pmode = MODE_SERVER; break; - default: - syslog(LOG_ERR, "refclock_receive: internal error, mode = %d", - peer->hmode); } /* * Abandon ship if the radio came bum. We only got this far * in order to make pretty billboards, even if bum. */ - if (leap == LEAP_NOTINSYNC) return; + if (leap == LEAP_NOTINSYNC) + return; /* * If this guy was previously unreachable, report him * reachable. */ if (peer->reach == 0) report_event(EVNT_REACH, peer); - peer->reach |= 1; + peer->reach |= 1; /* - * Give the data to the clock filter and update the clock. + * Give the data to the clock filter and update the clock. Note + * the clock reading precision initialized by the driver is + * added at this point. */ - clock_filter(peer, offset, delay, dispersion); + precision = FP_SECOND >> -(int)peer->precision; + if (precision == 0) + precision = 1; + refclock_report(peer, CEVNT_NOMINAL); + clock_filter(peer, offset, delay, dispersion + precision); clock_update(peer); } /* - * refclock_control - set and/or return clock values + * 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 line + * discipline/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. */ -void -refclock_control(srcadr, in, out) - struct sockaddr_in *srcadr; - struct refclockstat *in; - struct refclockstat *out; +int +refclock_gtlin(rbufp, lineptr, bmax, tsptr) + struct recvbuf *rbufp; /* receive buffer pointer */ + char *lineptr; /* current line pointer */ + int bmax; /* remaining characters in line */ + l_fp *tsptr; /* pointer to timestamp returned */ { - u_char clktype; - int unit; + char *dpt, *dpend, *dp; + int i; + l_fp trtmp, tstmp; + char c; + + /* + * Check for the presence of a timestamp left by the tty_clock + * line discipline/streams 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; + if (dpend >= dpt + 8) { + if (buftvtots(dpend - 8, &tstmp)) { + L_SUB(&trtmp, &tstmp); + if (trtmp.l_ui == 0) { +#ifdef DEBUG + if (debug) { + printf( + "refclock_gtlin: fd %d ldisc %s", + rbufp->fd, + lfptoa(&trtmp, 6)); + gettstamp(&trtmp); + L_SUB(&trtmp, &tstmp); + printf(" sigio %s\n", + lfptoa(&trtmp, 6)); + } +#endif + dpend -= 8; + trtmp = tstmp; + } else + trtmp = rbufp->recv_time; + } + } + + /* + * Edit timecode to remove control chars. Don't monkey with the + * line buffer if the input buffer contains no ASCII printing + * characters. + */ + if (dpend - dpt > bmax - 1) + dpend = dpt + bmax - 1; + for (dp = lineptr; dpt < dpend; dpt++) { + c = *dpt & 0x7f; + if (c >= ' ') + *dp++ = c; + } + i = dp - lineptr; + if (i > 0) + *dp = '\0'; + +#ifdef DEBUG + if (debug) + printf("refclock_gtlin: fd %d time %s timecode %d %s\n", + rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr); +#endif + *tsptr = trtmp; + return (i); +} + + +/* + * 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(dev, speed, flags) + char *dev; /* device name pointer */ + int speed; /* serial port speed (code) */ + int flags; /* line discipline flags */ +{ + int fd; +#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 HAVE_MODEM_CONTROL + u_long ltemp; +#endif /* HAVE_MODEM_CONTROL */ + + /* + * Open serial port and set default options + */ + fd = open(dev, O_RDWR, 0777); + if (fd == -1) { + syslog(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 /* - * Sanity... + * POSIX serial line parameters (termios interface) */ - if (!ISREFCLOCKADR(srcadr)) { + if (tcgetattr(fd, ttyp) < 0) { syslog(LOG_ERR, - "Internal error: refclock_control received %s as reference clock", - ntoa(srcadr)); - return; + "refclock_open: fd %d tcgetattr %m", fd); + return (0); } - clktype = REFCLOCKTYPE(srcadr); - unit = REFCLOCKUNIT(srcadr); + /* + * 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, speed); + (void)cfsetospeed(&ttyb, speed); + ttyp->c_lflag = ICANON; + ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; +#ifdef HAVE_MODEM_CONTROL + /* + * 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 *)<emp) < 0) + syslog(LOG_ERR, + "refclock_open: fd %d TIOCMGET %m", fd); +#if DEBUG + if (debug) + printf("refclock_open: fd %d modem status %lx\n", + fd, ltemp); +#endif + if (ltemp & TIOCM_DSR) + ttyp->c_cflag &= ~CLOCAL; +#endif /* HAVE_MODEM_CONTROL */ + if (tcsetattr(fd, TCSANOW, ttyp) < 0) { + syslog(LOG_ERR, + "refclock_open: fd %d tcsetattr %m", fd); + return (0); + } + if (tcflush(fd, TCIOFLUSH) < 0) { + syslog(LOG_ERR, + "refclock_open: fd %d tcflush %m", fd); + return (0); + } +#endif /* HAVE_TERMIOS */ + +#ifdef HAVE_SYSV_TTYS /* - * If clktype is invalid, return + * System V serial line parameters (termio interface) + * */ - if (clktype >= num_refclock_conf - || refclock_conf[clktype]->clock_control == noentry) { + if (ioctl(fd, TCGETA, ttyp) < 0) { syslog(LOG_ERR, - "Internal error: refclock_control finds %s as not supported", - ntoa(srcadr)); - return; + "refclock_open: fd %d TCGETA %m", fd); + return (0); } /* - * Give the stuff to the clock. + * 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'; +#ifdef HAVE_MODEM_CONTROL + /* + * 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. */ - (refclock_conf[clktype]->clock_control)(unit, in, out); + ltemp = 0; + if (ioctl(fd, TIOCMGET, (char *)<emp) < 0) + syslog(LOG_ERR, + "refclock_open: fd %d TIOCMGET %m", fd); +#if DEBUG + if (debug) + printf("refclock_open: fd %d modem status %lx\n", + fd, ltemp); +#endif + if (ltemp & TIOCM_DSR) + ttyp->c_cflag &= ~CLOCAL; +#endif /* HAVE_MODEM_CONTROL */ + if (ioctl(fd, TCSETA, ttyp) < 0) { + syslog(LOG_ERR, + "refclock_open: fd %d TCSETA %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) { + syslog(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) { + syslog(LOG_ERR, + "refclock_open: TIOCSETP %m"); + return (0); + } +#endif /* HAVE_BSD_TTYS */ + + if (!refclock_ioctl(fd, flags)) { + (void)close(fd); + syslog(LOG_ERR, "refclock_open: fd %d ioctl fails", + fd); + return (0); + } + return (fd); } +/* + * 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 the tty_clk, chu_clk and ppsclock streams module/line discipline, + * if compiled in the daemon and requested in the call. The routine + * returns one if success and zero if failure. + */ +int +refclock_ioctl(fd, flags) + int fd; /* file descriptor */ + int flags; /* line discipline flags */ +{ +#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 DEBUG + if (debug) + printf("refclock_ioctl: fd %d flags %x\n", + fd, flags); +#endif + + /* + * The following sections select optional features, such as + * modem control, line discipline 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, or line discipline modules, which must + * be compiled in the kernel. The streams modules require System + * V STREAMS support, while the line discipline modules require + * 4.3bsd or later. The checking frenzy is attenuated here, + * since the device is already open. + * + * Note that both the clk and ppsclock modules are optional; the + * dang thing still works, but the accuracy improvement using + * them will not be available. The ppsclock module is associated + * with a specific, declared line and should be used only once. + * If requested, the chu module is mandatory, since the driver + * will not work without it. + * + * Use the LDISC_PPS option ONLY with Sun baseboard ttya or + * ttyb. Using it with the SPIF multipexor crashes the kernel. + */ + if (flags == 0) + return (1); + +#if !(defined(HAVE_TERMIOS) || defined(HAVE_BSD_TTYS)) + if (flags & (LDISC_CLK | LDISC_CHU | LDISC_PPS | LDISC_ACTS)) + syslog(LOG_ERR, + "refclock_ioctl: unsupported terminal interface"); + return (0); +#endif /* HAVE_TERMIOS HAVE_BSD_TTYS */ + + ttyp = &ttyb; + +#ifdef STREAM +#ifdef CLK + + /* + * The CLK option provides timestamping at the driver level. + * It requires the tty_clk streams module and System V STREAMS + * support. + */ + if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) { + if (ioctl(fd, I_PUSH, "clk") < 0) + syslog(LOG_NOTICE, + "refclock_ioctl: optional clk streams module unavailable"); + else { + char *str; + + if (flags & LDISC_PPS) + str = "\377"; + else if (flags & LDISC_ACTS) + str = "*"; + else + str = "\n"; + if (ioctl(fd, CLK_SETSTR, str) < 0) + syslog(LOG_ERR, + "refclock_ioctl: CLK_SETSTR %m"); + } + } + + /* + * The ACTS line discipline requires additional line-ending + * character '*'. + */ + if (flags & LDISC_ACTS) { + (void)tcgetattr(fd, ttyp); + ttyp->c_cc[VEOL] = '*'; + (void)tcsetattr(fd, TCSANOW, ttyp); + } +#else + if (flags & LDISC_CLK) + syslog(LOG_NOTICE, + "refclock_ioctl: optional clk streams module unsupported"); +#endif /* CLK */ +#ifdef CHU + + /* + * The CHU option provides timestamping and decoding for the CHU + * timecode. It requires the tty_chu streams module and System V + * STREAMS support. + */ + if (flags & LDISC_CHU) { + (void)tcgetattr(fd, ttyp); + ttyp->c_lflag = 0; + ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; + ttyp->c_cc[VMIN] = 1; + ttyp->c_cc[VTIME] = 0; + (void)tcsetattr(fd, TCSANOW, ttyp); + (void)tcflush(fd, TCIOFLUSH); + while (ioctl(fd, I_POP, 0) >= 0); + if (ioctl(fd, I_PUSH, "chu") < 0) { + syslog(LOG_ERR, + "refclock_ioctl: required chu streams module unavailable"); + return (0); + } + } +#else + if (flags & LDISC_CHU) { + syslog(LOG_ERR, + "refclock_ioctl: required chu streams module unsupported"); + return (0); + } +#endif /* CHU */ +#ifdef PPS + + /* + * 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. + */ + if (flags & LDISC_PPS) { + if (fdpps != -1) { + syslog(LOG_ERR, + "refclock_ioctl: ppsclock already configured"); + return (0); + } + if (ioctl(fd, I_PUSH, "ppsclock") < 0) + syslog(LOG_NOTICE, + "refclock_ioctl: optional ppsclock streams module unavailable"); + else + fdpps = fd; + } +#else + if (flags & LDISC_PPS) + syslog(LOG_NOTICE, + "refclock_ioctl: optional ppsclock streams module unsupported"); +#endif /* PPS */ + +#else /* STREAM */ + +#ifdef HAVE_TERMIOS +#ifdef CLK + + /* + * The CLK option provides timestamping at the driver level. It + * requires the tty_clk line discipline and 4.3bsd or later. + */ + if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) { + (void)tcgetattr(fd, ttyp); + ttyp->c_lflag = 0; + if (flags & LDISC_CLKPPS) + ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\377'; + else if (flags & LDISC_ACTS) { + ttyp->c_cc[VERASE] = '*'; + ttyp->c_cc[VKILL] = '#'; + } else + ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\n'; + ttyp->c_cc[VMIN] = 1; + ttyp->c_cc[VTIME] = 0; + ttyp->c_line = CLKLDISC; + (void)tcsetattr(fd, TCSANOW, ttyp); + (void)tcflush(fd, TCIOFLUSH); + } +#else + if (flags & LDISC_CLK) + syslog(LOG_NOTICE, + "refclock_ioctl: optional clk line discipline unsupported"); +#endif /* CLK */ +#ifdef CHU + /* + * The CHU option provides timestamping and decoding for the CHU + * timecode. It requires the tty_chu line disciplne and 4.3bsd + * or later. + */ + if (flags & LDISC_CHU) { + (void)tcgetattr(fd, ttyp); + ttyp->c_lflag = 0; + ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\r'; + ttyp->c_cc[VMIN] = 1; + ttyp->c_cc[VTIME] = 0; + ttyp->c_line = CHULDISC; + (void)tcsetattr(fd, TCSANOW, ttyp) < 0); + (void)tcflush(fd, TCIOFLUSH); + } +#else + if (flags & LDISC_CHU) { + syslog(LOG_ERR, + "refclock_ioctl: required chu line discipline unsupported"); + return (0); + } +#endif /* CHU */ +#endif /* HAVE_TERMIOS */ + +#ifdef HAVE_BSD_TTYS +#ifdef CLK + + /* + * The CLK option provides timestamping at the driver level. It + * requires the tty_clk line discipline and 4.3bsd or later. + */ + if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) { + int ldisc = CLKLDISC; + + (void)ioctl(fd, TIOCGETP, (char *)ttyp); + if (flags & LDISC_CLKPPS) + ttyp->sg_erase = ttyp->sg_kill = '\377'; + else if (flags & LDISC_ACTS) { + ttyp->sg_erase = '*'; + ttyp->sg_kill = '#'; + } else + ttyp->sg_erase = ttyp->sg_kill = '\r'; + ttyp->sg_flags = RAW; + (void)ioctl(fd, TIOCSETP, ttyp); + if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0) + syslog(LOG_NOTICE, + "refclock_ioctl: optional clk line discipline unavailable"); + } +#else + if (flags & LDISC_CLK) + syslog(LOG_NOTICE, + "refclock_ioctl: optional clk line discipline unsupported"); + +#endif /* CLK */ +#ifdef CHU + + /* + * The CHU option provides timestamping and decoding for the CHU + * timecode. It requires the tty_chu line disciplne and 4.3bsd + * or later. + */ + if (flags & LDISC_CHU) { + int ldisc = CHULDISC; + + (void)ioctl(fd, TIOCGETP, (char *)ttyp); + ttyp->sg_erase = ttyp->sg_kill = '\r'; + ttyp->sg_flags = RAW; + (void)ioctl(fd, TIOCSETP, (char *)ttyp); + if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0) { + syslog(LOG_ERR, + "refclock_ioctl: required chu line discipline unavailable"); + return (0); + } + } +#else + if (flags & LDISC_CHU) { + syslog(LOG_ERR, + "refclock_ioctl: required chu line discipline unsupported"); + return (0); + } +#endif /* CHU */ +#endif /* HAVE_BSD_TTYS */ + +#endif /* STREAM */ + + return (1); +} + /* - * refclock_buginfo - return debugging info + * 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 + * xntpdc and the clockstat command. It can also be used to initialize + * configuration variables, such as fudgetimes, fudgevalues, reference + * ID and stratum. */ void -refclock_buginfo(srcadr, bug) +refclock_control(srcadr, in, out) struct sockaddr_in *srcadr; - struct refclockbug *bug; + struct refclockstat *in; + struct refclockstat *out; { + struct peer *peer; + struct refclockproc *pp; u_char clktype; int unit; /* - * Sanity... + * Check for valid address and running peer */ - if (!ISREFCLOCKADR(srcadr)) { - syslog(LOG_ERR, - "Internal error: refclock_buginfo received %s as reference clock", - ntoa(srcadr)); + if (!ISREFCLOCKADR(srcadr)) return; - } - clktype = REFCLOCKTYPE(srcadr); unit = REFCLOCKUNIT(srcadr); + if (clktype >= num_refclock_conf || unit > MAXUNIT) + return; + if (!(peer = typeunit[clktype][unit])) + return; + pp = peer->procptr; /* - * If clktype is invalid or call is unsupported, return + * Initialize requested data */ - if (clktype >= num_refclock_conf || - refclock_conf[clktype]->clock_buginfo == noentry) { - return; + 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 = 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; + } + if (in->flags & CLK_FLAG3) + (void)refclock_ioctl(pp->io.fd, LDISC_PPS); + } + + /* + * 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 = 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->lastcode = pp->lastcode; } /* - * Give the stuff to the clock. + * Give the stuff to the clock */ - (refclock_conf[clktype]->clock_buginfo)(unit, bug); + if (refclock_conf[clktype]->clock_control != noentry) + (refclock_conf[clktype]->clock_control)(unit, in, out); } + /* - * init_refclock - initialize the reference clock drivers + * 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 + * xntpdc and the clkbug command. */ void -init_refclock() +refclock_buginfo(srcadr, bug) + struct sockaddr_in *srcadr; /* clock address */ + struct refclockbug *bug; /* output structure */ { - register u_char i; + struct peer *peer; + struct refclockproc *pp; + u_char clktype; + int unit; + int i; - for (i = 0; i < num_refclock_conf; i++) { - if (refclock_conf[i]->clock_init != noentry) - (refclock_conf[i]->clock_init)(); - } + /* + * Check for valid address and peer structure + */ + if (!ISREFCLOCKADR(srcadr)) + return; + clktype = 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->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->ntimes = pp->nstages + 3; + if (bug->ntimes > NCLKBUGTIMES) + bug->ntimes = NCLKBUGTIMES; + bug->stimes = 0xfffffffc; + bug->times[0] = pp->lastref; + bug->times[1] = pp->lastrec; + UFPTOLFP(pp->dispersion, &bug->times[2]); + for (i = 0; i < bug->ntimes; i++) + bug->times[i + 3] = pp->filter[i]; + + /* + * Give the stuff to the clock + */ + if (refclock_conf[clktype]->clock_buginfo != noentry) + (refclock_conf[clktype]->clock_buginfo)(unit, bug); } -#endif + +#endif /* REFCLOCK */ diff --git a/usr.sbin/xntpd/xntpd/ntp_request.c b/usr.sbin/xntpd/xntpd/ntp_request.c index a77669875bea..5b3d481df1ed 100644 --- a/usr.sbin/xntpd/xntpd/ntp_request.c +++ b/usr.sbin/xntpd/xntpd/ntp_request.c @@ -1,10 +1,9 @@ -/* ntp_request.c,v 3.1 1993/07/06 01:11:26 jbj Exp +/* * ntp_request.c - respond to information requests */ #include <sys/types.h> #include <stdio.h> #include <errno.h> -#include <sys/ioctl.h> #include <sys/time.h> #include "ntpd.h" @@ -17,7 +16,7 @@ #include "ntp_stdlib.h" #ifdef KERNEL_PLL -#include <sys/timex.h> +#include "sys/timex.h" #define ntp_gettime(t) syscall(SYS_ntp_gettime, (t)) #define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t)) #endif /* KERNEL_PLL */ @@ -62,7 +61,7 @@ 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 setclr_flags P((struct sockaddr_in *, struct interface *, struct req_pkt *, u_long)); static void do_monitor P((struct sockaddr_in *, struct interface *, struct req_pkt *)); static void do_nomonitor P((struct sockaddr_in *, struct interface *, struct req_pkt *)); static void list_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *)); @@ -70,7 +69,8 @@ static void do_resaddflags P((struct sockaddr_in *, struct interface *, struct r 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 P((struct sockaddr_in *, struct interface *, struct req_pkt *)); +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 *)); @@ -125,23 +125,24 @@ static struct req_proc xntp_codes[] = { { 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 }, + { 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_DO_DIRTY_HACK, AUTH, 0, do_dirty_hack }, { REQ_DONT_DIRTY_HACK, AUTH, 0, dont_dirty_hack }, - { REQ_TRUSTKEY, AUTH, sizeof(U_LONG), trust_key }, - { REQ_UNTRUSTKEY, AUTH, sizeof(U_LONG), untrust_key }, + { 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_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 }, { REQ_GET_LEAPINFO, NOAUTH, 0, get_leap_info }, - { REQ_SET_PRECISION, AUTH, sizeof(LONG), set_precision }, + { REQ_SET_PRECISION, AUTH, sizeof(long), set_precision }, #ifdef KERNEL_PLL { REQ_GET_KERNEL, NOAUTH, 0, get_kernel_info }, #endif /* KERNEL_PLL */ @@ -158,16 +159,16 @@ static struct req_proc xntp_codes[] = { * Authentication keyid used to authenticate requests. Zero means we * don't allow writing anything. */ -U_LONG info_auth_keyid; +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 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 */ +u_long errorcounter[INFO_ERR_AUTH+1]; /* lazy way to count errors, indexed */ /* by the error code */ #ifdef KERNEL_PLL @@ -187,18 +188,33 @@ extern int debug; /* * Imported from the timer module */ -extern U_LONG current_time; +extern u_long current_time; /* * Imported from ntp_loopfilter.c */ extern int pll_control; +extern int pll_enable; +extern int pps_control; + +/* + * Imported from ntp_monitor.c + */ +extern int mon_enabled; + +/* + * Imported from ntp_util.c + */ +extern int stats_control; + +extern struct peer *peer_hash[]; +extern struct peer *sys_peer; /* * A hack. To keep the authentication module clear of xntp-ism's, we * include a time reset variable for its stats here. */ -static U_LONG auth_timereset; +static u_long auth_timereset; /* * Response packet used by these routines. Also some state information @@ -258,7 +274,7 @@ req_ack(srcadr, inter, inpkt, errcode) /* * send packet and bump counters */ - sendpkt(srcadr, inter, (struct pkt *)&rpkt, RESP_HEADER_SIZE); + sendpkt(srcadr, inter, -1, (struct pkt *)&rpkt, RESP_HEADER_SIZE); errorcounter[errcode]++; } @@ -322,7 +338,7 @@ more_pkt() rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, MORE_BIT); rpkt.auth_seq = AUTH_SEQ(0, seqno); rpkt.err_nitems = htons((u_short)nitems); - sendpkt(toaddr, frominter, (struct pkt *)&rpkt, + sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt, RESP_HEADER_SIZE+databytes); numresppkts++; @@ -388,7 +404,7 @@ flush_pkt() rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0); rpkt.auth_seq = AUTH_SEQ(0, seqno); rpkt.err_nitems = htons((u_short)nitems); - sendpkt(toaddr, frominter, (struct pkt *)&rpkt, + sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt, RESP_HEADER_SIZE+databytes); numresppkts++; } @@ -481,20 +497,18 @@ process_private(rbufp, mod_okay) * time stamp. */ if (proc->needs_auth) { - register U_LONG tmp_ui; - register U_LONG tmp_uf; + l_fp ftmp; /* * 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 + 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 %u pkt keyid %u\n", + "failed auth %d info_auth_keyid %lu pkt keyid %u\n", INFO_IS_AUTH(inpkt->auth_seq), info_auth_keyid, ntohl(inpkt->keyid)); #endif @@ -504,8 +518,8 @@ process_private(rbufp, mod_okay) if (rbufp->recv_length > REQ_LEN_MAC) { #ifdef DEBUG if (debug > 4) - printf("failed pkt length pkt %d req %d too long\n", - rbufp->recv_length, REQ_LEN_MAC); + printf("bad pkt length %d\n", + rbufp->recv_length); #endif req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); return; @@ -523,15 +537,12 @@ process_private(rbufp, mod_okay) * calculate absolute time difference between xmit time stamp * and receive time stamp. If too large, too bad. */ - tmp_ui = ntohl(inpkt->tstamp.l_ui); - tmp_uf = ntohl(inpkt->tstamp.l_uf); - M_SUB(tmp_ui, tmp_uf, rbufp->recv_time.l_ui, - rbufp->recv_time.l_uf); - if (M_ISNEG(tmp_ui, tmp_uf)) - M_NEG(tmp_ui, tmp_uf); + NTOHL_FP(&inpkt->tstamp, &ftmp); + L_SUB(&ftmp, &rbufp->recv_time); + if (L_ISNEG(&ftmp)) + L_NEG(&ftmp); - if (M_ISHIS(tmp_ui, tmp_uf, - INFO_TS_MAXSKEW_UI, INFO_TS_MAXSKEW_UF)) { + if (ftmp.l_ui >= INFO_TS_MAXSKEW_UI) { /* * He's a loser. Tell him. */ @@ -588,8 +599,6 @@ peer_list(srcadr, inter, inpkt) register struct info_peer_list *ip; register struct peer *pp; register int i; - extern struct peer *peer_hash[]; - extern struct peer *sys_peer; ip = (struct info_peer_list *)prepare_pkt(srcadr, inter, inpkt, sizeof(struct info_peer_list)); @@ -628,8 +637,6 @@ peer_list_sum(srcadr, inter, inpkt) register struct info_peer_summary *ips; register struct peer *pp; register int i; - extern struct peer *peer_hash[]; - extern struct peer *sys_peer; #ifdef DEBUG if (debug > 2) @@ -645,10 +652,14 @@ peer_list_sum(srcadr, inter, inpkt) if (debug > 3) printf("sum: got one\n"); #endif - if (pp->dstadr == any_interface) - ips->dstadr = 0; - else - ips->dstadr = pp->dstadr->sin.sin_addr.s_addr; + 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; @@ -712,7 +723,14 @@ peer_info (srcadr, inter, inpkt) ipl++; if ((pp = findexistingpeer(&addr, (struct peer *)0)) == 0) continue; - ip->dstadr = NSRCADR(&pp->dstadr->sin); + 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; @@ -743,7 +761,7 @@ peer_info (srcadr, inter, inpkt) ip->reach = pp->reach; ip->unreach = pp->unreach; ip->flash = pp->flash; - ip->estbdelay = htonl(pp->estbdelay); + ip->estbdelay = HTONS_FP(pp->estbdelay); ip->ttl = pp->ttl; ip->associd = htons(pp->associd); ip->rootdelay = HTONS_FP(pp->rootdelay); @@ -803,7 +821,14 @@ peer_stats (srcadr, inter, inpkt) ipl++; if ((pp = findexistingpeer(&addr, (struct peer *)0)) == 0) continue; - ip->dstadr = NSRCADR(&pp->dstadr->sin); + 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; @@ -826,21 +851,13 @@ peer_stats (srcadr, inter, inpkt) = htonl(pp->event_timer.event_time - current_time); ip->timereachable = htonl(current_time - pp->timereachable); ip->sent = htonl(pp->sent); - ip->received = htonl(pp->received); ip->processed = htonl(pp->processed); - ip->badlength = 0; ip->badauth = htonl(pp->badauth); ip->bogusorg = htonl(pp->bogusorg); ip->oldpkt = htonl(pp->oldpkt); - ip->baddelay = 0; - ip->seldelay = 0; ip->seldisp = htonl(pp->seldisptoolarge); ip->selbroken = htonl(pp->selbroken); - ip->selold = htonl(pp->seltooold); ip->candidate = pp->candidate; - ip->falseticker = 0; - ip->select = pp->select; - ip->select_total = 0; ip = (struct info_peer_stats *)more_pkt(); } flush_pkt(); @@ -866,14 +883,16 @@ sys_info(srcadr, inter, inpkt) extern s_char sys_precision; extern s_fp sys_rootdelay; extern u_fp sys_rootdispersion; - extern U_LONG sys_refid; + extern u_long sys_refid; extern l_fp sys_reftime; extern u_char sys_poll; extern struct peer *sys_peer; extern int sys_bclient; - extern U_LONG sys_bdelay; + extern s_fp sys_bdelay; extern int sys_authenticate; - extern U_LONG sys_authdelay; + extern u_long sys_authdelay; + extern u_fp clock_stability; + extern s_fp clock_frequency; is = (struct info_sys *)prepare_pkt(srcadr, inter, inpkt, sizeof(struct info_sys)); @@ -890,6 +909,8 @@ sys_info(srcadr, inter, inpkt) is->precision = sys_precision; is->rootdelay = htonl(sys_rootdelay); is->rootdispersion = htonl(sys_rootdispersion); + is->frequency = htonl(clock_frequency); + is->stability = htonl(clock_stability); is->refid = sys_refid; HTONL_FP(&sys_reftime, &is->reftime); @@ -899,9 +920,20 @@ sys_info(srcadr, inter, inpkt) if (sys_bclient) is->flags |= INFO_FLAG_BCLIENT; if (sys_authenticate) - is->flags |= INFO_FLAG_AUTHENABLE; - HTONL_UF(sys_bdelay, &is->bdelay); + is->flags |= INFO_FLAG_AUTHENTICATE; + if (pll_enable) + is->flags |= INFO_FLAG_PLL; + 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(sys_bdelay); HTONL_UF(sys_authdelay, &is->authdelay); + (void) more_pkt(); flush_pkt(); } @@ -921,15 +953,15 @@ sys_stats(srcadr, inter, inpkt) /* * 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; + 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)); @@ -966,10 +998,10 @@ mem_stats(srcadr, inter, inpkt) */ 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 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, @@ -1008,18 +1040,18 @@ io_stats(srcadr, inter, inpkt) /* * Importations from the io module */ - extern U_LONG io_timereset; - extern U_LONG full_recvbufs; - extern U_LONG free_recvbufs; - extern U_LONG total_recvbufs; - extern U_LONG lowater_additions; - extern U_LONG packets_dropped; - extern U_LONG packets_ignored; - extern U_LONG packets_received; - extern U_LONG packets_sent; - extern U_LONG packets_notsent; - extern U_LONG handler_calls; - extern U_LONG handler_pkts; + extern u_long io_timereset; + extern u_long full_recvbufs; + extern u_long free_recvbufs; + extern u_long total_recvbufs; + extern u_long lowater_additions; + extern u_long packets_dropped; + extern u_long packets_ignored; + extern u_long packets_received; + extern u_long packets_sent; + extern u_long packets_notsent; + extern u_long handler_calls; + extern u_long handler_pkts; io = (struct info_io_stats *)prepare_pkt(srcadr, inter, inpkt, sizeof(struct info_io_stats)); @@ -1056,10 +1088,10 @@ timer_stats(srcadr, inter, inpkt) /* * Importations from the timer module */ - extern U_LONG alarm_overflow; - extern U_LONG timer_timereset; - extern U_LONG timer_overflows; - extern U_LONG timer_xmtcalls; + extern u_long alarm_overflow; + 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)); @@ -1091,8 +1123,8 @@ loop_info(srcadr, inter, inpkt) */ extern l_fp last_offset; extern s_fp drift_comp; - extern int time_constant; - extern U_LONG watchdog_timer; + extern int tc_counter; + extern u_long last_time; li = (struct info_loop *)prepare_pkt(srcadr, inter, inpkt, sizeof(struct info_loop)); @@ -1100,8 +1132,8 @@ loop_info(srcadr, inter, inpkt) HTONL_FP(&last_offset, &li->last_offset); FPTOLFP(drift_comp, &tmp); HTONL_FP(&tmp, &li->drift_comp); - li->compliance = htonl(time_constant); - li->watchdog_timer = htonl(watchdog_timer); + li->compliance = htonl(tc_counter); + li->watchdog_timer = htonl(current_time - last_time); (void) more_pkt(); flush_pkt(); @@ -1139,7 +1171,7 @@ do_conf(srcadr, inter, inpkt) && cp->hmode != MODE_CLIENT && cp->hmode != MODE_BROADCAST) fl = 1; - if (cp->flags & ~(CONF_FLAG_AUTHENABLE|CONF_FLAG_PREFER)) + if (cp->flags & ~(CONF_FLAG_AUTHENABLE | CONF_FLAG_PREFER)) fl = 1; cp++; } @@ -1265,7 +1297,7 @@ set_sys_flag(srcadr, inter, inpkt) struct interface *inter; struct req_pkt *inpkt; { - setclr_flags(srcadr, inter, inpkt, (U_LONG)1); + setclr_flags(srcadr, inter, inpkt, 1); } @@ -1278,7 +1310,7 @@ clr_sys_flag(srcadr, inter, inpkt) struct interface *inter; struct req_pkt *inpkt; { - setclr_flags(srcadr, inter, inpkt, (U_LONG)0); + setclr_flags(srcadr, inter, inpkt, 0); } @@ -1290,9 +1322,9 @@ setclr_flags(srcadr, inter, inpkt, set) struct sockaddr_in *srcadr; struct interface *inter; struct req_pkt *inpkt; - U_LONG set; + u_long set; { - register U_LONG flags; + register u_long flags; if (INFO_NITEMS(inpkt->err_nitems) > 1) { req_ack(srcadr, inter, inpkt, INFO_ERR_FMT); @@ -1301,21 +1333,23 @@ setclr_flags(srcadr, inter, inpkt, set) flags = ((struct conf_sys_flags *)inpkt->data)->flags; - if (flags - & ~(SYS_FLAG_BCLIENT | SYS_FLAG_MCLIENT | SYS_FLAG_AUTHENTICATE)) { + if (flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_AUTHENTICATE | + SYS_FLAG_PLL | 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); - if (flags & SYS_FLAG_MCLIENT) - if (set) - proto_config(PROTO_MULTICAST_ADD, INADDR_NTP); - else - proto_config(PROTO_MULTICAST_DEL, INADDR_NTP); if (flags & SYS_FLAG_AUTHENTICATE) proto_config(PROTO_AUTHENTICATE, set); + if (flags & SYS_FLAG_PLL) + proto_config(PROTO_PLL, set); + if (flags & SYS_FLAG_MONITOR) + proto_config(PROTO_MONITOR, set); + if (flags & SYS_FLAG_FILEGEN) + proto_config(PROTO_FILEGEN, set); req_ack(srcadr, inter, inpkt, INFO_OKAY); } @@ -1454,7 +1488,7 @@ do_restrict(srcadr, inter, inpkt, op) bad = 1; if (cr->flags & ~(RES_ALLFLAGS)) bad = 1; - if (cr->addr == INADDR_ANY && cr->mask != INADDR_ANY) + if (cr->addr == htonl(INADDR_ANY) && cr->mask != htonl(INADDR_ANY)) bad = 1; cr++; } @@ -1490,7 +1524,7 @@ do_restrict(srcadr, inter, inpkt, op) * mon_getlist - return monitor data */ static void -mon_getlist(srcadr, inter, inpkt) +mon_getlist_0(srcadr, inter, inpkt) struct sockaddr_in *srcadr; struct interface *inter; struct req_pkt *inpkt; @@ -1502,7 +1536,7 @@ mon_getlist(srcadr, inter, inpkt) #ifdef DEBUG if (debug > 2) - printf("wants monitor list\n"); + printf("wants monitor 0 list\n"); #endif if (!mon_enabled) { req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); @@ -1530,6 +1564,57 @@ mon_getlist(srcadr, inter, inpkt) } /* + * mon_getlist - return monitor data + */ +static void +mon_getlist_1(srcadr, inter, inpkt) + 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(current_time - md->lasttime); + im->firsttime = htonl(current_time - md->firsttime); + if (md->lastdrop) + im->lastdrop = htonl(current_time - md->lastdrop); + else + im->lastdrop = 0; + im->count = htonl(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 { @@ -1557,7 +1642,7 @@ reset_stats(srcadr, inter, inpkt) struct interface *inter; struct req_pkt *inpkt; { - U_LONG flags; + u_long flags; struct reset_entry *rent; if (INFO_NITEMS(inpkt->err_nitems) > 1) { @@ -1714,11 +1799,11 @@ do_trustkey(srcadr, inter, inpkt, trust) struct req_pkt *inpkt; int trust; { - register U_LONG *kp; + register u_long *kp; register int items; items = INFO_NITEMS(inpkt->err_nitems); - kp = (U_LONG *)inpkt->data; + kp = (u_long *)inpkt->data; while (items-- > 0) { authtrust(*kp, trust); kp++; @@ -1742,14 +1827,13 @@ get_auth_info(srcadr, inter, inpkt) /* * Importations from the authentication module */ - extern U_LONG authnumkeys; - extern U_LONG authnumfreekeys; - extern U_LONG authkeylookups; - extern U_LONG authkeynotfound; - extern U_LONG authencryptions; - extern U_LONG authdecryptions; - extern U_LONG authdecryptok; - extern U_LONG authkeyuncached; + extern u_long authnumkeys; + extern u_long authnumfreekeys; + extern u_long authkeylookups; + extern u_long authkeynotfound; + extern u_long authencryptions; + extern u_long authdecryptions; + extern u_long authkeyuncached; ia = (struct info_auth *)prepare_pkt(srcadr, inter, inpkt, sizeof(struct info_auth)); @@ -1760,7 +1844,6 @@ get_auth_info(srcadr, inter, inpkt) ia->keynotfound = htonl(authkeynotfound); ia->encryptions = htonl(authencryptions); ia->decryptions = htonl(authdecryptions); - ia->decryptok = htonl(authdecryptok); ia->keyuncached = htonl(authkeyuncached); ia->timereset = htonl(current_time - auth_timereset); @@ -1780,18 +1863,16 @@ reset_auth_stats() /* * Importations from the authentication module */ - extern U_LONG authkeylookups; - extern U_LONG authkeynotfound; - extern U_LONG authencryptions; - extern U_LONG authdecryptions; - extern U_LONG authdecryptok; - extern U_LONG authkeyuncached; + 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; - authdecryptok = 0; authkeyuncached = 0; auth_timereset = current_time; } @@ -1837,7 +1918,7 @@ req_get_traps(srcadr, inter, inpkt) it->settime = htonl(current_time - tr->tr_settime); it->origtime = htonl(current_time - tr->tr_origtime); it->resets = htonl(tr->tr_resets); - it->flags = htonl((U_LONG)tr->tr_flags); + it->flags = htonl((u_long)tr->tr_flags); it = (struct info_trap *)more_pkt(); } } @@ -1951,7 +2032,7 @@ set_request_keyid(srcadr, inter, inpkt) struct interface *inter; struct req_pkt *inpkt; { - U_LONG keyid; + u_long keyid; /* * Restrict ourselves to one item only. @@ -1961,7 +2042,7 @@ set_request_keyid(srcadr, inter, inpkt) return; } - keyid = ntohl(*((U_LONG *)(inpkt->data))); + keyid = ntohl(*((u_long *)(inpkt->data))); info_auth_keyid = keyid; req_ack(srcadr, inter, inpkt, INFO_OKAY); } @@ -1977,8 +2058,8 @@ set_control_keyid(srcadr, inter, inpkt) struct interface *inter; struct req_pkt *inpkt; { - U_LONG keyid; - extern U_LONG ctl_auth_keyid; + u_long keyid; + extern u_long ctl_auth_keyid; /* * Restrict ourselves to one item only. @@ -1988,7 +2069,7 @@ set_control_keyid(srcadr, inter, inpkt) return; } - keyid = ntohl(*((U_LONG *)(inpkt->data))); + keyid = ntohl(*((u_long *)(inpkt->data))); ctl_auth_keyid = keyid; req_ack(srcadr, inter, inpkt, INFO_OKAY); } @@ -2009,21 +2090,21 @@ get_ctl_stats(srcadr, inter, inpkt) /* * 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; + 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)); @@ -2072,13 +2153,13 @@ get_leap_info(srcadr, inter, inpkt) extern u_char leap_indicator; extern u_char leap_warning; extern u_char leapbits; - extern U_LONG leap_timer; - extern U_LONG leap_processcalls; - extern U_LONG leap_notclose; - extern U_LONG leap_monthofleap; - extern U_LONG leap_dayofleap; - extern U_LONG leap_hoursfromleap; - extern U_LONG leap_happened; + extern u_long leap_timer; + extern u_long leap_processcalls; + extern u_long leap_notclose; + extern u_long leap_monthofleap; + extern u_long leap_dayofleap; + extern u_long leap_hoursfromleap; + extern u_long leap_happened; il = (struct info_leap *)prepare_pkt(srcadr, inter, inpkt, sizeof(struct info_leap)); @@ -2198,7 +2279,7 @@ get_clock_info(srcadr, inter, inpkt) ic->noresponse = htonl(clock.noresponse); ic->badformat = htonl(clock.badformat); ic->baddata = htonl(clock.baddata); - ic->timestarted = clock.timereset; + ic->timestarted = htonl(clock.timereset); HTONL_FP(&clock.fudgetime1, &ic->fudgetime1); HTONL_FP(&clock.fudgetime2, &ic->fudgetime2); ic->fudgeval1 = htonl(clock.fudgeval1); @@ -2285,9 +2366,9 @@ set_precision(srcadr, inter, inpkt) struct interface *inter; struct req_pkt *inpkt; { - register LONG precision; + register long precision; - precision = ntohl(*(LONG *)(inpkt->data)); + precision = ntohl(*(long *)(inpkt->data)); if (INFO_NITEMS(inpkt->err_nitems) > 1 || precision > -1 || precision < -20) { @@ -2353,10 +2434,9 @@ get_clkbug_info(srcadr, inter, inpkt) if (i > NUMCBUGTIMES) i = NUMCBUGTIMES; ic->ntimes = (u_char)i; - ic->stimes = htonl((U_LONG)bug.stimes & ((1<<i)-1)); + ic->stimes = htonl(bug.stimes); while (--i >= 0) { - ic->times[i].l_ui = htonl(bug.times[i].l_ui); - ic->times[i].l_uf = htonl(bug.times[i].l_uf); + HTONL_FP(&bug.times[i], &ic->times[i]); } ic = (struct info_clkbug *)more_pkt(); diff --git a/usr.sbin/xntpd/xntpd/ntp_restrict.c b/usr.sbin/xntpd/xntpd/ntp_restrict.c index 43f01f296624..0cc4b7d304b9 100644 --- a/usr.sbin/xntpd/xntpd/ntp_restrict.c +++ b/usr.sbin/xntpd/xntpd/ntp_restrict.c @@ -54,10 +54,10 @@ static int restrictcount; /* count of entries in the restriction list */ static struct restrictlist *resfree; static int numresfree; /* number of structures on free list */ -U_LONG res_calls; -U_LONG res_found; -U_LONG res_not_found; -U_LONG res_timereset; +u_long res_calls; +u_long res_found; +u_long res_not_found; +u_long res_timereset; /* * Parameters of the RES_LIMITED restriction option. @@ -65,14 +65,14 @@ U_LONG res_timereset; * 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; +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 ro RES_LIMITED control) */ -U_LONG res_limited_refcnt; +u_long res_limited_refcnt; /* * Our initial allocation of list entries. @@ -82,7 +82,7 @@ static struct restrictlist resinit[INITRESLIST]; /* * Imported from the timer module */ -extern U_LONG current_time; +extern u_long current_time; /* * debug flag @@ -116,7 +116,7 @@ init_restrict() * list as our default entry. Everything in here * should be zero for now. */ - resinit[0].addr = INADDR_ANY; + resinit[0].addr = htonl(INADDR_ANY); resinit[0].mask = 0; restrictlist = &resinit[0]; restrictcount = 1; @@ -137,9 +137,9 @@ init_restrict() client_limit_period = 3600; res_limited_refcnt = 0; - sprintf(bp, "client_limit=%d", client_limit); + sprintf(bp, "client_limit=%ld", client_limit); set_sys_var(bp, strlen(bp)+1, RO); - sprintf(bp, "client_limit_period=%d", client_limit_period); + sprintf(bp, "client_limit_period=%ld", client_limit_period); set_sys_var(bp, strlen(bp)+1, RO); } @@ -153,7 +153,7 @@ restrictions(srcadr) { register struct restrictlist *rl; register struct restrictlist *match; - register U_LONG hostaddr; + register u_long hostaddr; register int isntpport; res_calls++; @@ -210,7 +210,7 @@ restrictions(srcadr) #ifdef DEBUG if (debug > 2) - printf("limited clients check: %d clients, period %d seconds, net is 0x%X\n", + printf("limited clients check: %ld clients, period %ld seconds, net is 0x%lX\n", client_limit, client_limit_period, netof(hostaddr)); #endif /*DEBUG*/ @@ -238,7 +238,7 @@ restrictions(srcadr) > client_limit_period) { #ifdef DEBUG if (debug > 5) - printf("checking: %s: ignore: too old: %d\n", + printf("checking: %s: ignore: too old: %ld\n", numtoa(md->rmtadr), current_time - md->lasttime); #endif @@ -259,7 +259,7 @@ restrictions(srcadr) netof(hostaddr)) { #ifdef DEBUG if (debug > 5) - printf("checking: %s: different net 0x%X\n", + printf("checking: %s: different net 0x%lX\n", numtoa(md->rmtadr), netof(md->rmtadr)); #endif @@ -286,7 +286,7 @@ restrictions(srcadr) } #ifdef DEBUG if (debug > 4) - printf("this one is rank %d in list, limit is %d: %s\n", + printf("this one is rank %d in list, limit is %lu: %s\n", lcnt, client_limit, (lcnt <= client_limit) ? "ALLOW" : "REJECT"); #endif @@ -312,8 +312,8 @@ restrict(op, resaddr, resmask, mflags, flags) int mflags; int flags; { - register U_LONG addr; - register U_LONG mask; + register u_long addr; + register u_long mask; register struct restrictlist *rl; register struct restrictlist *rlprev; int i; @@ -329,7 +329,7 @@ restrict(op, resaddr, resmask, mflags, flags) * If this is the default address, point at first on list. Else * go searching for it. */ - if (addr == INADDR_ANY) { + if (addr == htonl(INADDR_ANY)) { rlprev = 0; rl = restrictlist; } else { @@ -433,7 +433,7 @@ restrict(op, resaddr, resmask, mflags, flags) * interface entry. */ if (rl != 0 - && rl->addr != INADDR_ANY + && rl->addr != htonl(INADDR_ANY) && !(rl->mflags & RESM_INTERFACE)) { rlprev->next = rl->next; restrictcount--; diff --git a/usr.sbin/xntpd/xntpd/ntp_timer.c b/usr.sbin/xntpd/xntpd/ntp_timer.c index e3ba54cd39bc..99551f74a9f3 100644 --- a/usr.sbin/xntpd/xntpd/ntp_timer.c +++ b/usr.sbin/xntpd/xntpd/ntp_timer.c @@ -1,4 +1,4 @@ -/* ntp_timer.c,v 3.1 1993/07/06 01:11:29 jbj Exp +/* * ntp_event.c - event timer support routines */ #include <stdio.h> @@ -30,18 +30,18 @@ int alarm_flag; /* * adjust and hourly counters */ -static U_LONG adjust_timer; -static U_LONG hourly_timer; +static u_long adjust_timer; +static u_long hourly_timer; /* * Imported from the leap module. The leap timer. */ -extern U_LONG leap_timer; +extern u_long leap_timer; /* * Statistics counter for the interested. */ -U_LONG alarm_overflow; +u_long alarm_overflow; #define HOUR (60*60) @@ -50,15 +50,15 @@ U_LONG alarm_overflow; * increments of 2**EVENT_TIMEOUT seconds. The timer queue is the * hash into which we sort timer entries. */ -U_LONG current_time; +u_long current_time; struct event timerqueue[TIMER_NSLOTS]; /* * Stats. Number of overflows and number of calls to transmit(). */ -U_LONG timer_timereset; -U_LONG timer_overflows; -U_LONG timer_xmtcalls; +u_long timer_timereset; +u_long timer_overflows; +u_long timer_xmtcalls; static RETSIGTYPE alarming P((int)); diff --git a/usr.sbin/xntpd/xntpd/ntp_unixclock.c b/usr.sbin/xntpd/xntpd/ntp_unixclock.c index 1950d8c06fb3..6b81429ca626 100644 --- a/usr.sbin/xntpd/xntpd/ntp_unixclock.c +++ b/usr.sbin/xntpd/xntpd/ntp_unixclock.c @@ -1,4 +1,4 @@ -/* ntp_unixclock.c,v 3.1 1993/07/06 01:11:30 jbj Exp +/* * ntp_unixclock.c - routines for reading and adjusting a 4BSD-style * system clock */ @@ -9,7 +9,7 @@ #include <sys/stat.h> #include <sys/time.h> -#if defined(SYS_HPUX) || defined(sgi) || defined(SYS_BSDI) +#if defined(SYS_HPUX) || defined(sgi) || defined(SYS_BSDI) || defined(SYS_44BSD) #include <sys/param.h> #include <utmp.h> #endif @@ -20,7 +20,7 @@ #include "ntp_stdlib.h" #if defined(HAVE_LIBKVM) -#ifdef SYS_BSDI +#if defined(SYS_BSDI) || defined(SYS_44BSD) #include <sys/proc.h> #endif /* SYS_BSDI */ #include <kvm.h> @@ -61,19 +61,19 @@ extern int debug; * We also remember the clock precision we computed from the kernel in * case someone asks us. */ -extern LONG adj_precision; /* adj precision in usec (tickadj) */ -extern LONG tvu_maxslew; /* maximum adjust doable in 1<<CLOCK_ADJ sec (usec) */ +extern long adj_precision; /* adj precision in usec (tickadj) */ +extern long tvu_maxslew; /* maximum adjust doable in 1<<CLOCK_ADJ sec (usec) */ -extern U_LONG tsf_maxslew; /* same as above, as LONG format */ +extern u_long tsf_maxslew; /* same as above, as long format */ extern l_fp sys_clock_offset; /* correction for current system time */ /* * Import sys_clock (it is updated in get_systime) */ -extern LONG sys_clock; +extern long sys_clock; -static void clock_parms P((U_LONG *, U_LONG *)); +static void clock_parms P((u_long *, u_long *)); /* * init_systime - initialize the system clock support code, return @@ -97,9 +97,9 @@ static void clock_parms P((U_LONG *, U_LONG *)); void init_systime() { - U_LONG tickadj; - U_LONG tick; - U_LONG hz; + u_long tickadj; + u_long tick; + u_long hz; /* * Obtain the values @@ -107,7 +107,7 @@ init_systime() clock_parms(&tickadj, &tick); #ifdef DEBUG if (debug) - printf("kernel vars: tickadj = %d, tick = %d\n", tickadj, tick); + printf("kernel vars: tickadj = %ld, tick = %ld\n", tickadj, tick); #endif /* @@ -162,14 +162,14 @@ init_systime() #ifdef DEBUG if (debug) printf( - "adj_precision = %d, tvu_maxslew = %d, tsf_maxslew = 0.%08x\n", + "adj_precision = %ld, tvu_maxslew = %ld, tsf_maxslew = 0.%08lx\n", adj_precision, tvu_maxslew, tsf_maxslew); #endif /* * Set the current offset to 0 */ - sys_clock_offset.l_ui = sys_clock_offset.l_uf = 0; + L_CLR(&sys_clock_offset); } #ifdef HAVE_LIBKVM @@ -180,8 +180,8 @@ init_systime() */ static void clock_parms(tickadj, tick) - U_LONG *tickadj; - U_LONG *tick; + u_long *tickadj; + u_long *tick; { static struct nlist nl[] = { #define N_TICKADJ 0 @@ -261,8 +261,8 @@ clock_parms(tickadj, tick) */ static void clock_parms(tickadj, tick) - U_LONG *tickadj; - U_LONG *tick; + u_long *tickadj; + u_long *tick; { *tick = 10000; /* microseconds */ *tickadj = 80; /* microseconds */ @@ -275,8 +275,13 @@ clock_parms(tickadj, tick) #endif #ifdef SYS_HPUX +#if defined(hp9000s300) #define K_TICKADJ_NAME "_tickadj" #define K_TICK_NAME "_old_tick" +#else +#define K_TICKADJ_NAME "tickadj" +#define K_TICK_NAME "old_tick" +#endif #endif /* The defaults if not defined previously */ @@ -289,8 +294,8 @@ clock_parms(tickadj, tick) static void clock_parms(tickadj, tick) - U_LONG *tickadj; - U_LONG *tick; + u_long *tickadj; + u_long *tick; { register int i; int kmem; @@ -377,8 +382,8 @@ clock_parms(tickadj, tick) } close(kmem); - *tickadj = (U_LONG)vars[K_TICKADJ]; - *tick = (U_LONG)vars[K_TICK]; + *tickadj = (u_long)vars[K_TICKADJ]; + *tick = (u_long)vars[K_TICK]; #undef K_TICKADJ #undef K_TICK @@ -398,8 +403,8 @@ clock_parms(tickadj, tick) */ static void clock_parms(tickadj, tick) - U_LONG *tickadj; - U_LONG *tick; + u_long *tickadj; + u_long *tick; { int hz; @@ -427,8 +432,8 @@ clock_parms(tickadj, tick) */ static void clock_parms(tickadj, tick) - U_LONG *tickadj; - U_LONG *tick; + u_long *tickadj; + u_long *tick; { *tick = 10000; *tickadj = 150; @@ -448,8 +453,8 @@ clock_parms(tickadj, tick) */ static void clock_parms(tickadj, tick) - U_LONG *tickadj; - U_LONG *tick; + u_long *tickadj; + u_long *tick; { #ifdef RS6000 *tickadj = 1000; @@ -481,8 +486,8 @@ clock_parms(tickadj, tick) */ static void clock_parms(tickadj, tick) - U_LONG *tickadj; - U_LONG *tick; + u_long *tickadj; + u_long *tick; { register int i; int kmem; @@ -560,8 +565,8 @@ clock_parms(tickadj, tick) } close(kmem); - *tickadj = (U_LONG)vars[K_TICKADJ]; - *tick = (U_LONG)(1000000/sysconf(_SC_CLK_TCK)); + *tickadj = (u_long)vars[K_TICKADJ]; + *tick = (u_long)(1000000/sysconf(_SC_CLK_TCK)); #undef K_TICKADJ #undef N_NAME @@ -569,18 +574,18 @@ clock_parms(tickadj, tick) #endif /* SOLARIS */ #ifdef SYS_LINUX -#include <sys/timex.h> +#include "sys/timex.h" static void clock_parms(tickadj, tick) - U_LONG *tickadj; - U_LONG *tick; + u_long *tickadj; + u_long *tick; { struct timex txc; txc.mode = 0; __adjtimex(&txc); - *tickadj = (U_LONG)1; /* our adjtime is accurate */ - *tick = (U_LONG)txc.tick; + *tickadj = (u_long)1; /* our adjtime is accurate */ + *tick = (u_long)txc.tick; } #endif /* SYS_LINUX */ diff --git a/usr.sbin/xntpd/xntpd/ntp_util.c b/usr.sbin/xntpd/xntpd/ntp_util.c index 4cbb7ac27f53..540be62a675a 100644 --- a/usr.sbin/xntpd/xntpd/ntp_util.c +++ b/usr.sbin/xntpd/xntpd/ntp_util.c @@ -1,4 +1,4 @@ -/* ntp_util.c,v 3.1 1993/07/06 01:11:31 jbj Exp +/* * ntp_util.c - stuff I didn't have any other place for */ #include <stdio.h> @@ -59,6 +59,12 @@ static FILEGEN clockstats; */ extern int errno; +/* + * This controls whether stats are written to the fileset. Provided + * so that xntpdc can turn off stats when the file system fills up. + */ +int stats_control; + #ifdef DEBUG extern int debug; #endif @@ -116,12 +122,11 @@ init_util() void hourly_stats() { - int fd; - char *val; - int vallen; + FILE *fp; extern l_fp last_offset; extern s_fp drift_comp; - extern int time_constant; + extern u_char sys_poll; + extern int pll_status; #ifdef DOSYNCTODR struct timeval tv; @@ -165,32 +170,21 @@ hourly_stats() skip: #endif - syslog(LOG_INFO, "offset %s freq %s comp %d", - lfptoa(&last_offset, 6), fptoa(drift_comp, 5), time_constant); + syslog(LOG_INFO, "offset %s freq %s poll %d", + lfptoa(&last_offset, 6), fptoa(drift_comp, 3), + sys_poll); if (stats_drift_file != 0) { - fd = open(stats_temp_file, O_WRONLY|O_TRUNC|O_CREAT, 0644); - if (fd == -1) { - syslog(LOG_ERR, "can't open %s: %m", stats_temp_file); + if ((fp = fopen(stats_temp_file, "w")) == NULL) { + syslog(LOG_ERR, "can't open %s: %m", + stats_temp_file); return; } - - val = fptoa(drift_comp, 5); - vallen = strlen(val); - /* - * Hack here. Turn the trailing \0 into a \n and write it. - */ - val[vallen] = '\n'; - if (write(fd, val, vallen+1) == -1) { - syslog(LOG_ERR, "write to %s failed: %m", - stats_temp_file); - (void) close(fd); - (void) unlink(stats_temp_file); - } else { - (void) close(fd); - /* atomic */ - (void) rename(stats_temp_file, stats_drift_file); - } + fprintf(fp, "%s %x\n", fptoa(drift_comp, 3), + pll_status); + (void)fclose(fp); + /* atomic */ + (void) rename(stats_temp_file, stats_drift_file); } } @@ -203,11 +197,11 @@ stats_config(item, value) int item; char *value; /* only one type so far */ { - register char *cp; FILE *fp; - int len; char buf[128]; l_fp old_drift; + int temp = 0; + int len; switch(item) { case STATS_FREQ_FILE: @@ -222,19 +216,17 @@ stats_config(item, value) break; stats_drift_file = emalloc((u_int)(len + 1)); - stats_temp_file = emalloc((u_int)(len + sizeof(".TEMP"))); + stats_temp_file = emalloc((u_int)(len + + sizeof(".TEMP"))); memmove(stats_drift_file, value, len+1); memmove(stats_temp_file, value, len); - memmove(stats_temp_file + len, ".TEMP", sizeof(".TEMP")); + memmove(stats_temp_file + len, ".TEMP", + sizeof(".TEMP")); L_CLR(&old_drift); -#ifdef DEBUG - if (debug > 1) { - printf("stats drift file %s\n", stats_drift_file); - printf("stats temp file %s\n", stats_temp_file); - } -#endif - + /* + * Open drift file and read frequency and mode. + */ if ((fp = fopen(stats_drift_file, "r")) == NULL) { if (errno != ENOENT) syslog(LOG_ERR, "can't open %s: %m", @@ -243,71 +235,48 @@ stats_config(item, value) break; } - if (fgets(buf, sizeof buf, fp) == NULL) { + if (fscanf(fp, "%s %x", buf, &temp) == 0) { syslog(LOG_ERR, "can't read %s: %m", stats_drift_file); (void) fclose(fp); loop_config(LOOP_DRIFTCOMP, &old_drift, 0); break; } - (void) fclose(fp); - - /* - * We allow leading spaces, then the number. Terminate - * at any trailing space or string terminator. - */ - cp = buf; - while (isspace(*cp)) - cp++; - while (*cp != '\0' && !isspace(*cp)) - cp++; - *cp = '\0'; - if (!atolfp(buf, &old_drift)) { syslog(LOG_ERR, "drift value %s invalid", buf); break; } - - /* - * Finally! Give value to the loop filter. - */ -#ifdef DEBUG - if (debug > 1) { - printf("loop_config finds old drift of %s\n", - lfptoa(&old_drift, 9)); - } -#endif - loop_config(LOOP_DRIFTCOMP, &old_drift, 0); + loop_config(LOOP_DRIFTCOMP, &old_drift, temp); break; case STATS_STATSDIR: if (strlen(value) >= sizeof(statsdir)) { syslog(LOG_ERR, - "value for statsdir too LONG (>%d, sigh)", + "value for statsdir too long (>%d, sigh)", sizeof(statsdir)-1); } else { l_fp now; - strcpy(statsdir,value); gettstamp(&now); + strcpy(statsdir,value); if(peerstats.prefix == &statsdir[0] && peerstats.fp != NULL) { fclose(peerstats.fp); peerstats.fp = NULL; - filegen_setup(&peerstats,now.l_ui); + 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); + 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); + filegen_setup(&clockstats, now.l_ui); } } break; @@ -348,14 +317,16 @@ record_peer_stats(addr, status, offset, delay, dispersion) u_fp dispersion; { struct timeval tv; - U_LONG day, sec, msec; + u_long day, sec, msec; + if (!stats_control) + return; GETTIMEOFDAY(&tv, (struct timezone *)NULL); - day = (U_LONG)tv.tv_sec / 86400 + MJD_1970; - sec = (U_LONG)tv.tv_sec % 86400; - msec = (U_LONG)tv.tv_usec / 1000; + 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)); + filegen_setup(&peerstats, (u_long)(tv.tv_sec + JAN_1970)); if (peerstats.fp != NULL) { fprintf(peerstats.fp, "%lu %lu.%03lu %s %x %s %s %s\n", day, sec, msec, ntoa(addr), status, lfptoa(offset, 6), @@ -374,24 +345,26 @@ record_peer_stats(addr, status, offset, delay, dispersion) * time constant (log base 2) */ void -record_loop_stats(offset, drift_comp, time_constant) +record_loop_stats(offset, freq, poll) l_fp *offset; - s_fp *drift_comp; - int time_constant; + s_fp freq; + u_char poll; { struct timeval tv; - U_LONG day, sec, msec; + u_long day, sec, msec; + if (!stats_control) + return; GETTIMEOFDAY(&tv, (struct timezone *)NULL); - day = (U_LONG)tv.tv_sec / 86400 + MJD_1970; - sec = (U_LONG)tv.tv_sec % 86400; - msec = (U_LONG)tv.tv_usec / 1000; + 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)); + filegen_setup(&loopstats, (u_long)(tv.tv_sec + JAN_1970)); if (loopstats.fp != NULL) { fprintf(loopstats.fp, "%lu %lu.%03lu %s %s %d\n", day, sec, msec, lfptoa(offset, 6), - fptoa(*drift_comp, 4), time_constant); + fptoa(freq, 4), poll); fflush(loopstats.fp); } } @@ -411,14 +384,16 @@ record_clock_stats(addr, text) char *text; { struct timeval tv; - U_LONG day, sec, msec; + u_long day, sec, msec; + if (!stats_control) + return; GETTIMEOFDAY(&tv, (struct timezone *)NULL); - day = (U_LONG)tv.tv_sec / 86400 + MJD_1970; - sec = (U_LONG)tv.tv_sec % 86400; - msec = (U_LONG)tv.tv_usec / 1000; + 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)); + 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); diff --git a/usr.sbin/xntpd/xntpd/ntpd.c b/usr.sbin/xntpd/xntpd/ntpd.c index 9ed43c5c4a81..43ae699c97a2 100644 --- a/usr.sbin/xntpd/xntpd/ntpd.c +++ b/usr.sbin/xntpd/xntpd/ntpd.c @@ -1,4 +1,4 @@ -/* ntpd.c,v 3.1 1993/07/06 01:11:32 jbj Exp +/* * ntpd.c - main program for the fixed point NTP daemon */ #include <stdio.h> @@ -75,7 +75,7 @@ extern char *Version; */ extern int alarm_flag; -#if !defined(SYS_386BSD) && !defined(SYS_BSDI) +#if !defined(SYS_386BSD) && !defined(SYS_BSDI) && !defined(SYS_44BSD) /* * We put this here, since the argument profile is syscall-specific */ @@ -130,7 +130,7 @@ main(argc, argv) exit(0); { - unsigned long s; + u_long s; int max_fd; #if defined(NTP_POSIX_SOURCE) && !defined(SYS_386BSD) max_fd = sysconf(_SC_OPEN_MAX); @@ -169,7 +169,7 @@ main(argc, argv) fid = open("/dev/tty", 2); if (fid >= 0) { - (void) ioctl(fid, (U_LONG) TIOCNOTTY, + (void) ioctl(fid, (u_long) TIOCNOTTY, (char *) 0); (void) close(fid); } @@ -279,6 +279,13 @@ main(argc, argv) #endif /* DEBUG */ /* + * Set up signals we should never pay attention to. + */ +#ifdef SIGPIPE + (void) signal_no_reset(SIGPIPE, SIG_IGN); +#endif /* SIGPIPE */ + + /* * Call the init_ routines to initialize the data structures. * Note that init_systime() may run a protocol to get a crude * estimate of the time as an NTP client when running on the @@ -307,6 +314,9 @@ main(argc, argv) 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 @@ -361,10 +371,8 @@ main(argc, argv) get_systime(&ts); (void)input_handler(&ts); - } - else if (nfound == -1 && errno != EINTR) { + } else if (nfound == -1 && errno != EINTR) syslog(LOG_ERR, "select() error: %m"); - } #else wait_for_signal(); #endif diff --git a/usr.sbin/xntpd/xntpd/refclock_acts.c b/usr.sbin/xntpd/xntpd/refclock_acts.c new file mode 100644 index 000000000000..af3cabf11c82 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_acts.c @@ -0,0 +1,895 @@ +/* + * refclock_acts - clock driver for the NIST Automated Computer Time + * Service aka Amalgamated Containerized Trash Service (ACTS) + */ +#if defined(REFCLOCK) && defined(ACTS) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" + +/* + * 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 + * xntp3 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 xntpdc, 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 + * xntpdc. + * + * 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 + */ + +/* + * Interface definitions + */ +#define DEVICE "/dev/acts%d" /* device name and unit */ +#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ +#define REFID "ACTS" /* reference ID */ +#define DESCRIPTION "NIST Automated Computer Time Service" /* WRU */ + +#define MODE_AUTO 0 /* automatic mode */ +#define MODE_BACKUP 1 /* backup mode */ +#define MODE_MANUAL 2 /* manual mode */ + +#define NSAMPLES 3 /* stages of median filter */ +#define MSGCNT 10 /* we need this many ACTS messages */ +#define SMAX 80 /* max token string length */ +#define LENCODE 50 /* length of valid timecode string */ +#define ACTS_MINPOLL 10 /* log2 min poll interval (1024 s) */ +#define ACTS_MAXPOLL 14 /* 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) */ + +/* + * Imported from ntp_timer module + */ +extern u_long current_time; /* current time (s) */ +extern u_long last_time; /* last clock update time (s) */ +extern struct event timerqueue[]; /* inner space */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * Imported from ntp_config module + */ +extern char sys_phone[][MAXDIAL]; /* modem dial strings */ + +/* + * Imported from ntp_proto module + */ +extern struct peer *sys_peer; /* who is running the show */ +extern u_char sys_poll; /* log2 of system poll interval */ +extern struct peer *sys_peer; /* system peer structure pointer */ + +/* + * Tables to compute the ddd of year form icky dd/mm timecode. Viva la + * leap. + */ +static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* + * Unit control structure + */ +struct actsunit { + struct event timer; /* timeout timer */ + 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 *, char *)); + +/* + * Transfer vector + */ +struct refclock refclock_acts = { + 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(unit, peer) + 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, TIOCMBIC, (char *)&dtr) < 0) { + syslog(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; + + /* + * 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 + */ + up->timer.peer = (struct peer *)peer; + up->timer.event_handler = acts_timeout; + up->timer.event_time = current_time + WAIT; + TIMER_INSERT(timerqueue, &up->timer); + return (1); +} + + +/* + * acts_shutdown - shut down the clock + */ +static void +acts_shutdown(unit, peer) + int unit; + struct peer *peer; +{ + register struct actsunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct actsunit *)pp->unitptr; + TIMER_DEQUEUE(&up->timer); + io_closeclock(&pp->io); + free(up); +} + + +/* + * acts_receive - receive data from the serial interface + */ +static void +acts_receive(rbufp) + struct recvbuf *rbufp; +{ + register struct actsunit *up; + struct refclockproc *pp; + struct peer *peer; + char str[SMAX]; + int i; + l_fp tstmp; + u_fp disp; + char hangup = '%'; /* ACTS hangup */ + int day; /* day of the month */ + int month; /* month of the year */ + u_long mjd; /* Modified Julian Day */ + u_int dst; /* daylight/standard time indicator */ + u_int leap; /* leap-second indicator */ + double dut1; /* DUT adjustment */ + double msADV; /* ACTS transmit advance (ms) */ + char utc[10]; /* this is NIST and you're not */ + char flag; /* calibration flag */ + + /* + * 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->lastcode, BMAX, + &pp->lastrec); + if (pp->lencode == 0) { + if (strcmp(pp->lastcode, "OK") == 0) + pp->lencode = 2; + return; + } +#ifdef DEBUG + if (debug) + printf("acts: timecode %d %s\n", pp->lencode, + pp->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. + */ + (void)strncpy(str, strtok(pp->lastcode, " "), SMAX); + if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") == + 0 || strcmp(str, "NO") == 0) { + TIMER_DEQUEUE(&up->timer); + syslog(LOG_NOTICE, + "clock %s ACTS modem status %s", + ntoa(&peer->srcadr), pp->lastcode); + acts_disc(peer); + } else if (strcmp(str, "CONNECT") == 0) { + TIMER_DEQUEUE(&up->timer); + up->timer.event_time = current_time + CONNECT; + TIMER_INSERT(timerqueue, &up->timer); + 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. + */ + TIMER_DEQUEUE(&up->timer); + up->timer.event_time = current_time + TIMECODE; + TIMER_INSERT(timerqueue, &up->timer); + 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; + + /* + * 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->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; + } + + /* + * 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. + */ + 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; + if (leap == 1) + pp->leap = LEAP_ADDSECOND; + else if (pp->leap == 2) + pp->leap = LEAP_DELSECOND; + else + pp->leap = 0; + pp->lasttime = current_time; + + /* + * 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 == '#') { + L_CLR(&tstmp); + TVUTOTSF((long)((msADV - up->msADV) * 1000.), + tstmp.l_uf); + L_ADD(&pp->offset, &tstmp); + } else { + up->msADV = msADV; + if (!refclock_process(pp, up->msgcnt, up->msgcnt - + up->msgcnt / 3)) { + 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. + */ + disp = LFPTOFP(&pp->fudgetime2); + record_clock_stats(&peer->srcadr, pp->lastcode); + refclock_receive(peer, &pp->offset, 0, pp->dispersion + + (u_fp)disp, &pp->lastrec, &pp->lastrec, pp->leap); + pp->sloppyclockflag &= ~CLK_FLAG1; + up->pollcnt = 0; + TIMER_DEQUEUE(&up->timer); + (void)write(pp->io.fd, &hangup, 1); + up->state = 0; + acts_disc(peer); +} + + +/* + * acts_poll - called by the transmit routine + */ +static void +acts_poll(unit, peer) + 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 + * xntpdc. 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 - called by the timer interrupt + */ +static void +acts_timeout(peer) + 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 xntpdc 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; + syslog(LOG_NOTICE, + "clock %s ACTS backup started ", + ntoa(&peer->srcadr)); + } + } else if (up->run && sys_peer->refclktype != + REFCLK_NIST_ACTS) { + peer->hpoll = peer->minpoll; + up->run = 0; + syslog(LOG_NOTICE, + "clock %s ACTS backup stopped", + ntoa(&peer->srcadr)); + } + break; + + default: + syslog(LOG_NOTICE, + "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 xntpdc, the ACTS calling program is + * started; if reset, the phones stop ringing. + */ + if (!(pp->sloppyclockflag & CLK_FLAG1)) { + up->pollcnt = 0; + up->timer.event_time = current_time + IDLE; + TIMER_INSERT(timerqueue, &up->timer); + 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); + syslog(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; + up->timer.event_time = current_time + IDLE; + TIMER_INSERT(timerqueue, &up->timer); + 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->lastcode, "OK") != 0) + syslog(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]); + syslog(LOG_NOTICE, "clock %s ACTS calling %s\n", + ntoa(&peer->srcadr), sys_phone[up->pollcnt]); + up->state = 1; + up->pollcnt++; + pp->polls++; + up->timer.event_time = current_time + ANSWER; + TIMER_INSERT(timerqueue, &up->timer); +} + + +/* + * acts_disc - disconnect the call and wait for the ruckus to cool + */ +static void +acts_disc(peer) + 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) { + up->state = 0; + syslog(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->timer.event_time = current_time + WAIT; + TIMER_INSERT(timerqueue, &up->timer); +} + + +/* + * acts_write - write a message to the serial port + */ +static int +acts_write(peer, str) + struct peer *peer; + 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, len) == len; + code |= write(pp->io.fd, &cr, 1) == 1; + if (!code) + refclock_report(peer, CEVNT_FAULT); + return (code); +} + +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_as2201.c b/usr.sbin/xntpd/xntpd/refclock_as2201.c index 97b5837c2f3b..6c60fd0b9447 100644 --- a/usr.sbin/xntpd/xntpd/refclock_as2201.c +++ b/usr.sbin/xntpd/xntpd/refclock_as2201.c @@ -1,7 +1,8 @@ /* - * refclock_gps - clock driver for the Austron 2201A GPS Timing Receiver + * refclock_as2201 - clock driver for the Austron 2201A GPS + * Timing Receiver */ -#if defined(REFCLOCK) && (defined(AS2201) || defined(AS2201CLK) || defined(AS2201PPS)) +#if defined(REFCLOCK) && defined(AS2201) #include <stdio.h> #include <ctype.h> @@ -11,30 +12,11 @@ #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(AS2201CLK) -#include <sys/clkdefs.h> -#endif /* AS2201CLK */ -#endif /* STREAM */ - -#if defined (AS2201PPS) +#ifdef PPS #include <sys/ppsclock.h> -#endif /* AS2201PPS */ - -#include "ntp_stdlib.h" +#endif /* PPS */ /* * This driver supports the Austron 2200A/2201A GPS Receiver with @@ -49,7 +31,9 @@ * 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. + * 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> @@ -58,105 +42,68 @@ * timescale using the "ts utc" command. * * There are two modes of operation for this driver. The first with - * undefined AS2201PPS 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 with AS2201PPS defined can be used for SunOS kernels - * that have been modified with the ppsclock streams module included in - * this distribution. In this mode a precise timestamp is available - * using a gadget box and 1-pps signal from the receiver; however, the - * sample rate is limited to the polling rate, normally about one poll - * every 16 seconds. 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 additional receivers. For the utmost accuracy, the - * sample rate can be increased to one per second using the PPSCD - * define. This improves the accuracy to the order of a few - * microseconds. - */ - -/* - * Definitions + * 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 + * additional receivers. */ -#define MAXUNITS 4 /* max number of GPS units */ -#define GPS232 "/dev/gps%d" /* name of radio device */ -#define SPEED232 B9600 /* uart speed (9600 baud) */ /* - * Radio interface parameters + * GPS Definitions */ -#define GPSPRECISION (-15) /* precision assumed (about 30 us) */ -#define GPSREFID "GPS" /* reference id */ -#define GPSDESCRIPTION "Austron 2201A GPS Receiver" /* who we are */ -#define GPSHSREFID 0x7f7f040a /* 127.127.4.10 refid hi strata */ -#define GMT 0 /* hour offset from Greenwich */ -#define NCODES 3 /* stages of median filter */ -#define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm datecode length */ -#define BMAX 100 /* timecode buffer length */ #define SMAX 200 /* statistics buffer length */ -#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */ +#define DEVICE "/dev/gps%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 "Austron 2201A GPS Receiver" /* WRU */ -/* - * Hack to avoid excercising the multiplier. I have no pride. - */ -#define MULBY10(x) (((x)<<3) + ((x)<<1)) +#define NSAMPLES 3 /* stages of median filter */ +#define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm timecode lngth */ /* * Imported from ntp_timer module */ -extern U_LONG current_time; /* current time (s) */ +extern u_long current_time; /* current time (s) */ /* - * Imported from ntp_loopfilter module + * Imported from ntpd module */ -extern int fdpps; /* pps file descriptor */ +extern int debug; /* global debug flag */ +#ifdef PPS /* - * Imported from ntpd module + * Imported from loop_filter module */ -extern int debug; /* global debug flag */ +extern int fdpps; /* ppsclock file descriptor */ +#endif /* PPS */ /* - * GPS unit control structure. + * AS2201 unit control structure. */ -struct gpsunit { - struct peer *peer; /* associated peer structure */ - struct refclockio io; /* given to the I/O handler */ - l_fp lastrec; /* last data receive time */ - l_fp lastref; /* last timecode time */ - l_fp offset[NCODES]; /* recent sample offsets */ - char lastcode[BMAX]; /* last timecode received */ - char *lastptr; /* statistics buffer pointer */ - char stats[SMAX]; /* statistics buffer */ - u_char lencode; /* length of last received ASCII string */ - U_LONG lasttime; /* last time clock heard from */ -#ifdef AS2201PPS - U_LONG lastev; /* last ppsclock second */ -#endif /* AS2201PPS */ - 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 year; /* year of eternity */ - u_short day; /* day of year */ - u_char hour; /* hour of day */ - u_char minute; /* minute of hour */ - u_char second; /* seconds of minute */ - u_short msec; /* milliseconds of second */ - u_char leap; /* leap indicators */ - U_LONG yearstart; /* start of current year */ - int linect; /* count of lines remaining */ - int index; /* current statistics command */ - /* - * 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 */ +struct as2201unit { + int pollcnt; /* poll message counter */ + + char *lastptr; /* statistics buffer pointer */ + char stats[SMAX]; /* statistics buffer */ + +#ifdef PPS + u_long lastev; /* last ppsclock second */ +#endif /* PPS */ + + int linect; /* count of lines remaining */ + int index; /* current statistics command */ }; /* @@ -205,385 +152,129 @@ static char stat_command[][30] = { }; /* - * Data space for the unit structures. Note that we allocate these on - * the fly, but never give them back. - */ -static struct gpsunit *gpsunits[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 as2201_init P(()); -static int as2201_start P((u_int, struct peer *)); -static void as2201_shutdown P((int)); -static void as2201_report_event P((struct gpsunit *, int)); +static int as2201_start P((int, struct peer *)); +static void as2201_shutdown P((int, struct peer *)); static void as2201_receive P((struct recvbuf *)); -static char as2201_process P((struct gpsunit *, l_fp *, u_fp *)); -static void as2201_poll P((int unit, struct peer *)); -static void as2201_control P((u_int, struct refclockstat *, struct refclockstat *)); -static void as2201_buginfo P((int, struct refclockbug *)); +static void as2201_poll P((int, struct peer *)); /* * Transfer vector */ -struct refclock refclock_as2201 = { - as2201_start, as2201_shutdown, as2201_poll, - as2201_control, as2201_init, as2201_buginfo, NOFLAGS +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_init - initialize internal gps driver data - */ -static void -as2201_init() -{ - register int i; - /* - * Just zero the data arrays - */ - memset((char *)gpsunits, 0, sizeof gpsunits); - memset((char *)unitinuse, 0, 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; - } -} - /* - * as2201_start - open the GPS devices and initialize data for processing + * as2201_start - open the devices and initialize data for processing */ static int as2201_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { - register struct gpsunit *gps; - register int i; - int fd232; - char as2201dev[20]; -#ifdef AS2201PPS - struct ppsclockev ev; -#endif /* AS2201PPS */ + register struct as2201unit *up; + struct refclockproc *pp; + int fd; + char gpsdev[20]; /* - * Check configuration info + * Open serial port. Use CLK line discipline, if available. */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "gps_start: unit %d invalid", unit); - return (0); - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "gps_start: unit %d in use", unit); + (void)sprintf(gpsdev, DEVICE, unit); + if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_CLK))) return (0); - } /* - * Open serial port + * Allocate and initialize unit structure */ - (void) sprintf(as2201dev, GPS232, unit); - fd232 = open(as2201dev, O_RDWR, 0777); - if (fd232 == -1) { - syslog(LOG_ERR, "gps_start: open of %s: %m", as2201dev); + if (!(up = (struct as2201unit *) + emalloc(sizeof(struct as2201unit)))) { + (void) close(fd); return (0); } - -#if defined(HAVE_SYSV_TTYS) - /* - * System V serial line parameters (termio interface) - * - */ - { struct termio ttyb; - if (ioctl(fd232, TCGETA, &ttyb) < 0) { - syslog(LOG_ERR, - "as2201_start: ioctl(%s, TCGETA): %m", as2201dev); - 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) { - syslog(LOG_ERR, - "as2201_start: ioctl(%s, TCSETA): %m", as2201dev); - goto screwed; - } - } -#endif /* HAVE_SYSV_TTYS */ -#if defined(HAVE_TERMIOS) - /* - * POSIX serial line parameters (termios interface) - * - * The AS2201CLK option provides timestamping at the driver level. - * It requires the tty_clk streams module. - * - * The AS2201PPS 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 SunOS 4.1.1 or - * later. - */ - { struct termios ttyb, *ttyp; - - ttyp = &ttyb; - if (tcgetattr(fd232, ttyp) < 0) { - syslog(LOG_ERR, - "as2201_start: tcgetattr(%s): %m", as2201dev); - 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) { - syslog(LOG_ERR, - "as2201_start: tcsetattr(%s): %m", as2201dev); - goto screwed; - } - if (tcflush(fd232, TCIOFLUSH) < 0) { - syslog(LOG_ERR, - "as2201_start: tcflush(%s): %m", as2201dev); - goto screwed; - } - } -#endif /* HAVE_TERMIOS */ -#ifdef STREAM -#if defined(AS2201CLK) - if (ioctl(fd232, I_PUSH, "clk") < 0) - syslog(LOG_ERR, - "as2201_start: ioctl(%s, I_PUSH, clk): %m", as2201dev); - if (ioctl(fd232, CLK_SETSTR, "\n") < 0) - syslog(LOG_ERR, - "as2201_start: ioctl(%s, CLK_SETSTR): %m", as2201dev); -#endif /* AS2201CLK */ -#if defined(AS2201PPS) - if (ioctl(fd232, I_PUSH, "ppsclock") < 0) - syslog(LOG_ERR, - "as2201_start: ioctl(%s, I_PUSH, ppsclock): %m", as2201dev); - else - fdpps = fd232; -#endif /* AS2201PPS */ -#endif /* STREAM */ -#if defined(HAVE_BSD_TTYS) - /* - * 4.3bsd serial line parameters (sgttyb interface) - * - * The AS2201CLK option provides timestamping at the driver level. - * It requires the tty_clk line discipline and 4.3bsd or later. - */ - { struct sgttyb ttyb; -#if defined(AS2201CLK) - int ldisc = CLKLDISC; -#endif /* AS2201CLK */ - - if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { - syslog(LOG_ERR, - "as2201_start: ioctl(%s, TIOCGETP): %m", as2201dev); - goto screwed; - } - ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; -#if defined(AS2201CLK) - 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 /* AS2201CLK */ - if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { - syslog(LOG_ERR, - "as2201_start: ioctl(%s, TIOCSETP): %m", as2201dev); - goto screwed; - } -#if defined(AS2201CLK) - if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { - syslog(LOG_ERR, - "as2201_start: ioctl(%s, TIOCSETD): %m",as2201dev); - goto screwed; - } -#endif /* AS2201CLK */ - } -#endif /* HAVE_BSD_TTYS */ - - /* - * Allocate unit structure - */ - if (gpsunits[unit] != 0) { - gps = gpsunits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < MAXUNITS; i++) { - if (!unitinuse[i] && gpsunits[i] != 0) - break; - } - if (i < MAXUNITS) { - /* - * Reclaim this one - */ - gps = gpsunits[i]; - gpsunits[i] = 0; - } else { - gps = (struct gpsunit *) - emalloc(sizeof(struct gpsunit)); - } + 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); } - memset((char *)gps, 0, sizeof(struct gpsunit)); - gpsunits[unit] = gps; + pp->unitptr = (caddr_t)up; /* - * Set up the structures + * Initialize miscellaneous variables */ - gps->peer = peer; - gps->unit = (u_char)unit; - gps->timestarted = current_time; - gps->lastptr = gps->stats; - gps->index = 0; - - gps->io.clock_recv = as2201_receive; - gps->io.srcclock = (caddr_t)gps; - gps->io.datalen = 0; - gps->io.fd = fd232; -#ifdef AS2201PPS - if (ioctl(fd232, CIOGETEV, (caddr_t)&ev) < 0) { - syslog(LOG_ERR, - "gps_start: ioctl(%s, CIOGETEV): %m", as2201dev); - goto screwed; - } else - gps->lastev = ev.tv.tv_sec; -#endif /* AS2201PPS */ - if (!io_addclock(&gps->io)) { - goto screwed; - } + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; - /* - * All done. Initialize a few random peer variables, then - * return success. Note that root delay and root dispersion are - * always zero for this clock. - */ - peer->precision = GPSPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = 0; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, GPSREFID, 4); - else - peer->refid = htonl(GPSHSREFID); - unitinuse[unit] = 1; + up->lastptr = up->stats; + up->index = 0; return (1); - - /* - * Something broke; abandon ship. - */ -screwed: - (void) close(fd232); - return (0); -} - -/* - * as2201_shutdown - shut down a GPS clock - */ -static void -as2201_shutdown(unit) - int unit; -{ - register struct gpsunit *gps; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "gps_shutdown: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "gps_shutdown: unit %d not in use", unit); - return; - } - - /* - * Tell the I/O module to turn us off. We're history. - */ - gps = gpsunits[unit]; - io_closeclock(&gps->io); - unitinuse[unit] = 0; } /* - * as2201_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. + * as2201_shutdown - shut down the clock */ static void -as2201_report_event(gps, code) - struct gpsunit *gps; - int code; -{ +as2201_shutdown(unit, peer) + int unit; struct peer *peer; +{ + register struct as2201unit *up; + struct refclockproc *pp; - peer = gps->peer; - if (gps->status != (u_char)code) { - gps->status = (u_char)code; - if (code != CEVNT_NOMINAL) - gps->lastevent = (u_char)code; - syslog(LOG_INFO, - "clock %s event %x\n", ntoa(&peer->srcadr), code); - } + pp = peer->procptr; + up = (struct as2201unit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); } /* - * as2201_receive - receive data from the serial interface + * as2201__receive - receive data from the serial interface */ static void as2201_receive(rbufp) struct recvbuf *rbufp; { - register int i; - register struct gpsunit *gps; - -#if defined(AS2201PPS) - struct ppsclockev ev; + register struct as2201unit *up; + struct refclockproc *pp; + struct peer *peer; l_fp trtmp; -#endif /* AS2201PPS */ - register u_char *dpt; - register char *cp, *dp; - int dpend; - l_fp tstmp; - u_fp dispersion; +#ifdef PPS + long ltemp; + struct ppsclockev ev; +#endif /* PPS */ /* - * Get the clock this applies to and pointers to the data. - * Edit the timecode to remove control chars and trashbits. + * Initialize pointers and read the timecode and timestamp. */ - gps = (struct gpsunit *)rbufp->recv_srcclock; - dpt = (u_char *)&rbufp->recv_space; - dpend = rbufp->recv_length; - if (dpend > BMAX - 1) - dpend = BMAX - 1; - cp = dp = gps->lastcode; - for (i = 0; i < dpend; i++) - if ((*dp = 0x7f & *dpt++) >= ' ') dp++; - *dp = '\0'; - gps->lencode = dp - cp; + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct as2201unit *)pp->unitptr; + pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, &trtmp); #ifdef DEBUG if (debug) printf("gps: timecode %d %d %s\n", - gps->linect, gps->lencode, gps->lastcode); + up->linect, pp->lencode, pp->lastcode); #endif - if (gps->lencode == 0) + if (pp->lencode == 0) return; /* @@ -597,252 +288,136 @@ as2201_receive(rbufp) * the timecode; in the later case, save the number of lines and * quietly return. */ - if (gps->linect > 0) { - gps->linect--; - if ((int)(gps->lastptr - gps->stats + gps->lencode) > SMAX - 2) + if (up->linect > 0) { + up->linect--; + if (up->lastptr - up->stats + pp->lencode > SMAX - 2) return; - *gps->lastptr++ = ' '; - (void)strcpy(gps->lastptr, gps->lastcode); - gps->lastptr += gps->lencode; + *up->lastptr++ = ' '; + (void)strcpy(up->lastptr, pp->lastcode); + up->lastptr += pp->lencode; return; } else { - if (gps->lencode == 1) { - gps->linect = atoi(gps->lastcode); + if (pp->lencode == 1) { + up->linect = atoi(pp->lastcode); return; } else { - record_clock_stats(&(gps->peer->srcadr), gps->stats); + up->pollcnt = 2; + record_clock_stats(&peer->srcadr, up->stats); #ifdef DEBUG if (debug) - printf("gps: stat %s\n", gps->stats); + printf("gps: stat %s\n", up->stats); #endif } } - gps->lastptr = gps->stats; - *gps->lastptr = '\0'; - - /* - * We check the timecode format and decode its contents. The - * timecode has format yy:ddd:hh:mm:ss.mmm). If it has invalid - * length or is not in proper format, the driver declares bad - * format and exits. If the converted decimal values are out of - * range, the driver declares bad data and exits. - */ - if (gps->lencode != LENTOC || !isdigit(cp[0]) || - !isdigit(cp[1]) || !isdigit(cp[3]) || !isdigit(cp[4]) || - !isdigit(cp[5]) || !isdigit(cp[7]) || !isdigit(cp[8]) || - !isdigit(cp[10]) || !isdigit(cp[11]) || !isdigit(cp[13]) || - !isdigit(cp[14]) || !isdigit(cp[16]) || !isdigit(cp[17]) || - !isdigit(cp[18])) { - gps->badformat++; - as2201_report_event(gps, CEVNT_BADREPLY); - return; - } + up->lastptr = up->stats; + *up->lastptr = '\0'; /* - * Convert date and check values. + * 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. */ - gps->day = cp[3] - '0'; - gps->day = MULBY10(gps->day) + cp[4] - '0'; - gps->day = MULBY10(gps->day) + cp[5] - '0'; - if (gps->day < 1 || gps->day > 366) { - gps->baddata++; - as2201_report_event(gps, CEVNT_BADDATE); + if (pp->lencode < LENTOC) { + refclock_report(peer, CEVNT_BADREPLY); return; } /* - * Convert time and check values. + * Timecode format: "yy:ddd:hh:mm:ss.mmm" */ - gps->hour = MULBY10(cp[7] - '0') + cp[8] - '0'; - gps->minute = MULBY10(cp[10] - '0') + cp[11] - '0'; - gps->second = MULBY10(cp[13] - '0') + cp[14] - '0'; - gps->msec = MULBY10(MULBY10(cp[16] - '0') + cp[17] - '0') - + cp[18] - '0'; - if (gps->hour > 23 || gps->minute > 59 || gps->second > 59) { - gps->baddata++; - as2201_report_event(gps, CEVNT_BADTIME); + if (sscanf(pp->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 (cp[2] != ':') - gps->leap = LEAP_NOTINSYNC; - else - gps->lasttime = current_time; - - /* - * Now, compute the reference time value. Use the heavy - * machinery for the second, which presumably is the one which - * occured at the last pps pulse and which was captured by the - * loop_filter module. All we have to do here is present a - * reasonable facsimile of the time at that pulse so the clock- - * filter and selection machinery declares us truechimer. The - * precision offset within the second is really tuned by the - * loop_filter module. Note that this code does not yet know how - * to do the years and relies on the clock-calendar chip for - * sanity. - */ - if (!clocktime(gps->day, gps->hour, gps->minute, - gps->second, GMT, gps->lastrec.l_ui, - &gps->yearstart, &gps->lastref.l_ui)) { - gps->baddata++; - as2201_report_event(gps, CEVNT_BADTIME); - - printf("gps: bad data\n"); - - return; + if (pp->lastcode[2] != ':') { + pp->leap = LEAP_NOTINSYNC; + } else { + pp->leap = 0; + pp->lasttime = current_time; } - MSUTOTSF(gps->msec, gps->lastref.l_uf); -#if defined(AS2201PPS) +#ifdef PPS /* - * If the pps signal is available and the local time is within - * +-0.5 second of the timecode, use the pps offset instead. - * Note that we believe the ppsclock timestamp only if the ioctl - * works and the new timestamp is greater than the previous one. + * If CLK_FLAG3 is set and the local time is within +-0.5 second + * of the timecode, use the pps offset instead. Note that we + * believe the ppsclock timestamp only if the ioctl works and + * the new timestamp is greater than the previous one. */ - gps->lastrec = rbufp->recv_time; - tstmp = gps->lastref; - L_SUB(&tstmp, &gps->lastrec); - L_ADD(&tstmp, &(fudgefactor[gps->unit])); - trtmp = tstmp; - if (L_ISNEG(&trtmp)) - L_NEG(&trtmp); - if (trtmp.l_i < CLOCK_MAX_I || (trtmp.l_i == CLOCK_MAX_I - && (U_LONG)trtmp.l_uf < (U_LONG)CLOCK_MAX_F)) { - if (ioctl(fdpps, CIOGETEV, (caddr_t)&ev) >= 0) { - if (gps->lastev < ev.tv.tv_sec) { - trtmp.l_ui = ev.tv.tv_sec + (U_LONG)JAN_1970; - TVUTOTSF(ev.tv.tv_usec, trtmp.l_uf); - L_NEG(&trtmp); - tstmp.l_i = tstmp.l_f = 0; - M_ADDF(tstmp.l_i, tstmp.l_f, trtmp.l_f); + if (pp->sloppyclockflag & CLK_FLAG3 && fdpps != -1) { + if (!clocktime(pp->day, pp->hour, pp->minute, + pp->second, GMT, pp->lastrec.l_ui, &pp->yearstart, + &pp->lastref.l_ui)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + MSUTOTSF(pp->msec, pp->lastref.l_uf); + pp->lastrec = trtmp; + L_SUB(&trtmp, &pp->lastref); + if (L_ISNEG(&trtmp)) + L_NEG(&trtmp); + if (trtmp.l_i < CLOCK_MAX_I || (trtmp.l_i == CLOCK_MAX_I + && trtmp.l_uf < CLOCK_MAX_F)) { + if (ioctl(fdpps, CIOGETEV, (caddr_t)&ev) >= 0) { + if (up->lastev < ev.tv.tv_sec) { + TVUTOTSF(ev.tv.tv_usec, ltemp); + pp->lastrec = pp->lastref; + L_ADDF(&pp->lastrec, ltemp); + } + up->lastev = ev.tv.tv_sec; } - gps->lastev = ev.tv.tv_sec; } } -#else - tstmp = gps->lastref; - L_SUB(&tstmp, &gps->lastrec); - L_ADD(&tstmp, &(fudgefactor[gps->unit])); -#endif /* AS2201PPS */ - i = ((int)(gps->coderecv)) % NCODES; - gps->offset[i] = tstmp; - gps->coderecv++; -#if DEBUG +#endif /* PPS */ +#ifdef DEBUG if (debug) printf("gps: times %s %s %s\n", - ulfptoa(&gps->lastref, 6), ulfptoa(&gps->lastrec, 6), - lfptoa(&tstmp, 6)); + ulfptoa(&pp->lastref, 6), ulfptoa(&pp->lastrec, 6), + lfptoa(&trtmp, 6)); #endif /* - * If the statistics-record switch (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(gps->lastptr, gps->lastcode); - gps->lastptr += gps->lencode; - if (sloppyclockflag[gps->unit] & CLK_FLAG4) { - *gps->lastptr++ = ' '; - (void)strcpy(gps->lastptr, stat_command[gps->index]); - gps->lastptr += strlen(stat_command[gps->index]); - gps->lastptr--; - *gps->lastptr = '\0'; - (void)write(gps->io.fd, stat_command[gps->index], - strlen(stat_command[gps->index])); - gps->index++; - if (*stat_command[gps->index] == '\0') - gps->index = 0; - } - - /* - * Process the samples in the median filter, add the fudge - * factor and pass the offset and dispersion along. We use - * lastref 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. + * 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 (gps->coderecv < NCODES) - return; - if (!as2201_process(gps, &tstmp, &dispersion)) { - gps->baddata++; - as2201_report_event(gps, CEVNT_BADTIME); + if (!refclock_process(pp, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); return; } - refclock_receive(gps->peer, &tstmp, GMT, dispersion, - &gps->lastrec, &gps->lastrec, gps->leap); -} - -/* - * as2201_process - process a pile of samples from the clock - * - * This routine uses a three-stage median filter to calculate offset and - * dispersion and reduce jitter. The dispersion is calculated as the - * span of the filter (max - min). - */ -static char -as2201_process(gps, offset, dispersion) - struct gpsunit *gps; - l_fp *offset; - u_fp *dispersion; -{ - register int i, j; - register U_LONG tmp_ui, tmp_uf; - int not_median1 = -1; /* XXX correct? */ - int not_median2 = -1; /* XXX correct? */ - int median; - u_fp disp_tmp, disp_tmp2; + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->leap); /* - * This code implements a three-stage median filter. First, we - * check if the samples are within 125 ms of each other. If not, - * dump the sample set. We take the median of the three offsets - * and use that as the sample offset. There probably is not much - * to be gained by a longer filter, since the clock filter in - * ntp_proto should do its thing. + * If CLK_FLAG4 is set, initialize the statistics buffer and + * send the next command. If not, simply write the timecode to + * the clockstats file. */ - disp_tmp2 = 0; - for (i = 0; i < NCODES-1; i++) { - for (j = i+1; j < NCODES; j++) { - tmp_ui = gps->offset[i].l_ui; - tmp_uf = gps->offset[i].l_uf; - M_SUB(tmp_ui, tmp_uf, gps->offset[j].l_ui, - gps->offset[j].l_uf); - if (M_ISNEG(tmp_ui, tmp_uf)) { - M_NEG(tmp_ui, tmp_uf); - } - if (tmp_ui != 0 || tmp_uf > CODEDIFF) { - return (0); - } - disp_tmp = MFPTOFP(0, tmp_uf); - if (disp_tmp > disp_tmp2) { - disp_tmp2 = disp_tmp; - not_median1 = i; - not_median2 = j; - } - } + (void)strcpy(up->lastptr, pp->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; } - if (gps->lasttime == 0) - disp_tmp2 = NTP_MAXDISPERSE; - else - disp_tmp2 = current_time - gps->lasttime; - if (not_median1 == 0) { - if (not_median2 == 1) - median = 2; - else - median = 1; - } else { - median = 0; - } - *offset = gps->offset[median]; - *dispersion = disp_tmp2; - return (1); } + /* * as2201_poll - called by the transmit procedure * @@ -854,142 +429,25 @@ as2201_poll(unit, peer) int unit; struct peer *peer; { - struct gpsunit *gps; + register struct as2201unit *up; + struct refclockproc *pp; - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "gps_poll: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "gps_poll: unit %d not in use", unit); - return; - } - gps = gpsunits[unit]; - if ((current_time - gps->lasttime) > 150) - as2201_report_event(gpsunits[unit], CEVNT_TIMEOUT); - gettstamp(&gps->lastrec); - if (write(gps->io.fd, "\r*toc\r", 6) != 6) { - syslog(LOG_ERR, "gps_poll: unit %d: %m", gps->unit); - as2201_report_event(gps, CEVNT_FAULT); + /* + * 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; + up = (struct as2201unit *)pp->unitptr; + if (up->pollcnt == 0) + refclock_report(peer, CEVNT_TIMEOUT); + else + up->pollcnt--; + gettstamp(&pp->lastrec); + if (write(pp->io.fd, "\r*toc\r", 6) != 6) { + refclock_report(peer, CEVNT_FAULT); } else - gps->polls++; + pp->polls++; } -/* - * as2201_control - set fudge factors, return statistics - */ -static void -as2201_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - register struct gpsunit *gps; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "gps_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 - */ - gps = gpsunits[unit]; - peer = gps->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, - GPSREFID, 4); - else - peer->refid = htonl(GPSHSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG4) { - sloppyclockflag[unit] = in->flags & CLK_FLAG4; - } - } - - if (out != 0) { - out->type = REFCLK_GPS_AS2201; - out->haveflags - = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG4; - out->clockdesc = GPSDESCRIPTION; - 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]) { - gps = gpsunits[unit]; - out->lencode = LENTOC; - out->lastcode = gps->stats; - out->timereset = current_time - gps->timestarted; - out->polls = gps->polls; - out->noresponse = gps->noreply; - out->badformat = gps->badformat; - out->baddata = gps->baddata; - out->lastevent = gps->lastevent; - out->currentstatus = gps->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; - } - } -} - -/* - * as2201_buginfo - return clock dependent debugging info - */ -static void -as2201_buginfo(unit, bug) - int unit; - register struct refclockbug *bug; -{ - register struct gpsunit *gps; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "gps_buginfo: unit %d invalid", unit); - return; - } - - if (!unitinuse[unit]) - return; - gps = gpsunits[unit]; - - bug->nvalues = 10; - bug->ntimes = 5; - if (gps->lasttime != 0) - bug->values[0] = current_time - gps->lasttime; - else - bug->values[0] = 0; - bug->values[1] = (U_LONG)gps->reason; - bug->values[2] = (U_LONG)gps->year; - bug->values[3] = (U_LONG)gps->day; - bug->values[4] = (U_LONG)gps->hour; - bug->values[5] = (U_LONG)gps->minute; - bug->values[6] = (U_LONG)gps->second; - bug->values[7] = (U_LONG)gps->msec; - bug->values[8] = gps->noreply; - bug->values[9] = gps->yearstart; - bug->stimes = 0x1c; - bug->times[0] = gps->lastref; - bug->times[1] = gps->lastrec; - bug->times[2] = gps->offset[0]; - bug->times[3] = gps->offset[1]; - bug->times[4] = gps->offset[2]; -} -#endif +#endif /* REFCLOCK */ diff --git a/usr.sbin/xntpd/xntpd/refclock_atom.c b/usr.sbin/xntpd/xntpd/refclock_atom.c new file mode 100644 index 000000000000..71fe05d8f0da --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_atom.c @@ -0,0 +1,499 @@ +/* + * refclock_atom - clock driver for 1-pps signals + */ +#if defined(REFCLOCK) && defined(ATOM) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" + +#ifdef PPS +#include <sys/ppsclock.h> +#endif /* PPS */ + +/* + * 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 and RS232 level converter. When operated + * at 38.4 kbps with a SPARCstation IPC, this arrangement has a worst- + * case jitter less than 26 us. + * + * There are three ways in which this driver can be used. The first way + * uses the LDISC_PPS line discipline and works only for the baseboard + * serial ports of the Sun SPARCstation. The PPS signal is connected via + * a gadget box to the carrier detect (CD) line of a serial port and + * flag3 of the driver configured for that port is set. This causes the + * ppsclock streams module to be configured for that port and capture a + * timestamp at the on-time transition of the PPS signal. This driver + * then reads the timestamp directly by a designated ioctl() system + * call. This provides the most accurate time and least jitter of any + * other scheme. There is no need to configure a dedicated device for + * this purpose, which ordinarily is the device used for the associated + * radio clock. + * + * The second 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 third 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. + * + * Fudge Factors + * + * There are no special fudge factors other than the generic and those + * explicitly defined above. 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 SunOS streams + * nonsense. + */ + +/* + * Interface definitions + */ +#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 +#define PRECISION (-20) /* precision assumed (about 1 usec) */ +#define REFID "PPS\0" /* reference ID */ +#define DESCRIPTION "PPS Clock Discipline" /* WRU */ + +#define PPSMAXDISPERSE (FP_SECOND / 100) /* max sample dispersion */ +#define NSAMPLES 32 /* final stages of median filter */ +#ifdef PPS +#define PPS_POLL 2 /* ppsclock poll interval (s) */ +#endif /* PPS */ + +/* + * Imported from ntp_timer module + */ +extern u_long current_time; /* current time (s) */ +extern struct event timerqueue[]; /* inner space */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ +extern int pps_update; /* prefer peer valid update */ + +/* + * Imported from ntp_proto module + */ +extern struct peer *sys_peer; /* somebody in charge */ + +/* + * Unit control structure + */ +struct atomunit { +#ifdef PPS + struct event timer; /* pps poll interval timer */ + struct ppsclockev ev; /* ppsclock control */ +#endif /* PPS */ + int pollcnt; /* poll message counter */ +}; + +/* + * Global variables + */ +struct peer *last_atom_peer; /* peer structure pointer */ + +/* + * Function prototypes + */ +static int atom_start P((int, struct peer *)); +static void atom_shutdown P((int, struct peer *)); +static void atom_receive P((struct recvbuf *)); +static void atom_poll P((int, struct peer *)); +#ifdef PPS +static void atom_pps P((struct peer *)); +#endif /* PPS */ + +/* + * 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(unit, peer) + int unit; + struct peer *peer; +{ + register struct atomunit *up; + struct refclockproc *pp; + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct atomunit *) + emalloc(sizeof(struct atomunit)))) + return (0); + memset((char *)up, 0, sizeof(struct atomunit)); + pp = peer->procptr; + pp->unitptr = (caddr_t)up; + + /* + * Initialize miscellaneous variables + */ + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; + pp->nstages = MAXSTAGE; + +#ifdef PPS + /* + * Arm the timer for the first interrupt. Give it ten seconds to + * allow the ppsclock line to be configured, since it could be + * assigned to another driver. + */ + up->timer.peer = (struct peer *)peer; + up->timer.event_handler = atom_pps; + up->timer.event_time = current_time + 10; + TIMER_INSERT(timerqueue, &up->timer); +#endif /* PPS */ + last_atom_peer = peer; + return (1); +} + + +/* + * atom_shutdown - shut down the clock + */ +static void +atom_shutdown(unit, peer) + int unit; + struct peer *peer; +{ + register struct atomunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct atomunit *)pp->unitptr; + + if (last_atom_peer == peer) + last_atom_peer = 0; +#ifdef PPS + TIMER_DEQUEUE(&up->timer); +#endif /* PPS */ + if (pp->io.fd) + io_closeclock(&pp->io); + free(up); +} + +/* + * pps_sample - process pps sample offset -- backwards compatible + * interface + */ +int +pps_sample(tsr) + l_fp *tsr; +{ + struct peer *peer; + struct refclockproc *pp; + register struct atomunit *up; + int i; + l_fp lftemp; /* l_fp temps */ + + /* + * This routine is called once per second by an auxilliary + * routine in another driver. It saves the sign-extended + * fraction supplied in the argument in a circular buffer for + * processing at the next poll event. + */ + peer = last_atom_peer; + if (!peer) + return (-1); /* no ATOM configured ? Forget it ! */ + + pp = peer->procptr; + up = (struct atomunit *)pp->unitptr; + + L_CLR(&lftemp); + L_ADDF(&lftemp, tsr->l_f); + i = ((int)(pp->coderecv)) % pp->nstages; + pp->filter[i] = lftemp; + if (pp->coderecv == 0) + for (i = 1; i < pp->nstages; i++) + pp->filter[i] = pp->filter[0]; + pp->coderecv++; + up->pollcnt = 2; + + /* HACK -- use the local UN*X clock to get the time -- this is wrong */ + pp->lastrec.l_ui = time(0) - 2 + JAN_1970; + pp->lastrec.l_uf = 0; + + return (0); +} + +#ifdef PPS +/* + * atom_pps - receive data from the LDISC_PPS discipline + */ +static void +atom_pps(peer) + struct peer *peer; +{ + register struct atomunit *up; + struct refclockproc *pp; + l_fp lftmp; + int i; + + /* + * 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; + + /* + * Arm the timer for the next interrupt + */ + up->timer.event_time = current_time + PPS_POLL; + TIMER_INSERT(timerqueue, &up->timer); + + /* + * 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. + */ + i = up->ev.serial; + if (ioctl(fdpps, CIOGETEV, (caddr_t)&up->ev) < 0) + return; + if (i == up->ev.serial) + return; + pp->lastrec.l_ui = up->ev.tv.tv_sec + JAN_1970; + TVUTOTSF(up->ev.tv.tv_usec, pp->lastrec.l_uf); + L_CLR(&lftmp); + L_ADDF(&lftmp, pp->lastrec.l_f); + L_NEG(&lftmp); + i = ((int)(pp->coderecv)) % pp->nstages; + pp->filter[i] = lftmp; + if (pp->coderecv == 0) + for (i = 1; i < pp->nstages; i++) + pp->filter[i] = pp->filter[0]; + pp->coderecv++; + up->pollcnt = 2; +} +#endif /* PPS */ + +/* + * atom_receive - receive data from the serial line interface + */ +static void +atom_receive(rbufp) + struct recvbuf *rbufp; +{ + register struct atomunit *up; + struct refclockproc *pp; + struct peer *peer; + l_fp lftmp; + int i; + + /* + * 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->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. + */ + L_CLR(&lftmp); + L_ADDF(&lftmp, pp->lastrec.l_f); + L_NEG(&lftmp); + i = ((int)(pp->coderecv)) % pp->nstages; + pp->filter[i] = lftmp; + if (pp->coderecv == 0) + for (i = 1; i < pp->nstages; i++) + pp->filter[i] = pp->filter[0]; + pp->coderecv++; + up->pollcnt = 2; +} + +/* + * Compare two l_fp's - used with qsort() + */ +static int +atom_cmpl_fp(p1, p2) + register void *p1, *p2; /* l_fp to compare */ +{ + + if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2)) + return (-1); + if (L_ISEQU((l_fp *)p1, (l_fp *)p2)) + return (0); + return (1); +} + +/* + * atom_poll - called by the transmit procedure + */ +static void +atom_poll(unit, peer) + int unit; + struct peer *peer; +{ + register struct atomunit *up; + struct refclockproc *pp; + int i, n; + l_fp median, lftmp; + l_fp off[MAXSTAGE]; + u_fp disp; + + /* + * At each poll we check for timeout. At the first timeout we + * test to see if the LDISC_PPS discipline is present and, if + * so, use that. If not, we attempt to open a serial line with + * LDISC_CLKPPS discipline. If that fails, we bitch to the log + * and clam up. + */ + pp = peer->procptr; + up = (struct atomunit *)pp->unitptr; + pp->polls++; + if (up->pollcnt == 0) { + refclock_report(peer, CEVNT_FAULT); + return; + } + up->pollcnt--; + if (up->pollcnt == 0) { + if (!pp->io.fd && fdpps == -1) { + int fd; + char device[20]; + + /* + * Open serial port. Use CLKPPS line discipline, + * if available. If unavailable, the code works + * anyway, but at reduced accuracy. + */ + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, + LDISC_CLKPPS))) { + refclock_report(peer, CEVNT_FAULT); + return; + } + 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); + refclock_report(peer, CEVNT_FAULT); + 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 insures + * the pps time is within +-0.5 s of the local time and the + * seconds numbering is unambiguous. + */ + if (pps_update) { + pp->leap = 0; + pp->lasttime = current_time; + } else + pp->leap = LEAP_NOTINSYNC; + + /* + * Copy the raw offsets and sort into ascending order + */ + for (i = 0; i < MAXSTAGE; i++) + off[i] = pp->filter[i]; + qsort((char *)off, pp->nstages, sizeof(l_fp), atom_cmpl_fp); + + /* + * Reject the furthest from the median of nstages samples until + * nskeep samples remain. + */ + i = 0; + n = pp->nstages; + while ((n - i) > NSAMPLES) { + 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--; + } + } + + /* + * 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. + * Otherwise, return the median offset plus the configured + * fudgetime1 value. + */ + lftmp = off[n - 1]; + L_SUB(&lftmp, &off[i]); + disp = LFPTOFP(&lftmp) + current_time - pp->lasttime; + if (disp > PPSMAXDISPERSE) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + pp->offset = off[(n + 1) / 2]; + L_ADD(&pp->offset, &pp->fudgetime1); + pp->dispersion = disp; + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->leap); +} + +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_chu.c b/usr.sbin/xntpd/xntpd/refclock_chu.c index 4596db22e2f5..2488da62e9c0 100644 --- a/usr.sbin/xntpd/xntpd/refclock_chu.c +++ b/usr.sbin/xntpd/xntpd/refclock_chu.c @@ -1,7 +1,7 @@ /* * refclock_chu - clock driver for the CHU time code */ -#if defined(REFCLOCK) && (defined(CHU) || defined(CHUCLK) || defined(CHUPPS)) +#if defined(REFCLOCK) && defined(CHU) #include <stdio.h> #include <ctype.h> @@ -11,28 +11,7 @@ #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_unixtime.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 /* STREAM */ - -#if defined (CHUPPS) -#include <sys/ppsclock.h> -#endif /* CHUPPS */ - #include <sys/chudefs.h> - #include "ntp_stdlib.h" /* @@ -79,29 +58,30 @@ * in this nibble. * * The start bit in each character has a precise relationship to - * the on-time second. Most often UART's synchronize themselves to the + * the on-time second. Most often UART's synchronize themselves to the * start bit and will post an interrupt at the center of the first stop * bit. Thus each character's interrupt should occur at a fixed offset - * from the on-time second. This means that a timestamp taken at the + * from the on-time second. This means that a timestamp taken at the * arrival of each character in the code will provide an independent * estimate of the offset. Since there are 10 characters in the time - * code and the code is sent 9 times per minute, this means you potentially - * get 90 offset samples per minute. Much of the code in here is dedicated - * to producing a single offset estimate from these samples. + * code and the code is sent 9 times per minute, this means you + * potentially get 90 offset samples per minute. Much of the code in + * here is dedicated to producing a single offset estimate from these + * samples. * - * A note about the line discipline. It is possible to receive the - * CHU time code in raw mode, but this has disadvantages. In particular, + * A note about the line discipline. It is possible to receive the + * CHU time code in raw mode, but this has disadvantages. In particular, * this puts a lot of code between the interrupt and the time you freeze - * a time stamp, decreasing precision. It is also expensive in terms of + * a time stamp, decreasing precision. It is also expensive in terms of * context switches, and made even more expensive by the way I do I/O. * Worse, since you are listening directly to the output of your radio, * CHU is noisy and will make you spend a lot of time receiving noise. * - * The line discipline fixes a lot of this. It knows that the CHU time + * The line discipline fixes a lot of this. It knows that the CHU time * code consists of 10 bytes which arrive with an intercharacter * spacing of about 37 ms, and that the data is BCD, and filters on this - * basis. It delivers block of ten characters plus their associated time - * stamps all at once. The time stamps are hence about as accurate as + * basis. It delivers block of ten characters plus their associated time + * stamps all at once. The time stamps are hence about as accurate as * a Unix machine can get them, and much of the noise disappears in the * kernel with no context switching cost. * @@ -112,13 +92,18 @@ */ /* - * Definitions + * CHU definitions */ -#define MAXUNITS 4 /* maximum number of CHU units permitted */ -#define CHUDEV "/dev/chu%d" /* device we open. %d is unit number */ -#define SPEED232 B300 /* uart speed (300 baud) */ +#define DEVICE "/dev/chu%d" /* device name and unit */ +#define SPEED232 B300 /* uart speed (300 baud) */ +#define PRECISION (-9) /* what the heck */ +#define REFID "CHU\0" /* reference ID */ +#define DESCRIPTION "Scratchbuilt CHU Receiver" /* WRU */ + #define NCHUCODES 8 /* expect 8 CHU codes per minute */ +#ifndef CHULDISC #define CHULDISC 10 /* XXX temp CHU line discipline */ +#endif /* * To compute a quality for the estimate (a pseudo dispersion) we add a @@ -129,14 +114,6 @@ #define CHUDELAYPENALTY 0x0000028f /* - * Other constant stuff - */ -#define CHUPRECISION (-9) /* what the heck */ -#define CHUREFID "CHU\0" -#define CHUDESCRIPTION "Direct synchronized to CHU timecode" -#define CHUHSREFID 0x7f7f070a /* 127.127.7.10 refid for hi strata */ - -/* * Default fudge factors */ #define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */ @@ -160,37 +137,27 @@ static char hexstring[]="0123456789abcdef"; /* - * CHU unit control structure. + * Unit control structure. */ struct chuunit { - struct peer *peer; /* associated peer structure */ - struct event chutimer; /* timeout timer structure */ - struct refclockio chuio; /* given to the I/O handler */ - l_fp offsets[NCHUCODES]; /* offsets computed from each code */ - l_fp rectimes[NCHUCODES]; /* times we received this stuff */ - U_LONG reftimes[NCHUCODES]; /* time of last code received */ - u_char lastcode[NCHUCHARS*4]; /* last code we received */ - u_char asciicode[NCHUCHARS*4+1]; /* last code translated to ascii */ - u_char expect; /* the next offset expected */ - u_char unit; /* unit number for this guy */ - u_short haveoffset; /* flag word indicating valid offsets */ - u_short flags; /* operational flags */ - u_char status; /* clock status */ - u_char lastevent; /* last clock event */ - u_char unused[2]; - U_LONG lastupdate; /* last time data received */ - U_LONG responses; /* number of responses */ - U_LONG badformat; /* number of bad format responses */ - U_LONG baddata; /* number of invalid time codes */ - U_LONG timestarted; /* time we started this */ - u_char leap; /* leap status */ + struct peer *peer; /* peer structure pointer */ + struct event chutimer; /* timeout timer structure */ + l_fp offsets[NCHUCODES]; /* offsets computed from each code */ + l_fp rectimes[NCHUCODES]; /* times we received this stuff */ + u_long reftimes[NCHUCODES]; /* time of last code received */ + u_char lastcode[NCHUCHARS * 4]; /* last code we received */ + u_char expect; /* the next offset expected */ + u_short haveoffset; /* flag word indicating valid offsets */ + u_short flags; /* operational flags */ + u_long responses; /* number of responses */ + int pollcnt; /* poll message counter */ }; -#define CHUTIMERSET 0x1 /* timer is set to fire */ +#define CHUTIMERSET 0x1 /* timer is set to fire */ /* - * The CHU table. This gives the expected time of arrival of each + * The CHU table. This gives the expected time of arrival of each * character after the on-time second and is computed as follows: * The CHU time code is sent at 300 bps. Your average UART will * synchronize at the edge of the start bit and will consider the @@ -198,12 +165,11 @@ struct chuunit { * 0.031667 ms later (some UARTS may complete the character at the * end of the stop bit instead of the middle, but you can fudge this). * Thus the expected time of each interrupt is the start bit time plus - * 0.031667 seconds. These times are in chutable[]. To this we add - * such things as propagation delay and delay fudge factor. + * 0.031667 seconds. These times are in chutable[]. */ #define CHARDELAY 0x081b4e82 -static U_LONG chutable[NCHUCHARS] = { +static u_long chutable[NCHUCHARS] = { 0x22222222 + CHARDELAY, /* 0.1333333333 */ 0x2b851eb8 + CHARDELAY, /* 0.170 (exactly) */ 0x34e81b4e + CHARDELAY, /* 0.2066666667 */ @@ -217,348 +183,110 @@ static U_LONG chutable[NCHUCHARS] = { }; /* - * Data space for the unit structures. Note that we allocate these on - * the fly, but never give them back. - */ -static struct chuunit *chuunits[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 propagation_delay[MAXUNITS]; -static l_fp fudgefactor[MAXUNITS]; -static l_fp offset_fudge[MAXUNITS]; -static u_char stratumtouse[MAXUNITS]; -static u_char sloppyclockflag[MAXUNITS]; - -/* - * We keep track of the start of the year, watching for changes. - * We also keep track of whether the year is a leap year or not. - * All because stupid CHU doesn't include the year in the time code. - */ -static U_LONG yearstart; - -/* * Imported from the timer module */ -extern U_LONG current_time; +extern u_long current_time; extern struct event timerqueue[]; /* - * Imported from ntp_loopfilter module - */ -extern int fdpps; /* pps file descriptor */ - -/* * Imported from ntpd module */ extern int debug; /* global debug flag */ /* - * Event reporting. This optimizes things a little. + * Function prototypes */ -#define chu_event(chu, evcode) \ - do { \ - if ((chu)->status != (u_char)(evcode)) \ - chu_report_event((chu), (evcode)); \ - } while (0) - -static void chu_init P((void)); -static int chu_start P((u_int, struct peer *)); -static void chu_shutdown P((int)); -static void chu_report_event P((struct chuunit *, int)); +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_process P((struct chuunit *)); static void chu_poll P((int, struct peer *)); -static void chu_control P((u_int, struct refclockstat *, struct refclockstat *)); static void chu_timeout P((struct peer *)); /* * Transfer vector */ struct refclock refclock_chu = { - chu_start, chu_shutdown, chu_poll, - chu_control, chu_init, noentry, NOFLAGS + 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_init - initialize internal chu driver data - */ -static void -chu_init() -{ - register int i; - /* - * Just zero the data arrays - */ - memset((char *)chuunits, 0, sizeof chuunits); - memset((char *)unitinuse, 0, sizeof unitinuse); - - /* - * Initialize fudge factors to default. - */ - for (i = 0; i < MAXUNITS; i++) { - propagation_delay[i].l_ui = 0; - propagation_delay[i].l_uf = DEFPROPDELAY; - fudgefactor[i].l_ui = 0; - fudgefactor[i].l_uf = DEFFILTFUDGE; - offset_fudge[i] = propagation_delay[i]; - L_ADD(&offset_fudge[i], &fudgefactor[i]); - stratumtouse[i] = 0; - sloppyclockflag[i] = 0; - } -} - /* * chu_start - open the CHU device and initialize data for processing */ static int chu_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { - register struct chuunit *chu; - register int i; - int fd232; - char chudev[20]; - l_fp ts; + register struct chuunit *up; + struct refclockproc *pp; + int fd; + char device[20]; /* - * Check configuration info + * Open serial port and set CHU line discipline */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "chu_start: unit %d invalid", unit); - return 0; - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "chu_start: unit %d in use", unit); - return 0; - } + (void) sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_CHU))) + return (0); /* - * Open serial port + * Allocate and initialize unit structure */ - (void) sprintf(chudev, CHUDEV, unit); - fd232 = open(chudev, O_RDONLY, 0777); - if (fd232 == -1) { - syslog(LOG_ERR, "chu_start: open of %s: %m", chudev); - return 0; + if (!(up = (struct chuunit *) + emalloc(sizeof(struct chuunit)))) { + (void) close(fd); + return (0); } - -#if defined(HAVE_SYSV_TTYS) - /* - * System V serial line parameters (termio interface) - */ - CHU SUPPORT NOT AVAILABLE IN TERMIO INTERFACE -#endif /* HAVE_SYSV_TTYS */ -#if defined(HAVE_TERMIOS) - /* - * POSIX serial line parameters (termios interface) - * - * The CHUCLK support uses a 300-baud modem and level converter - * (gadget box). It requires the chu_clk streams module and - * SunOS 4.1.1 or later. - * - * The CHUPPS 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 SunOS 4.1.1 or - * later. - */ - { struct termios ttyb, *ttyp; - - ttyp = &ttyb; - if (tcgetattr(fd232, ttyp) < 0) { - syslog(LOG_ERR, - "chu_start: tcgetattr(%s): %m", chudev); - goto screwed; - } - ttyp->c_iflag = IGNBRK|IGNPAR; - ttyp->c_oflag = 0; - ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD; - ttyp->c_lflag = 0; - ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; - ttyp->c_cc[VMIN] = 1; - ttyp->c_cc[VTIME] = 0; - if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { - syslog(LOG_ERR, - "chu_start: tcsetattr(%s): %m", chudev); - goto screwed; - } - if (tcflush(fd232, TCIOFLUSH) < 0) { - syslog(LOG_ERR, - "chu_start: tcflush(%s): %m", chudev); - goto screwed; - } - } -#endif /* HAVE_TERMIOS */ -#ifdef STREAM - while (ioctl(fd232, I_POP, 0 ) >= 0) ; - if (ioctl(fd232, I_PUSH, "chu" ) < 0) { - syslog(LOG_ERR, - "chu_start: ioctl(%s, I_PUSH, chu): %m", chudev); - goto screwed; - } -#if defined(CHUPPS) - if (ioctl(fd232, I_PUSH, "ppsclock") < 0) - syslog(LOG_ERR, - "chu_start: ioctl(%s, I_PUSH, ppsclock): %m", chudev); - else - fdpps = fd232; -#endif /* CHUPPS */ -#endif /* STREAM */ -#if defined(HAVE_BSD_TTYS) - /* - * 4.3bsd serial line parameters (sgttyb interface) - * - * The CHUCLK support uses a 300-baud modem and level converter - * (gadget box). It requires the chu_clk streams module and - * 4.3bsd or later. - */ - { struct sgttyb ttyb; - int ldisc = CHULDISC; - - if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { - syslog(LOG_ERR, - "chu_start: ioctl(%s, TIOCGETP): %m", chudev); - goto screwed; - } - ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; - ttyb.sg_erase = ttyb.sg_kill = '\r'; - ttyb.sg_flags = RAW; - if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { - syslog(LOG_ERR, - "chu_start: ioctl(%s, TIOCSETP): %m", chudev); - goto screwed; + memset((char *)up, 0, sizeof(struct chuunit)); + up->chutimer.peer = (struct peer *)up; + up->chutimer.event_handler = chu_timeout; + up->peer = peer; + pp = peer->procptr; + 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); } - if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { - syslog(LOG_ERR, - "chu_start: ioctl(%s, TIOCSETD): %m",chudev); - goto screwed; - } - } -#endif /* HAVE_BSD_TTYS */ - - /* - * Allocate unit structure - */ - if (chuunits[unit] != 0) { - chu = chuunits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < MAXUNITS; i++) { - if (!unitinuse[i] && chuunits[i] != 0) - break; - } - if (i < MAXUNITS) { - /* - * Reclaim this one - */ - chu = chuunits[i]; - chuunits[i] = 0; - } else { - chu = (struct chuunit *)emalloc(sizeof(struct chuunit)); - } - } - memset((char *)chu, 0, sizeof(struct chuunit)); - chuunits[unit] = chu; + pp->unitptr = (caddr_t)up; /* - * Set up the structure + * Initialize miscellaneous variables */ - chu->peer = peer; - chu->unit = (u_char)unit; - chu->timestarted = current_time; - - chu->chutimer.peer = (struct peer *)chu; - chu->chutimer.event_handler = chu_timeout; - - chu->chuio.clock_recv = chu_receive; - chu->chuio.srcclock = (caddr_t)chu; - chu->chuio.datalen = sizeof(struct chucode); - chu->chuio.fd = fd232; - - /* - * Initialize the year from the system time in case this is the - * first open. - */ - get_systime(&ts); - yearstart = calyearstart(ts.l_ui); - if (!io_addclock(&chu->chuio)) { - goto screwed; - } - - /* - * All done. Initialize a few random peer variables, then - * return success. - */ - peer->precision = CHUPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = 0; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, CHUREFID, 4); - else - peer->refid = htonl(CHUHSREFID); - unitinuse[unit] = 1; + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; return (1); - - /* - * Something broke; abandon ship. - */ -screwed: - (void) close(fd232); - return (0); } /* - * chu_shutdown - shut down a CHU clock + * chu_shutdown - shut down the clock */ static void -chu_shutdown(unit) +chu_shutdown(unit, peer) int unit; + struct peer *peer; { - register struct chuunit *chu; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "chu_shutdown: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "chu_shutdown: unit %d not in use", unit); - return; - } + register struct chuunit *up; + struct refclockproc *pp; - /* - * Tell the I/O module to turn us off, and dequeue timer - * if any. We're history. - */ - chu = chuunits[unit]; - if (chu->flags & CHUTIMERSET) - TIMER_DEQUEUE(&chu->chutimer); - io_closeclock(&chu->chuio); - unitinuse[unit] = 0; -} - -/* - * chu_report_event - record an event and report it - */ -static void -chu_report_event(chu, code) - struct chuunit *chu; - int code; -{ - /* - * Trap support isn't up to handling this, so just - * record it. - */ - if (chu->status != (u_char)code) { - chu->status = (u_char)code; - if (code != CEVNT_NOMINAL) - chu->lastevent = (u_char)code; - } + pp = peer->procptr; + up = (struct chuunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); } @@ -570,14 +298,16 @@ static void chu_receive(rbufp) struct recvbuf *rbufp; { - register int i; - register U_LONG date_ui; - register U_LONG tmp; - register u_char *code; - register struct chuunit *chu; - register struct chucode *chuc; + register struct chuunit *up; + struct refclockproc *pp; + struct peer *peer; + int i; + u_long date_ui; + u_long tmp; + u_char *code; + struct chucode *chuc; int isneg; - U_LONG reftime; + u_long reftime; l_fp off[NCHUCHARS]; int day, hour, minute, second; @@ -585,7 +315,8 @@ chu_receive(rbufp) * Do a length check on the data. Should be what we asked for. */ if (rbufp->recv_length != sizeof(struct chucode)) { - syslog(LOG_ERR, "chu_receive: received %d bytes, expected %d", + syslog(LOG_ERR, + "chu_receive: received %d bytes, expected %d", rbufp->recv_length, sizeof(struct chucode)); return; } @@ -593,27 +324,39 @@ chu_receive(rbufp) /* * Get the clock this applies to and a pointer to the data */ - chu = (struct chuunit *)rbufp->recv_srcclock; + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct chuunit *)pp->unitptr; chuc = (struct chucode *)&rbufp->recv_space; - chu->responses++; - chu->lastupdate = current_time; + up->responses++; /* * Just for fun, we can debug the whole frame if * we want. */ - -#ifdef CHU_DEBUG - syslog(LOG_DEBUG, "CHU %s packet:", (chuc->chutype == CHU_YEAR)? - "year":"time"); - for (i=0; i < NCHUCHARS; i++) { - char c[64]; - - sprintf(c,"%c%c %s",hexstring[chuc->codechars[i]&0xf], - hexstring[chuc->codechars[i]>>4], - ctime(&(chuc->codetimes[i].tv_sec))); - c[strlen(c)-1]=0; /* ctime() adds a damn \n */ - syslog(LOG_DEBUG, "%s .%06d", c, chuc->codetimes[i].tv_usec); + for (i = 0; i < NCHUCHARS; i++) { + pp->lastcode[2 * i] = hexstring[chuc->codechars[i] & + 0xf]; + pp->lastcode[2 * i + 1] = hexstring[chuc->codechars[i] + >> 4]; + } + pp->lencode = 2 * i; + pp->lastcode[pp->lencode] = '\0'; +#ifdef DEBUG + if (debug > 3) { + printf("chu: %s packet\n", (chuc->chutype == CHU_YEAR)? + "year":"time"); + for (i = 0; i < NCHUCHARS; i++) { + char c[64]; + + sprintf(c,"%c%c %s", + hexstring[chuc->codechars[i] & 0xf], + hexstring[chuc->codechars[i] >> 4], + ctime(&(chuc->codetimes[i].tv_sec))); + c[strlen(c) - 1] = 0; /* ctime() adds \n */ + printf("chu: %s .%06d\n", c, + chuc->codetimes[i].tv_usec); + } } #endif @@ -633,7 +376,7 @@ chu_receive(rbufp) * Break out the code into the BCD nibbles. * Put it in the half of lastcode. */ - code = chu->lastcode; + code = up->lastcode; code += 2*NCHUCHARS; for (i = 0; i < NCHUCHARS; i++) { *code++ = chuc->codechars[i] & 0xf; @@ -651,8 +394,7 @@ chu_receive(rbufp) parity = (parity ^ (parity>>1))&0x1; if (parity) { - chu->badformat++; - chu_event(chu, CEVNT_BADREPLY); + refclock_report(peer, CEVNT_BADREPLY); return; } @@ -660,15 +402,14 @@ chu_receive(rbufp) * This just happens to work. :-) */ - chu->leap = (leapbits >> 1) & 0x3; + pp->leap = (leapbits >> 1) & 0x3; return; } if (chuc->chutype != CHU_TIME) { - chu->badformat++; - chu_event(chu, CEVNT_BADREPLY); + refclock_report(peer, CEVNT_BADREPLY); return; } @@ -677,7 +418,7 @@ chu_receive(rbufp) * with the first half since both are identical. Note the first * BCD character is the low order nibble, the second the high order. */ - code = chu->lastcode; + code = up->lastcode; for (i = 0; i < NCHUCHARS; i++) { *code++ = chuc->codechars[i] & 0xf; *code++ = (chuc->codechars[i] >> 4) & 0xf; @@ -688,19 +429,18 @@ chu_receive(rbufp) * There's really no need for this, but it can't hurt. */ for (i = 0; i < NCHUCHARS/2; i++) - if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) { - chu->badformat++; - chu_event(chu, CEVNT_BADREPLY); + if (chuc->codechars[i] != + chuc->codechars[i+(NCHUCHARS/2)]) { + refclock_report(peer, CEVNT_BADREPLY); return; } /* * If the first nibble isn't a 6, we're up the creek */ - code = chu->lastcode; + code = up->lastcode; if (*code++ != 6) { - chu->badformat++; - chu_event(chu, CEVNT_BADREPLY); + refclock_report(peer, CEVNT_BADREPLY); return; } @@ -725,11 +465,11 @@ chu_receive(rbufp) if (day < 1 || day > 366 || hour > 23 || minute > 59 || second < 32 || second > 39) { - chu->baddata++; + pp->baddata++; if (day < 1 || day > 366) { - chu_event(chu, CEVNT_BADDATE); + refclock_report(peer, CEVNT_BADDATE); } else { - chu_event(chu, CEVNT_BADTIME); + refclock_report(peer, CEVNT_BADTIME); } return; } @@ -740,8 +480,8 @@ chu_receive(rbufp) * date as bad and forget it. */ if (!clocktime(day, hour, minute, second, 0, - rbufp->recv_time.l_ui, &yearstart, &reftime)) { - chu_event(chu, CEVNT_BADDATE); + rbufp->recv_time.l_ui, &pp->yearstart, (U_LONG *)&reftime)) { + refclock_report(peer, CEVNT_BADDATE); return; } date_ui = reftime;; @@ -752,7 +492,7 @@ chu_receive(rbufp) * the offsets for each character. */ for (i = 0; i < NCHUCHARS; i++) { - register U_LONG tmp2; + register u_long tmp2; off[i].l_ui = date_ui; off[i].l_uf = chutable[i]; @@ -761,7 +501,7 @@ chu_receive(rbufp) M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2); } - if (!sloppyclockflag[chu->unit]) { + if (!pp->sloppyclockflag) { u_short ord[NCHUCHARS]; /* * In here we assume the clock has adequate bits @@ -798,7 +538,7 @@ chu_receive(rbufp) for (tmp = 0; tmp < (NCHUCHARS-1); tmp++) { for (i = (int)tmp+1; i < NCHUCHARS; i++) { if (!L_ISGEQ(&off[ord[i]], &off[ord[tmp]])) { - date_ui = (U_LONG)ord[i]; + date_ui = (u_long)ord[i]; ord[i] = ord[tmp]; ord[tmp] = (u_short)date_ui; } @@ -853,8 +593,8 @@ chu_receive(rbufp) * out of a 64 bit product, even after rounding. */ if (date_ui < 9 || date_ui > 0xfffffff7) { - register U_LONG prod_ui; - register U_LONG prod_uf; + register u_long prod_ui; + register u_long prod_uf; prod_ui = prod_uf = 0; /* @@ -881,10 +621,10 @@ chu_receive(rbufp) * date_ui is integral part, tmp is fraction. */ } else { - register U_LONG prod_ovr; - register U_LONG prod_ui; - register U_LONG prod_uf; - register U_LONG highbits; + register u_long prod_ovr; + register u_long prod_ui; + register u_long prod_uf; + register u_long highbits; prod_ovr = prod_ui = prod_uf = 0; if (isneg) @@ -902,7 +642,7 @@ chu_receive(rbufp) } if (prod_uf & 0x80000000) - M_ADDUF(prod_ovr, prod_ui, (U_LONG)1); + M_ADDUF(prod_ovr, prod_ui, (u_long)1); date_ui = prod_ovr; tmp = prod_ui; } @@ -914,56 +654,50 @@ chu_receive(rbufp) * it in the structure. */ i = second - 32; /* gives a value 0 through 8 */ - if (i < (int)chu->expect) { + if (i < (int)up->expect) { /* * This shouldn't actually happen, but might if a single * bit error occurred in the code which fooled us. * Throw away all previous data. */ - chu->expect = 0; - chu->haveoffset = 0; - if (chu->flags & CHUTIMERSET) { - TIMER_DEQUEUE(&chu->chutimer); - chu->flags &= ~CHUTIMERSET; + up->expect = 0; + up->haveoffset = 0; + if (up->flags & CHUTIMERSET) { + TIMER_DEQUEUE(&up->chutimer); + up->flags &= ~CHUTIMERSET; } } - /* - * Add in fudge factor. - */ - M_ADD(date_ui, tmp, offset_fudge[chu->unit].l_ui, - offset_fudge[chu->unit].l_uf); - - chu->offsets[i].l_ui = date_ui; - chu->offsets[i].l_uf = tmp; - chu->rectimes[i] = rbufp->recv_time; - chu->reftimes[i] = reftime; + up->offsets[i].l_ui = date_ui; + up->offsets[i].l_uf = tmp; + up->rectimes[i] = rbufp->recv_time; + up->reftimes[i] = reftime; - chu->expect = i + 1; - chu->haveoffset |= (1<<i); + up->expect = i + 1; + up->haveoffset |= (1 << i); - if (chu->expect >= NCHUCODES) { + if (up->expect >= NCHUCODES) { /* * Got a full second's worth. Dequeue timer and * process this. */ - if (chu->flags & CHUTIMERSET) { - TIMER_DEQUEUE(&chu->chutimer); - chu->flags &= ~CHUTIMERSET; + if (up->flags & CHUTIMERSET) { + TIMER_DEQUEUE(&up->chutimer); + up->flags &= ~CHUTIMERSET; } - chu_process(chu); - } else if (!(chu->flags & CHUTIMERSET)) { + chu_process(up); + } else if (!(up->flags & CHUTIMERSET)) { /* * Try to take an interrupt sometime after the * 42 second mark (leaves an extra 2 seconds for * slop). Round it up to an even multiple of * 4 seconds. */ - chu->chutimer.event_time = - current_time + (U_LONG)(10 - i) + (1<<EVENT_TIMEOUT); - chu->chutimer.event_time &= ~((1<<EVENT_TIMEOUT) - 1); - TIMER_INSERT(timerqueue, &chu->chutimer); - chu->flags |= CHUTIMERSET; + up->chutimer.event_time = + current_time + (u_long)(10 - i) + (1<<EVENT_TIMEOUT); + up->chutimer.event_time &= ~((1<<EVENT_TIMEOUT) - 1); + TIMER_INSERT(timerqueue, &up->chutimer); + up->flags |= CHUTIMERSET; } } @@ -990,62 +724,57 @@ chu_timeout(fakepeer) * the results on to the NTP clock filters. */ static void -chu_process(chu) - register struct chuunit *chu; +chu_process(up) + register struct chuunit *up; { - register int i; - register s_fp bestoff; - register s_fp tmpoff; + struct peer *peer; + struct refclockproc *pp; + int i; + s_fp bestoff; + s_fp tmpoff; u_fp dispersion; int imax; - l_fp ts; /* * The most positive offset. */ + peer = up->peer; + pp = peer->procptr; imax = NCHUCODES; for (i = 0; i < NCHUCODES; i++) - if (chu->haveoffset & (1<<i)) - if (i < imax || L_ISGEQ(&chu->offsets[i], - &chu->offsets[imax])) + if (up->haveoffset & (1<<i)) + if (i < imax || L_ISGEQ(&up->offsets[i], + &up->offsets[imax])) imax = i; /* * The most positive estimate is our best bet. Go through * the list again computing the dispersion. */ - bestoff = LFPTOFP(&chu->offsets[imax]); + bestoff = LFPTOFP(&up->offsets[imax]); dispersion = 0; for (i = 0; i < NCHUCODES; i++) { - if (chu->haveoffset & (1<<i)) { - tmpoff = LFPTOFP(&chu->offsets[i]); + if (up->haveoffset & (1<<i)) { + tmpoff = LFPTOFP(&up->offsets[i]); dispersion += (bestoff - tmpoff); } else { dispersion += CHUDELAYPENALTY; } } - /* - * Make up a reference time stamp, then give it to the - * reference clock support code for further processing. - */ - ts.l_ui = chu->reftimes[imax]; - ts.l_uf = chutable[NCHUCHARS-1]; - - for (i = 0; i < NCHUCHARS*4; i++) { - chu->asciicode[i] = hexstring[chu->lastcode[i]]; - } - chu->asciicode[i] = '\0'; - record_clock_stats(&(chu->peer->srcadr), chu->asciicode); - refclock_receive(chu->peer, &chu->offsets[imax], 0, - dispersion, &ts, &chu->rectimes[imax], chu->leap); + pp->lasttime = current_time; + up->pollcnt = 2; + record_clock_stats(&peer->srcadr, pp->lastcode); + refclock_receive(peer, &up->offsets[imax], 0, + dispersion, &up->rectimes[imax], &up->rectimes[imax], + pp->leap); /* * Zero out unit for next code series */ - chu->haveoffset = 0; - chu->expect = 0; - chu_event(chu, CEVNT_NOMINAL); + up->haveoffset = 0; + up->expect = 0; + refclock_report(peer, CEVNT_NOMINAL); } @@ -1057,103 +786,15 @@ chu_poll(unit, peer) int unit; struct peer *peer; { - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "chu_poll: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "chu_poll: unit %d not in use", unit); - return; - } + register struct chuunit *up; + struct refclockproc *pp; - if ((current_time - chuunits[unit]->lastupdate) > 150) { - chu_event(chuunits[unit], CEVNT_PROP); - } + pp = peer->procptr; + up = (struct chuunit *)pp->unitptr; + if (up->pollcnt == 0) + refclock_report(peer, CEVNT_TIMEOUT); + else + up->pollcnt--; } - - -/* - * chu_control - set fudge factors, return statistics - */ -static void -chu_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - register struct chuunit *chu; - U_LONG npolls; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "chu_control: unit %d invalid", unit); - return; - } - - if (in != 0) { - if (in->haveflags & CLK_HAVETIME1) - propagation_delay[unit] = in->fudgetime1; - if (in->haveflags & CLK_HAVETIME2) - fudgefactor[unit] = in->fudgetime2; - offset_fudge[unit] = propagation_delay[unit]; - L_ADD(&offset_fudge[unit], &fudgefactor[unit]); - 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 - */ - peer = chuunits[unit]->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, - CHUREFID, 4); - else - peer->refid = htonl(CHUHSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG1) { - sloppyclockflag[unit] = in->flags & CLK_FLAG1; - } - } - - if (out != 0) { - out->type = REFCLK_CHU; - out->flags = 0; - out->haveflags - = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1| - CLK_HAVEVAL2|CLK_HAVEFLAG1; - out->clockdesc = CHUDESCRIPTION; - out->fudgetime1 = propagation_delay[unit]; - out->fudgetime2 = fudgefactor[unit]; - out->fudgeval1 = (long)stratumtouse[unit]; - out->flags = sloppyclockflag[unit]; - if (unitinuse[unit]) { - chu = chuunits[unit]; - out->lencode = NCHUCHARS*4; - out->fudgeval2 = chu->lastcode[2*NCHUCHARS+1]; - out->fudgeval2 *= (chu->lastcode[2*NCHUCHARS]&1)?-1:1; - out->lastcode = chu->asciicode; - out->timereset = current_time - chu->timestarted; - npolls = out->timereset / 6; /* **divide** */ - out->polls = npolls; - out->noresponse = (npolls - chu->responses); - out->badformat = chu->badformat; - out->baddata = chu->baddata; - out->lastevent = chu->lastevent; - out->currentstatus = chu->status; - } else { - out->fudgeval2 = 0; - out->lencode = 0; - out->lastcode = ""; - out->polls = out->noresponse = 0; - out->badformat = out->baddata = 0; - out->timereset = 0; - out->currentstatus = out->lastevent = CEVNT_NOMINAL; - } - } -} #endif diff --git a/usr.sbin/xntpd/xntpd/refclock_conf.c b/usr.sbin/xntpd/xntpd/refclock_conf.c index 535ca27e9ebe..c0674d917311 100644 --- a/usr.sbin/xntpd/xntpd/refclock_conf.c +++ b/usr.sbin/xntpd/xntpd/refclock_conf.c @@ -15,95 +15,137 @@ static struct refclock refclock_none = { }; #ifdef LOCAL_CLOCK -extern struct refclock refclock_local; +extern struct refclock refclock_local; #else #define refclock_local refclock_none #endif #if defined(TRAK) || defined(TRAKCLK) || defined(TRAKPPS) -extern struct refclock refclock_trak; +extern struct refclock refclock_trak; #else #define refclock_trak refclock_none #endif -#if defined(PST) || defined(PSTCLK) || defined(PSTPPS) -extern struct refclock refclock_pst; +#if defined(PST) +extern struct refclock refclock_pst; #else #define refclock_pst refclock_none #endif -#if defined(CHU) || defined(CHUCLK) || defined(CHUPPS) -extern struct refclock refclock_chu; +#if defined(CHU) +extern struct refclock refclock_chu; #else #define refclock_chu refclock_none #endif #if defined(GOES) || defined(GOESCLK) || defined(GOESPPS) -extern struct refclock refclock_goes; +extern struct refclock refclock_goes; #else #define refclock_goes refclock_none #endif -#if defined(WWVB) || defined(WWVBCLK) || defined(WWVBPPS) -extern struct refclock refclock_wwvb; +#if defined(WWVB) +extern struct refclock refclock_wwvb; #else #define refclock_wwvb refclock_none #endif #if defined(PARSE) || defined(PARSEPPS) -extern struct refclock refclock_parse; +extern struct refclock refclock_parse; #else #define refclock_parse refclock_none #endif -#if defined(MX4200) || defined(MX4200CLK) || defined(MX4200PPS) -extern struct refclock refclock_mx4200; +#if defined(PPS) && (defined(MX4200) || defined(MX4200CLK) || defined(MX4200PPS)) +extern struct refclock refclock_mx4200; #else #define refclock_mx4200 refclock_none #endif -#if defined(AS2201) || defined(AS2201CLK) || defined(AS2201PPS) -extern struct refclock refclock_as2201; +#if defined(AS2201) +extern struct refclock refclock_as2201; #else #define refclock_as2201 refclock_none #endif #if defined(OMEGA) || defined(OMEGACLK) || defined(OMEGAPPS) -extern struct refclock refclock_omega; +extern struct refclock refclock_omega; #else #define refclock_omega refclock_none #endif -#ifdef TPRO -extern struct refclock refclock_tpro; +#if defined(TPRO) && defined(sun) /* XXX sun only */ +extern struct refclock refclock_tpro; #else #define refclock_tpro refclock_none #endif #if defined(LEITCH) || defined(LEITCHCLK) || defined(LEITCHPPS) -extern struct refclock refclock_leitch; +extern struct refclock refclock_leitch; #else #define refclock_leitch refclock_none #endif -#ifdef IRIG -extern struct refclock refclock_irig; +#if defined(IRIG) && defined(sun) /* XXX sun only */ +extern struct refclock refclock_irig; #else #define refclock_irig refclock_none #endif #if defined(MSFEESPPS) -extern struct refclock refclock_msfees; +extern struct refclock refclock_msfees; #else #define refclock_msfees refclock_none #endif #if defined(GPSTM) || defined(GPSTMCLK) || defined(GPSTMPPS) -extern struct refclock refclock_gpstm; +extern struct refclock refclock_gpstm; #else #define refclock_gpstm refclock_none #endif +#if defined(BANC) || defined(BANCCLK) || defined(BANCPPS) +extern struct refclock refclock_bancomm; +#else +#define refclock_bancomm refclock_none +#endif + +#ifdef DATUM +extern struct refclock refclock_datum; +#else +#define refclock_datum refclock_none +#endif + +#ifdef ACTS +extern struct refclock refclock_acts; +#else +#define refclock_acts refclock_none +#endif + +#ifdef HEATH +extern struct refclock refclock_heath; +#else +#define refclock_heath refclock_none +#endif + +#ifdef NMEA +extern struct refclock refclock_nmea; +#else +#define refclock_nmea refclock_none +#endif + +#ifdef MOTO +extern struct refclock refclock_moto; +#else +#define refclock_moto refclock_none +#endif + +#ifdef ATOM +extern struct refclock refclock_atom; +#else +#define refclock_atom refclock_none +#endif + /* * Order is clock_start(), clock_shutdown(), clock_poll(), * clock_control(), clock_init(), clock_buginfo, clock_flags; @@ -127,6 +169,15 @@ struct refclock *refclock_conf[] = { &refclock_leitch, /* 13 REFCLK_ATOM_LEITCH */ &refclock_msfees, /* 14 REFCLK_MSF_EES */ &refclock_gpstm, /* 15 REFCLK_GPSTM_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_moto, /* 21 REFCLK_GPS_MOTO */ + &refclock_atom, /* 22 REFCLK_ATOM_PPS */ + &refclock_none, /* 23 reserved */ + &refclock_none, /* 24 reserved */ }; u_char num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *); diff --git a/usr.sbin/xntpd/xntpd/refclock_datum.c b/usr.sbin/xntpd/xntpd/refclock_datum.c new file mode 100644 index 000000000000..825dee7ad7df --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_datum.c @@ -0,0 +1,871 @@ +/* +** 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. +*/ + +#if defined(REFCLOCK) && (defined(DATUM) || defined(DATUMCLK) || defined(DATUMPPS)) + +/* +** Include Files +*/ + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#include <sys/errno.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.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) + +/* +** External Variables +*/ + +extern u_long current_time; /* current time (s) - not really used */ +extern int debug; /* global debug flag - not relly used */ + +/* +** 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 xntp */ + struct refclockio io; /* io structure used by xntp */ + 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 FILE *logfile; /* log file for logging information */ +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 xntpd 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 *)); +static void datum_pts_init P((void)); +static void datum_pts_buginfo P((int, struct refclockbug *)); + +/* +** This is the call back function structure that xntpd 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 xntpd 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, xntpd +** 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 xntpd 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 xntpd 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 xntpd.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(unit, peer) + 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 + fprintf(logfile, "Starting Datum PTS unit %d\n", unit); + fflush(logfile); +#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 + fprintf(logfile,"Opening RS232 port with file descriptor %d\n", + datum_pts->PTS_fd); + fflush(logfile); +#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 then this program exits. You can change this +** if you want by editing this source. Please give the changes back to the +** xntp 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 + + syslog(LOG_ERR, "Datum_PTS: Exiting - Termios not supported in this driver"); + exit(1); + +#endif + +/* +** Initialize the xntpd IO structure +*/ + + datum_pts->peer = peer; + datum_pts->timestarted = current_time; + + 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 + fprintf(logfile,"Problem adding clock\n"); + fflush(logfile); +#endif + + syslog(LOG_ERR, "Datum_PTS: Problem adding clock"); + + } + + peer->precision = PTSPRECISION; + peer->rootdelay = 0; + peer->rootdispersion = 0; + 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(unit, peer) + int unit; + struct peer *peer; +{ + int i,j; + struct datum_pts_unit **temp_datum_pts_unit; + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Shutdown Datum PTS\n"); + fflush(logfile); +#endif + + syslog(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 + fprintf(logfile,"Error, could not shut down unit %d\n",unit); + fflush(logfile); +#endif + + syslog(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(unit, peer) + int unit; + struct peer *peer; +{ + int i; + int index; + int error_code; + struct datum_pts_unit *datum_pts; + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Poll Datum PTS\n"); + fflush(logfile); +#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 + fprintf(logfile,"Error, could not poll unit %d\n",unit); + fflush(logfile); +#endif + + syslog(LOG_ERR, "Datum_PTS: Could not poll unit %d",unit); + return; + + } + +} + + +/*......................................................................*/ +/* datum_pts_control - not used */ +/*......................................................................*/ + +static void datum_pts_control(unit, in, out) + int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Control Datum PTS\n"); + fflush(logfile); +#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() +{ + +/* */ +/*...... 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). +*/ + +#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS) +/* + logfile = fopen("xntpd.log", "w"); +*/ +#endif + + logfile = stdout; + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Init Datum PTS\n"); + fflush(logfile); +#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(unit, bug) + int unit; + register struct refclockbug *bug; +{ + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Buginfo Datum PTS\n"); + fflush(logfile); +#endif + +} + + +/*......................................................................*/ +/* datum_pts_receive - receive the time buffer that was read in */ +/* by the xntpd 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 xntpd clock_receive() routine which causes the systems */ +/* clock to be updated (several layers down). */ +/*......................................................................*/ + +static void datum_pts_receive(rbufp) + 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; + u_fp dispersion; + int goodtime; + +/* +** 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 + fprintf(logfile,"Receive Datum PTS: %d bytes\n", dpend); + fflush(logfile); +#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 +** xntpd, 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 + fprintf(logfile,"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); + fflush(logfile); +#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 + fprintf(logfile,"Error: bad clocktime\n"); + fprintf(logfile,"GMT %d, lastrec %d, yearstart %d, lastref %d\n", + tzoff, + datum_pts->lastrec.l_ui, + datum_pts->yearstart, + datum_pts->lastref.l_ui); + fflush(logfile); +#endif + + syslog(LOG_ERR, "Datum_PTS: Bad clocktime"); + + return; + + }else{ + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Good clocktime\n"); + fflush(logfile); +#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 */ + + dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */ + +#ifdef DEBUG_DATUM_PTC + ftimerr = dispersion; + ftimerr /= (1024.0 * 64.0); + fprintf(logfile,"dispersion = %d, %f\n", dispersion, ftimerr); + fflush(logfile); +#endif + +/* +** Pass the new time to xntpd 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. +*/ + + refclock_receive( datum_pts->peer, + &tstmp, + tzoff, + dispersion, + &datum_pts->lastrec, + &datum_pts->lastrec, + datum_pts->leap ); + +/* +** Compute sigma squared (not used currently). Maybe later, this could be +** used for the dispersion estimate. The problem is that xntpd 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 + fprintf(logfile,"Time error = %f seconds\n", ftimerr); + fflush(logfile); +#endif + +#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS) + fprintf(logfile, + "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); + fflush(logfile); +#endif + +} +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_goes.c b/usr.sbin/xntpd/xntpd/refclock_goes.c index 0b53326dc5d2..512131b8625d 100644 --- a/usr.sbin/xntpd/xntpd/refclock_goes.c +++ b/usr.sbin/xntpd/xntpd/refclock_goes.c @@ -1,9 +1,12 @@ /* - * refclock_goes - clock driver for the Kinimetrics Truetime GOES receiver - * Version 2.0 + * refclock_goes - clock driver for the Kinemetrics Truetime GOES + * 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 */ -#if defined(REFCLOCK) && (defined(GOES) || defined(GOESCLK) || defined(GOESPPS)) +#if defined(REFCLOCK) && defined(GOES) #include <stdio.h> #include <ctype.h> @@ -13,113 +16,102 @@ #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_unixtime.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(GOESCLK) -#include <clkdefs.h> -#endif /* GOESCLK */ -#endif /* STREAM */ - -#if defined (GOESPPS) -#include <sys/ppsclock.h> -#endif /* GOESPPS */ - #include "ntp_stdlib.h" /* * Support for Kinemetrics Truetime 468-DC GOES Receiver + * OM-DC OMEGA and GPS-TM/TMD support in progress... + * + * 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 + * Q Quality indication: indicates possible error of + * C - Carriage return + * L - Line feed * - * Most of this code is copied from refclock_goes.c with thanks. + * 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 time code looks like follows; Send the clock a R or C and once per - * second a timestamp will appear that looks like this: - * ADDD:HH:MM:SSQCL - * A - control A - * Q Quality indication: indicates possible error of - * ? +/- 500 milliseconds # +/- 50 milliseconds - * * +/- 5 milliseconds . +/- 1 millisecond - * space less than 1 millisecond - * C - Carriage return - * L - Line feed - * The carriage return start bit begins on 0 seconds and extends to 1 bit time. + * 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. + * + * Notes on the 468-DC receiver: + * + * Unless you live on 125 degrees west longitude, 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 * - * Unless you live on 125 degrees west longitude, 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 5 milliseconds retard needed. - * The software will ask the clock which satellite it sees. * - * Flag1 set to 1 will silence the clock side of xntpd, just reading the - * clock without trying to write to it. This is usefull if several - * xntpds listen to the same clock. This has not been tested yet... + * 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 = "GOES", see below) + * flag1 - will silence the clock side of xntpd, just reading the clock + * without trying to write to it. (default = 0) + * flag2 - not assigned + * flag3 - enable ppsclock streams module + * flag4 - not assigned + * */ /* * Definitions */ -#define MAXUNITS 4 /* max number of GOES units */ -#define GOES232 "/dev/goes%d" +#define DEVICE "/dev/goes%d" #define SPEED232 B9600 /* 9600 baud */ /* * Radio interface parameters */ -#define GOESMAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */ -#define GOESSKEWFACTOR 17 /* skew factor (for about 32 ppm) */ -#define GOESPRECISION (-10) /* precision assumed (about 1 ms) */ -#define GOESREFID "GOES" /* reference id */ -#define GOESDESCRIPTION "Kinemetrics GOES Receiver" /* who we are */ -#define GOESHSREFID 0x7f7f050a /* 127.127.5.10 refid hi strata */ -#define GMT 0 /* hour offset from Greenwich */ -#define NCODES 3 /* stages of median filter */ -#define LENGOES0 13 /* format 0 timecode length */ -#define LENGOES2 21 /* format 2 timecode length */ -#define FMTGOESU 0 /* unknown format timecode id */ -#define FMTGOES0 1 /* format 0 timecode id */ -#define FMTGOES2 2 /* format 2 timecode id */ -#define DEFFUDGETIME 0 /* default fudge time (ms) */ -#define BMAX 50 /* timecode buffer length */ -#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */ +#define MAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ +#define REFID "GOES" /* reference id */ +#define DESCRIPTION "TrueTime GPS/GOES Receivers" /* WRU */ +#define NSAMPLES 3 /* stages of median filter */ /* - * Tag which satellite we see + * Tags which station (satellite) we see */ -#define GOES_SAT_NONE 0 -#define GOES_SAT_WEST 1 -#define GOES_SAT_EAST 2 -#define GOES_SAT_STAND 3 +#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */ +#define GOES_EAST 1 /* until you discover otherwise */ /* - * Hack to avoid excercising the multiplier. I have no pride. + * used by the state machine */ -#define MULBY10(x) (((x)<<3) + ((x)<<1)) +enum goes_event {e_Init, e_F18, e_F50, e_F51, e_TS}; /* * Imported from the timer module */ -extern U_LONG current_time; -extern struct event timerqueue[]; - -/* - * Imported from ntp_loopfilter module - */ -extern int fdpps; /* pps file descriptor */ +extern u_long current_time; /* * Imported from ntpd module @@ -127,634 +119,294 @@ extern int fdpps; /* pps file descriptor */ extern int debug; /* global debug flag */ /* - * GOES unit control structure + * unit control structure */ struct goesunit { - struct peer *peer; /* associated peer structure */ - struct refclockio io; /* given to the I/O handler */ - l_fp lastrec; /* last receive time */ - l_fp lastref; /* last timecode time */ - l_fp offset[NCODES]; /* recent sample offsets */ - char lastcode[BMAX]; /* last timecode received */ - u_short satellite; /* which satellite we saw */ - u_short polled; /* Hand in a time sample? */ - u_char format; /* timecode format */ - u_char lencode; /* length of last timecode */ - U_LONG lasttime; /* last time clock heard from */ - 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 year; /* year of eternity */ - u_short day; /* day of year */ - u_char hour; /* hour of day */ - u_char minute; /* minute of hour */ - u_char second; /* seconds of minute */ - u_char leap; /* leap indicators */ - u_short msec; /* millisecond of second */ - u_char quality; /* quality char from format 2 */ - U_LONG yearstart; /* start of current year */ - /* - * 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 */ + int pollcnt; /* poll message counter */ + u_short station; /* which station we are on */ + u_short polled; /* Hand in a time sample? */ + enum {Base, Start, F18, F50, F51, F08} + State; /* State machine */ }; /* - * Data space for the unit structures. Note that we allocate these on - * the fly, but never give them back. - */ -static struct goesunit *goesunits[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 fudgefactor1[MAXUNITS]; -static l_fp fudgefactor2[MAXUNITS]; -static u_char stratumtouse[MAXUNITS]; -static u_char readonlyclockflag[MAXUNITS]; - -/* * Function prototypes */ -static void goes_init P((void)); -static int goes_start P((u_int, struct peer *)); -static void goes_shutdown P((int)); -static void goes_report_event P((struct goesunit *, int)); +static int goes_start P((int, struct peer *)); +static void goes_shutdown P((int, struct peer *)); static void goes_receive P((struct recvbuf *)); -static char goes_process P((struct goesunit *, l_fp *, u_fp *)); static void goes_poll P((int, struct peer *)); -static void goes_control P((u_int, struct refclockstat *, struct refclockstat *)); -static void goes_buginfo P((int, struct refclockbug *)); -static void goes_send P((struct goesunit *, char *)); - -struct refclock refclock_goes = { - goes_start, goes_shutdown, goes_poll, - goes_control, goes_init, goes_buginfo, NOFLAGS -}; +static void goes_send P((struct peer *, char *)); +static void goes_initstate P((struct peer *)); +static void goes_doevent P((struct peer *, enum goes_event)); /* - * goes_init - initialize internal goes driver data + * Transfer vector */ -static void -goes_init() -{ - register int i; - /* - * Just zero the data arrays - */ - memset((char *)goesunits, 0, sizeof goesunits); - memset((char *)unitinuse, 0, sizeof unitinuse); - - /* - * Initialize fudge factors to default. - */ - for (i = 0; i < MAXUNITS; i++) { - fudgefactor1[i].l_ui = 0; - fudgefactor1[i].l_uf = DEFFUDGETIME; - fudgefactor2[i].l_ui = 0; - fudgefactor2[i].l_uf = DEFFUDGETIME; - stratumtouse[i] = 0; - readonlyclockflag[i] = 0; - } -} +struct refclock refclock_goes = { + goes_start, /* start up driver */ + goes_shutdown, /* shut down driver */ + goes_poll, /* transmit poll message */ + noentry, /* not used (old goes_control) */ + noentry, /* initialize driver (not used) */ + noentry, /* not used (old goes_buginfo) */ + NOFLAGS /* not used */ +}; /* - * goes_start - open the GOES devices and initialize data for processing + * goes_start - open the devices and initialize data for processing */ static int goes_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { - register struct goesunit *goes; - register int i; - int fd232; - char goesdev[20]; - - /* - * Check configuration info - */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_start: unit %d invalid", unit); - return 0; - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "goes_start: unit %d in use", unit); - return 0; - } + register struct goesunit *up; + struct refclockproc *pp; + int fd; + char device[20]; /* * Open serial port */ - (void) sprintf(goesdev, GOES232, unit); - fd232 = open(goesdev, O_RDWR, 0777); - if (fd232 == -1) { - syslog(LOG_ERR, "goes_start: open of %s: %m", goesdev); - return 0; - } - -#if defined(HAVE_SYSV_TTYS) - /* - * System V serial line parameters (termio interface) - * - */ - { struct termio ttyb; - if (ioctl(fd232, TCGETA, &ttyb) < 0) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TCGETA): %m", goesdev); - 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) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TCSETA): %m", goesdev); - goto screwed; - } - } -#endif /* HAVE_SYSV_TTYS */ -#if defined(HAVE_TERMIOS) - /* - * POSIX serial line parameters (termios interface) - * - * The GOESCLK option provides timestamping at the driver level. - * It requires the tty_clk streams module. - * - * The GOESPPS 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 SunOS 4.1.1 or - * later. - */ - { struct termios ttyb, *ttyp; - ttyp = &ttyb; - - if (tcgetattr(fd232, ttyp) < 0) { - syslog(LOG_ERR, - "goes_start: tcgetattr(%s): %m", goesdev); - 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) { - syslog(LOG_ERR, - "goes_start: tcsetattr(%s): %m", goesdev); - goto screwed; - } - if (tcflush(fd232, TCIOFLUSH) < 0) { - syslog(LOG_ERR, - "goes_start: tcflush(%s): %m", goesdev); - goto screwed; - } - } -#endif /* HAVE_TERMIOS */ -#ifdef STREAM -#if defined(GOESCLK) - if (ioctl(fd232, I_PUSH, "clk") < 0) - syslog(LOG_ERR, - "goes_start: ioctl(%s, I_PUSH, clk): %m", goesdev); - if (ioctl(fd232, CLK_SETSTR, "\n") < 0) - syslog(LOG_ERR, - "goes_start: ioctl(%s, CLK_SETSTR): %m", goesdev); -#endif /* GOESCLK */ -#if defined(GOESPPS) - if (ioctl(fd232, I_PUSH, "ppsclock") < 0) - syslog(LOG_ERR, - "goes_start: ioctl(%s, I_PUSH, ppsclock): %m", goesdev); - else - fdpps = fd232; -#endif /* GOESPPS */ -#endif /* STREAM */ -#if defined(HAVE_BSD_TTYS) - /* - * 4.3bsd serial line parameters (sgttyb interface) - * - * The GOESCLK option provides timestamping at the driver level. - * It requires the tty_clk line discipline and 4.3bsd or later. - */ - { struct sgttyb ttyb; -#if defined(GOESCLK) - int ldisc = CLKLDISC; -#endif /* GOESCLK */ - - if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TIOCGETP): %m", goesdev); - goto screwed; - } - ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; -#if defined(GOESCLK) - 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 /* GOESCLK */ - if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TIOCSETP): %m", goesdev); - goto screwed; - } -#if defined(GOESCLK) - if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { - syslog(LOG_ERR, - "goes_start: ioctl(%s, TIOCSETD): %m",goesdev); - goto screwed; - } -#endif /* GOESCLK */ - } -#endif /* HAVE_BSD_TTYS */ - - /* - * Allocate unit structure - */ - if (goesunits[unit] != 0) { - goes = goesunits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < MAXUNITS; i++) { - if (!unitinuse[i] && goesunits[i] != 0) - break; - } - if (i < MAXUNITS) { - /* - * Reclaim this one - */ - goes = goesunits[i]; - goesunits[i] = 0; - } else { - goes = (struct goesunit *) - emalloc(sizeof(struct goesunit)); - } - } - memset((char *)goes, 0, sizeof(struct goesunit)); - goesunits[unit] = goes; + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) + return (0); /* - * Set up the structures + * Allocate and initialize unit structure */ - goes->peer = peer; - goes->unit = (u_char)unit; - goes->timestarted = current_time; - goes->satellite = GOES_SAT_NONE; - - goes->io.clock_recv = goes_receive; - goes->io.srcclock = (caddr_t)goes; - goes->io.datalen = 0; - goes->io.fd = fd232; - if (!io_addclock(&goes->io)) { - goto screwed; + if (!(up = (struct goesunit *) + emalloc(sizeof(struct goesunit)))) { + (void) close(fd); + return (0); } - - /* - * All done. Initialize a few random peer variables, then - * return success. - */ - peer->precision = GOESPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = 0; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, GOESREFID, 4); - else - peer->refid = htonl(GOESHSREFID); - unitinuse[unit] = 1; - return 1; - - /* - * Something broke; abandon ship - */ -screwed: - (void) close(fd232); - return 0; -} - -/* - * goes_shutdown - shut down a GOES clock - */ -static void -goes_shutdown(unit) - int unit; -{ - register struct goesunit *goes; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_shutdown: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "goes_shutdown: unit %d not in use", unit); - return; + memset((char *)up, 0, sizeof(struct goesunit)); + pp = peer->procptr; + pp->io.clock_recv = goes_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; /* - * Tell the I/O module to turn us off. We're history. + * Initialize miscellaneous variables */ - goes = goesunits[unit]; - io_closeclock(&goes->io); - unitinuse[unit] = 0; + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; +/* goes_initstate(peer);*/ + return (1); } /* - * goes_report_event - note the occurance of an event + * goes_shutdown - shut down the clock */ static void -goes_report_event(goes, code) - struct goesunit *goes; - int code; -{ +goes_shutdown(unit, peer) + int unit; struct peer *peer; +{ + register struct goesunit *up; + struct refclockproc *pp; - peer = goes->peer; - if (goes->status != (u_char)code) { - goes->status = (u_char)code; - if (code != CEVNT_NOMINAL) - goes->lastevent = (u_char)code; - syslog(LOG_INFO, - "clock %s event %x\n", ntoa(&peer->srcadr), code); -#ifdef DEBUG - if (debug) { - printf("goes_report_event(goes%d, code %d)\n", - goes->unit, code); - } -#endif - } + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); } /* - * goes_receive - receive data from the serial interface on a Kinimetrics - * clock + * goes_receive - receive data from the serial interface on a + * Kinimetrics clock */ static void goes_receive(rbufp) struct recvbuf *rbufp; { - register int i; - register struct goesunit *goes; - register u_char *dpt; - register char *cp; - register u_char *dpend; - l_fp tstmp; - u_fp dispersion; + register struct goesunit *up; + struct refclockproc *pp; + struct peer *peer; + l_fp tmp_l_fp; + u_short new_station; + char sync, c1, c2; + int i; + int lat, lon, off; /* GOES Satellite position */ /* - * Get the clock this applies to and a pointers to the data + * Get the clock this applies to and pointers to the data */ - goes = (struct goesunit *)rbufp->recv_srcclock; - dpt = (u_char *)&rbufp->recv_space; + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; /* - * Edit timecode to remove control chars + * Read clock output. Automatically handles STREAMS, CLKLDISC */ - dpend = dpt + rbufp->recv_length; - cp = goes->lastcode; - while (dpt < dpend) { - if ((*cp = 0x7f & *dpt++) >= ' ') cp++; -#ifdef GOESCLK - else if (*cp == '\r') { - if (dpend - dpt < 8) { - /* short timestamp */ - return; - } - if (!buftvtots(dpt,&goes->lastrec)) { - /* screwy timestamp */ - return; - } - dpt += 8; - } -#endif - } - *cp = '\0'; - goes->lencode = cp - goes->lastcode; - if (goes->lencode == 0) return; -#ifndef GOESCLK - goes->lastrec = rbufp->recv_time; -#endif /* GOESCLK */ - tstmp = goes->lastrec; - -#ifdef DEBUG - if (debug) - printf("goes: timecode %d %s\n", - goes->lencode, goes->lastcode); -#endif + pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, + &pp->lastrec); /* - * We get down to business, check the timecode format and decode - * its contents. This code checks for and decodes both format 0 - * and format 2 and need not be told which in advance. + * There is a case where <cr><lf> generates 2 timestamps */ - cp = goes->lastcode; - goes->leap = 0; - goes->format = FMTGOESU; - if (goes->lencode == LENGOES0) { + if (pp->lencode == 0) + return; - /* - * Check timecode format 0 - */ - if (!isdigit(cp[0]) || /* day of year */ - !isdigit(cp[1]) || - !isdigit(cp[2]) || - cp[3] != ':' || /* <sp> */ - !isdigit(cp[4]) || /* hours */ - !isdigit(cp[5]) || - cp[6] != ':' || /* : separator */ - !isdigit(cp[7]) || /* minutes */ - !isdigit(cp[8]) || - cp[9] != ':' || /* : separator */ - !isdigit(cp[10]) || /* seconds */ - !isdigit(cp[11])) { - goes->badformat++; - goes_report_event(goes, CEVNT_BADREPLY); - return; - } - else goes->format = FMTGOES0; + up->pollcnt = 2; + record_clock_stats(&peer->srcadr, pp->lastcode); - /* - * Convert format 0 and check values - */ - goes->year = 0; /* fake */ - goes->day = cp[0] - '0'; - goes->day = MULBY10(goes->day) + cp[1] - '0'; - goes->day = MULBY10(goes->day) + cp[2] - '0'; - goes->hour = MULBY10(cp[4] - '0') + cp[5] - '0'; - goes->minute = MULBY10(cp[7] - '0') + cp[8] - '0'; - goes->second = MULBY10(cp[10] - '0') + cp[11] - '0'; - goes->msec = 0; - - if (cp[12] != ' ' && cp[12] != '.' && cp[12] != '*') - goes->leap = LEAP_NOTINSYNC; - else - goes->lasttime = current_time; - - if (goes->day < 1 || goes->day > 366) { - goes->baddata++; - goes_report_event(goes, CEVNT_BADDATE); - return; - } - if (goes->hour > 23 || goes->minute > 59 - || goes->second > 59) { - goes->baddata++; - goes_report_event(goes, CEVNT_BADTIME); - return; - } + /* + * 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. + */ - } else if (goes->lencode == LENGOES2) { + /* + * Timecode: "nnnnn+nnn-nnn" + */ + if (sscanf(pp->lastcode, "%5d%c%3d%c%3d", + &lon, &c1, &lat, &c2, &off) == 5 && + (c1 == '+' || c1 == '-') && + (c2 == '+' || c2 == '-')) { /* - * Extended precision satelite location info + * This is less than perfect. Call the (satellite) + * either EAST or WEST and adjust slop accodingly + * Perfectionists would recalcuted the exact delay + * and adjust accordingly... */ - if (!isdigit(cp[0]) || /* longitude */ - !isdigit(cp[1]) || - !isdigit(cp[2]) || - cp[3] != '.' || - !isdigit(cp[4]) || - !isdigit(cp[5]) || - !isdigit(cp[6]) || - !isdigit(cp[7]) || - (cp[8] != '+' && cp[8] != '-') || - !isdigit(cp[9]) || /*latitude */ - cp[10] != '.' || - !isdigit(cp[11]) || - !isdigit(cp[12]) || - !isdigit(cp[13]) || - !isdigit(cp[14]) || - (cp[15] != '+' && cp[15] != '-') || - !isdigit(cp[16]) || /* height */ - !isdigit(cp[17]) || - !isdigit(cp[18]) || - cp[19] != '.' || - !isdigit(cp[20])) { - goes->badformat++; - goes_report_event(goes, CEVNT_BADREPLY); - return; + if (lon > 7000 && lon < 14000) { + if (lon < 10000) + new_station = GOES_EAST; + else + new_station = GOES_WEST; +#ifdef DEBUG + if (debug) { + if (new_station == GOES_EAST) + printf("goes: station EAST\n"); + if (new_station == GOES_WEST) + printf("goes: station WEST\n"); } - else goes->format = FMTGOES2; - - /* - * Figure out which satellite this is. - * This allows +-5 degrees from nominal. - */ - if (cp[0] == '1' && (cp[1] == '3' || cp[1] == '2')) - goes->satellite = GOES_SAT_WEST; - else if (cp[0] == '1' && cp[1] == '0') - goes->satellite = GOES_SAT_STAND; - else if (cp[0] == '0' && cp[1] == '7') - goes->satellite = GOES_SAT_EAST; - else - goes->satellite = GOES_SAT_NONE; - +#endif + if (new_station != up->station) { + tmp_l_fp = pp->fudgetime1; + pp->fudgetime1 = pp->fudgetime2; + pp->fudgetime2 = tmp_l_fp; + up->station = new_station; + } + } + else { + refclock_report(peer, CEVNT_BADREPLY); #ifdef DEBUG - if (debug) - printf("goes_receive: select satellite %d\n", - goes->satellite); + if (debug) + printf("goes: station UNKNONW\n"); #endif - + } /* - * Switch back to on-second time codes. + * Switch back to on-second time codes and return. */ - goes_send(goes,"C"); + goes_send(peer, "C"); - /* - * Since this is not a time code, just return... - */ - return; - } else { - goes_report_event(goes, CEVNT_BADREPLY); return; } /* - * The clock will blurt a timecode every second but we only - * want one when polled. If we havn't been polled, bail out. + * Timecode: "Fnn" */ - if (!goes->polled) - return; + if (sscanf(pp->lastcode, "F%2d", &i) == 1 && + i > 0 && i < 80) { + enum goes_event event = 0; + + if (i == 50) event = e_F50; + if (i == 51) event = e_F51; + if (i == 50 || i == 51) { + goes_doevent(peer, event); + return; + } + } /* - * Now, compute the reference time value. Use the heavy - * machinery for the seconds and the millisecond field for the - * fraction when present. - * - * this code does not yet know how to do the years + * Timecode:" TRUETIME Mk III" */ - tstmp = goes->lastrec; - if (!clocktime(goes->day, goes->hour, goes->minute, - goes->second, GMT, tstmp.l_ui, - &goes->yearstart, &goes->lastref.l_ui)) { - goes->baddata++; - goes_report_event(goes, CEVNT_BADTIME); + if (strcmp(pp->lastcode, " TRUETIME Mk III") == 0) { + enum goes_event event; + + event = e_F18; + goes_doevent(peer, event); return; } - MSUTOTSF(goes->msec, goes->lastref.l_uf); - /* - * Slop the read value by fudgefactor1 or fudgefactor2 depending - * on which satellite we are viewing last time we checked. - */ + * Timecode: "ddd:hh:mm:ssQ" + */ + if (sscanf(pp->lastcode, "%3d:%2d:%2d:%2d%c", + &pp->day, &pp->hour, &pp->minute, + &pp->second, &sync) == 5) { -#ifdef DEBUG - if (debug) - printf("GOES_RECEIVE: Slopping for satellite %d\n", - goes->satellite); -#endif - if (goes->satellite == GOES_SAT_WEST) - L_ADD(&goes->lastref, &fudgefactor1[goes->unit]); - else if (goes->satellite == GOES_SAT_EAST) - L_ADD(&goes->lastref, &fudgefactor2[goes->unit]); -/* else if (goes->satellite == GOES_SAT_STAND) - L_ADD(&goes->lastref, &((fudgefactor1[goes->unit] + - fudgefactor2[goes->unit]) / 2)); */ - - i = ((int)(goes->coderecv)) % NCODES; - goes->offset[i] = goes->lastref; - L_SUB(&goes->offset[i], &tstmp); - if (goes->coderecv == 0) - for (i = 1; i < NCODES; i++) - goes->offset[i] = goes->offset[0]; - - goes->coderecv++; + /* + * Adjust the synchronize indicator according to timecode + */ + if (sync !=' ' && sync !='.' && sync !='*') + pp->leap = LEAP_NOTINSYNC; + else { + pp->leap = 0; + pp->lasttime = current_time; + } - /* - * Check the satellite position - */ - goes_send(goes,"E"); + /* goes_doevent(peer, e_TS); */ + + /* + * 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; + + /* + * After each poll, check the station (satellite) + */ + goes_send(peer, "P"); + + /* + * 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, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->leap); + + /* + * We have succedded in answering the poll. + * Turn off the flag and return + */ + up->polled = 0; - /* - * Process the median filter, add the fudge factor and pass the - * offset and dispersion along. 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 (!goes_process(goes, &tstmp, &dispersion)) { - goes->baddata++; - goes_report_event(goes, CEVNT_BADTIME); return; } - refclock_receive(goes->peer, &tstmp, GMT, dispersion, - &goes->lastrec, &goes->lastrec, goes->leap); /* - * We have succedded in answering the poll. Turn off the flag + * No match to known timecodes, report failure and return */ - goes->polled = 0; + refclock_report(peer, CEVNT_BADREPLY); + return; } @@ -762,95 +414,89 @@ goes_receive(rbufp) * goes_send - time to send the clock a signal to cough up a time sample */ static void -goes_send(goes,cmd) - struct goesunit *goes; +goes_send(peer, cmd) + struct peer *peer; char *cmd; { - if (!readonlyclockflag[goes->unit]) { - /* - * Send a command to the clock. C for on-second timecodes. - * E for extended resolution satelite postion information. - */ - if (write(goes->io.fd, cmd, 1) != 1) { - syslog(LOG_ERR, "goes_send: unit %d: %m", goes->unit); - goes_report_event(goes, CEVNT_FAULT); - } else { - goes->polls++; - } + struct refclockproc *pp; + register int len = strlen(cmd); + + pp = peer->procptr; + if (!pp->sloppyclockflag & CLK_FLAG1) { +#ifdef DEBUG + if (debug) + printf("goes: Send '%s'\n", cmd); +#endif + if (write(pp->io.fd, cmd, len) != len) { + refclock_report(peer, CEVNT_FAULT); + } else { + pp->polls++; + } } } + /* - * goes_process - process a pile of samples from the clock + * state machine for initializing the clock */ -static char -goes_process(goes, offset, dispersion) - struct goesunit *goes; - l_fp *offset; - u_fp *dispersion; +static void +goes_doevent(peer, event) + struct peer *peer; + enum goes_event event; { - register int i, j; - register U_LONG tmp_ui, tmp_uf; - int not_median1 = -1; /* XXX correct? */ - int not_median2 = -1; /* XXX correct? */ - int median; - u_fp disp_tmp, disp_tmp2; + struct goesunit *up; + struct refclockproc *pp; - /* - * This code implements a three-stage median filter. First, we - * check if the samples are within 125 ms of each other. If not, - * dump the sample set. We take the median of the three offsets - * and use that as the sample offset. We take the maximum - * difference and use that as the sample dispersion. There - * probably is not much to be gained by a longer filter, since - * the clock filter in ntp_proto should do its thing. - */ - disp_tmp2 = 0; - for (i = 0; i < NCODES-1; i++) { - for (j = i+1; j < NCODES; j++) { - tmp_ui = goes->offset[i].l_ui; - tmp_uf = goes->offset[i].l_uf; - M_SUB(tmp_ui, tmp_uf, goes->offset[j].l_ui, - goes->offset[j].l_uf); - if (M_ISNEG(tmp_ui, tmp_uf)) { - M_NEG(tmp_ui, tmp_uf); - } - if (tmp_ui != 0 || tmp_uf > CODEDIFF) { - return 0; - } - disp_tmp = MFPTOFP(0, tmp_uf); - if (disp_tmp > disp_tmp2) { - disp_tmp2 = disp_tmp; - not_median1 = i; - not_median2 = j; - } - } + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; + +#ifdef DEBUG + if (debug) { + printf("goes_doevent: %d\n", (int)event); + } +#endif + if (event == e_TS && up->State != F51 && up->State != F08) { + goes_send(peer, "\03\r"); } - /* - * It seems as if all are within 125 ms of each other. - * Now to determine the median of the three. Whlie the - * 125 ms check was going on, we also subtly catch the - * dispersion and set-up for a very easy median calculation. - * The largest difference between any two samples constitutes - * the dispersion. The sample not involve in the dispersion is - * the median sample. EASY! - */ - if (goes->lasttime == 0 || disp_tmp2 > GOESMAXDISPERSE) - disp_tmp2 = GOESMAXDISPERSE; - if (not_median1 == 0) { - if (not_median2 == 1) - median = 2; - else - median = 1; - } else { - median = 0; - } - *offset = goes->offset[median]; - *dispersion = disp_tmp2; - return 1; + switch (event) { + case e_Init: + goes_send(peer, "F18\r"); + up->State = Start; + break; + case e_F18: + goes_send(peer, "F50\r"); + up->State = F18; + break; + case e_F50: + goes_send(peer, "F51\r"); + up->State = F50; + break; + case e_F51: + goes_send(peer, "F08\r"); + up->State = F51; + break; + case e_TS: + /* nothing to send - we like this mode */ + up->State = F08; + break; + } +} + +static void +goes_initstate(peer) + struct peer *peer; +{ + struct goesunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; + up->State = Base; /* just in case */ + goes_doevent(peer, e_Init); } + /* * goes_poll - called by the transmit procedure */ @@ -859,155 +505,29 @@ goes_poll(unit, peer) int unit; struct peer *peer; { - struct goesunit *goes; + struct goesunit *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. */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_poll: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "goes_poll: unit %d not in use", unit); - return; - } - goes = goesunits[unit]; - if ((current_time - goes->lasttime) > 150) { - goes->noreply++; - goes_report_event(goesunits[unit], CEVNT_TIMEOUT); + pp = peer->procptr; + up = (struct goesunit *)pp->unitptr; + if (up->pollcnt == 0) { + refclock_report(peer, CEVNT_TIMEOUT); + goes_send(peer, "C"); } + else + up->pollcnt--; /* - * polled every 64 seconds. Ask GOES_RECEIVE to hand in a timestamp. + * polled every 64 seconds. Ask goes_receive to hand in a + * timestamp. */ - goes->polled = 1; - goes->polls++; - - goes_send(goes,"C"); -} - -/* - * goes_control - set fudge factors, return statistics - */ -static void -goes_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - register struct goesunit *goes; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_control: unit %d invalid", unit); - return; - } - goes = goesunits[unit]; - - if (in != 0) { - if (in->haveflags & CLK_HAVETIME1) - fudgefactor1[unit] = in->fudgetime1; - if (in->haveflags & CLK_HAVETIME2) - fudgefactor2[unit] = in->fudgetime2; - 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 - */ - peer = goes->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, - GOESREFID, 4); - else - peer->refid = htonl(GOESHSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG1) { - readonlyclockflag[unit] = in->flags & CLK_FLAG1; - } - } - - if (out != 0) { - out->type = REFCLK_GOES_TRUETIME; - out->haveflags - = CLK_HAVETIME1|CLK_HAVETIME2| - CLK_HAVEVAL1|CLK_HAVEVAL2| - CLK_HAVEFLAG1|CLK_HAVEFLAG2; - out->clockdesc = GOESDESCRIPTION; - out->fudgetime1 = fudgefactor1[unit]; - out->fudgetime2 = fudgefactor2[unit]; - out->fudgeval1 = (LONG)stratumtouse[unit]; - out->fudgeval2 = 0; - out->flags = readonlyclockflag[unit] | - (goes->satellite << 1); - if (unitinuse[unit]) { - out->lencode = goes->lencode; - out->lastcode = goes->lastcode; - out->timereset = current_time - goes->timestarted; - out->polls = goes->polls; - out->noresponse = goes->noreply; - out->badformat = goes->badformat; - out->baddata = goes->baddata; - out->lastevent = goes->lastevent; - out->currentstatus = goes->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; - } - } + up->polled = 1; + pp->polls++; } -/* - * goes_buginfo - return clock dependent debugging info - */ -static void -goes_buginfo(unit, bug) - int unit; - register struct refclockbug *bug; -{ - register struct goesunit *goes; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "goes_buginfo: unit %d invalid", unit); - return; - } - - if (!unitinuse[unit]) - return; - goes = goesunits[unit]; - - bug->nvalues = 11; - bug->ntimes = 5; - if (goes->lasttime != 0) - bug->values[0] = current_time - goes->lasttime; - else - bug->values[0] = 0; - bug->values[1] = (U_LONG)goes->reason; - bug->values[2] = (U_LONG)goes->year; - bug->values[3] = (U_LONG)goes->day; - bug->values[4] = (U_LONG)goes->hour; - bug->values[5] = (U_LONG)goes->minute; - bug->values[6] = (U_LONG)goes->second; - bug->values[7] = (U_LONG)goes->msec; - bug->values[8] = goes->noreply; - bug->values[9] = goes->yearstart; - bug->values[10] = goes->quality; - bug->stimes = 0x1c; - bug->times[0] = goes->lastref; - bug->times[1] = goes->lastrec; - bug->times[2] = goes->offset[0]; - bug->times[3] = goes->offset[1]; - bug->times[4] = goes->offset[2]; -} #endif diff --git a/usr.sbin/xntpd/xntpd/refclock_gpstm.c b/usr.sbin/xntpd/xntpd/refclock_gpstm.c index 93abcf93773c..71eb015cb14b 100644 --- a/usr.sbin/xntpd/xntpd/refclock_gpstm.c +++ b/usr.sbin/xntpd/xntpd/refclock_gpstm.c @@ -78,13 +78,11 @@ * Radio interface parameters */ #define MAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */ -#define PRECISION (-20) /* precision assumed (about 1 ms) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ #define REFID "GPS\0" /* reference id */ #define DESCRIPTION "Kinemetrics GPS-TM/TMD Receiver" /* who we are */ -#define HSREFID 0x7f7f0f0a /* 127.127.15.10 refid hi strata */ #define GMT 0 /* hour offset from Greenwich */ #define NCODES 3 /* stages of median filter */ -#define BMAX 99 /* timecode buffer length */ #define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */ #define TIMEOUT 180 /* ping the clock if it's silent this long */ @@ -93,7 +91,6 @@ */ enum gpstm_event {e_Init, e_F18, e_F50, e_F51, e_TS}; static enum {Base, Start, F18, F50, F51, F08} State[MAXUNITS]; -static time_t Last[MAXUNITS]; static void gpstm_doevent P((int, enum gpstm_event)); static void gpstm_initstate P((int)); @@ -143,7 +140,7 @@ struct gpstm_unit { u_char leap; /* leap indicators */ u_short msec; /* millisecond of second */ u_char quality; /* quality character */ - U_LONG yearstart; /* start of current year */ + u_long yearstart; /* start of current year */ /* * Status tallies */ @@ -170,18 +167,19 @@ static l_fp fudgefactor1[MAXUNITS]; static l_fp fudgefactor2[MAXUNITS]; static u_char stratumtouse[MAXUNITS]; static u_char readonlyclockflag[MAXUNITS]; +static U_LONG refid[MAXUNITS]; /* * Function prototypes */ static void gpstm_init P((void)); -static int gpstm_start P((u_int, struct peer *)); -static void gpstm_shutdown P((int)); +static int gpstm_start P((int, struct peer *)); +static void gpstm_shutdown P((int, struct peer *)); static void gpstm_rep_event P((struct gpstm_unit *, int)); static void gpstm_receive P((struct recvbuf *)); static char gpstm_process P((struct gpstm_unit *, l_fp *, u_fp *)); static void gpstm_poll P((int, struct peer *)); -static void gpstm_control P((u_int, struct refclockstat *, +static void gpstm_control P((int, struct refclockstat *, struct refclockstat *)); static void gpstm_buginfo P((int, struct refclockbug *)); static void gpstm_send P((struct gpstm_unit *, char *)); @@ -214,6 +212,7 @@ gpstm_init() fudgefactor2[i].l_uf = 0; stratumtouse[i] = 0; readonlyclockflag[i] = 0; + memcpy((char *)&refid[i], REFID, 4); } } @@ -223,7 +222,7 @@ gpstm_init() */ static int gpstm_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { register struct gpstm_unit *gpstm; @@ -417,10 +416,7 @@ gpstm_start(unit, peer) peer->rootdelay = 0; peer->rootdispersion = 0; peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, REFID, 4); - else - peer->refid = htonl(HSREFID); + peer->refid = refid[unit]; unitinuse[unit] = 1; gpstm_initstate(unit); return 1; @@ -437,8 +433,9 @@ screwed: * gpstm_shutdown - shut down a clock */ static void -gpstm_shutdown(unit) +gpstm_shutdown(unit, peer) int unit; + struct peer *peer; { register struct gpstm_unit *gpstm; @@ -779,7 +776,9 @@ gpstm_doevent(unit, event) } static void -gpstm_initstate(unit) { +gpstm_initstate(unit) + int unit; + { State[unit] = Base; /* just in case */ gpstm_doevent(unit, e_Init); } @@ -897,7 +896,7 @@ gpstm_poll(unit, peer) */ static void gpstm_control(unit, in, out) - u_int unit; + int unit; struct refclockstat *in; struct refclockstat *out; { @@ -907,48 +906,40 @@ gpstm_control(unit, in, out) syslog(LOG_ERR, "gpstm_control: unit %d invalid", unit); return; } - gpstm = gpstm_units[unit]; if (in != 0) { if (in->haveflags & CLK_HAVETIME1) fudgefactor1[unit] = in->fudgetime1; if (in->haveflags & CLK_HAVETIME2) fudgefactor2[unit] = in->fudgetime2; - 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 - */ - peer = gpstm->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, - REFID, 4); - else - peer->refid = htonl(HSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG1) { + if (in->haveflags & CLK_HAVEVAL1) + stratumtouse[unit] = (u_char)(in->fudgeval1); + if (in->haveflags & CLK_HAVEVAL2) + refid[unit] = in->fudgeval2; + if (in->haveflags & CLK_HAVEFLAG1) readonlyclockflag[unit] = in->flags & CLK_FLAG1; + if (unitinuse[unit]) { + struct peer *peer; + + peer = gpstm_units[unit]->peer; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; } } if (out != 0) { + memset((char *)out, 0, sizeof (struct refclockstat)); out->type = REFCLK_GPSTM_TRUETIME; - out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 - | CLK_HAVEVAL1 | CLK_HAVEVAL2 - | CLK_HAVEFLAG1; + out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 | + CLK_HAVEVAL2 | CLK_HAVEFLAG1; out->clockdesc = DESCRIPTION; out->fudgetime1 = fudgefactor1[unit]; out->fudgetime2 = fudgefactor2[unit]; out->fudgeval1 = (LONG)stratumtouse[unit]; - out->fudgeval2 = 0; + out->fudgeval2 = refid[unit]; out->flags = readonlyclockflag[unit]; if (unitinuse[unit]) { + gpstm = gpstm_units[unit]; out->lencode = gpstm->lencode; out->lastcode = gpstm->lastcode; out->timereset = current_time - gpstm->timestarted; @@ -958,13 +949,6 @@ gpstm_control(unit, in, out) out->baddata = gpstm->baddata; out->lastevent = gpstm->lastevent; out->currentstatus = gpstm->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; } } } diff --git a/usr.sbin/xntpd/xntpd/refclock_heath.c b/usr.sbin/xntpd/xntpd/refclock_heath.c new file mode 100644 index 000000000000..1fbb9b7e6876 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_heath.c @@ -0,0 +1,393 @@ +/* + * refclock_heath - clock driver for Heath GC-1000 Most Accurate Clock + */ +#if defined(REFCLOCK) && defined(HEATH) + +#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 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 NSAMPLES 3 /* stages of median filter */ +#define LENHEATH 23 /* min timecode length */ + +/* + * Imported from ntp_timer module + */ +extern u_long current_time; /* current time (s) */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * Tables to compute the ddd of year form icky dd/mm timecode. Viva la + * leap. + */ +static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static 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 */ +}; + + +/* + * heath_start - open the devices and initialize data for processing + */ +static int +heath_start(unit, peer) + 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; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; + return (1); +} + + +/* + * heath_shutdown - shut down the clock + */ +static void +heath_shutdown(unit, peer) + 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(rbufp) + 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->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; + record_clock_stats(&peer->srcadr, pp->lastcode); +#ifdef DEBUG + if (debug) + printf("heath: timecode %d %s\n", pp->lencode, + pp->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->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, then add the local + * time correction (in hours east of Greenwich) from + * fudgetime2. + */ + switch (a[1]) { + case 'P': + if (pp->hour < 12) + pp->hour += 12; + break; + + case 'A': + if (pp->hour == 12) + pp->hour -= 12; + break; + } + i = (int)pp->hour - (int)pp->fudgetime2.l_ui; + if (i < 0) + i += 24; + pp->hour = i % 24; + + /* + * 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. + */ + 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(dsec)) { + pp->leap = LEAP_NOTINSYNC; + } else { + pp->leap = 0; + pp->lasttime = current_time; + pp->msec = (dsec - '0') * 100; + } + + /* + * 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, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->leap); +} + + +/* + * heath_poll - called by the transmit procedure + */ +static void +heath_poll(unit, peer) + 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; + if (up->pollcnt == 0) + refclock_report(peer, CEVNT_TIMEOUT); + else + up->pollcnt--; + 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); + gettstamp(&up->tstamp); + ioctl(pp->io.fd, TIOCMBIS, (char *)&bits); + +} + +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_irig.c b/usr.sbin/xntpd/xntpd/refclock_irig.c index 6167af2784ee..bc93fd74a918 100644 --- a/usr.sbin/xntpd/xntpd/refclock_irig.c +++ b/usr.sbin/xntpd/xntpd/refclock_irig.c @@ -1,8 +1,8 @@ /* * refclock_irig - clock driver for the IRIG audio decoder */ +#if defined(REFCLOCK) && defined(IRIG) && defined(sun) -#if defined(REFCLOCK) && defined(IRIG) #include <stdio.h> #include <ctype.h> #include <sys/time.h> @@ -19,72 +19,72 @@ * This driver supports the IRIG audio decoder. This clever gadget uses * a modified BSD audio driver for the Sun SPARCstation which provides * a timestamp, raw binary timecode, status byte and decoded ASCII - * timecode. The data are represented in the structure: + * timecode. The data are represented in the structure in the + * sys/bsd_audioirig.h header file: * * struct irig_time { - * struct timeval stamp; timestamp - * u_char bits[13]; 100 irig data bits - * u_char status; status byte - * char time[14]; time string (null terminated) + * struct timeval stamp; timestamp + * u_char bits[13]; 100 IRIG data bits + * u_char status; status byte + * char time[14]; time string (null terminated) * * where stamp represents a timestamp at the zero crossing of the index * marker at the second's epoch, bits is a 13-octet, zero-padded binary- * coded string representing code elements 1 through 100 in the IRIG-B - * code format and status is a status bute, The decoded timestamp is a + * code format, and status is a status bute, The decoded timestamp is a * 13-octet, null-terminated ASCII string "ddd hh:mm:ss*", where ddd is - * the day of year, hh:mm:ss the time of day and * is a status indicator, - * with " " indicating valid time and "?" indicating invalid time. The - * timestamp is in unix timeval format, consisting of two 32-bit - * longwords, the first of which is the seconds since 1970 and the second - * is the fraction of the second in microseconds. The status byte is zero + * the day of year, hh:mm:ss the time of day and * is a status + * indicator, with " " indicating valid time and "?" indicating + * something wrong. + * + * The timestamp is in Unix timeval format, consisting of two 32-bit + * words, the first of which is the seconds since 1970 and the second is + * the fraction of the second in microseconds. The status byte is zero * if (a) the input signal is within amplitude tolerances, (b) the raw * binary timecode contains only valid code elements, (c) 11 position - * identifiers have been found at the expected element positions, (d) the - * clock status byte contained in the timecode is valid, and (e) a time - * determination has been made since the last read() system call. + * identifiers have been found at the expected element positions, (d) + * the clock status byte contained in the timecode is valid, and (e) a + * time determination has been made since the last read() system call. * * The 100 elements of the IRIG-B timecode are numbered from 0 through * 99. Position identifiers occur at elements 0, 9, 19 and every ten - * thereafter to 99. The control function elements begin at element 50, - * which is control-field element 1, and extend to element 78, which is - * control-field element 27. The control functions have different - * interpretations in various devices. The straight-binary-seconds(SBS) - * field begins at element 80 and is 17 bits long. + * thereafter to 99. The control function (CF) elements begin at element + * 50 (CF 1) and extend to element 78 (CF 27). The straight-binary- + * seconds (SBS) field, which encodes the seconds of the UTC day, begins + * at element 80 (CF 28) and extends to element 97 (CF 44). The encoding + * of elements 50 (CF 1) through 78 (CF 27) is device dependent. This + * driver presently does not use the CF elements. + * + * Where feasible, the interface should be operated with signature + * control, so that, if the IRIG signal is lost or malformed, the + * interface produces an unmodulated signal, rather than possibly random + * digits. The driver will declare "unsynchronized" in this case. * * Spectracom Netclock/2 WWVB Synchronized Clock - * 6 time sync status - * 10-13 bcd year units - * 15-18 bcd year tens * - * Austron 2201A GPS Receiver (speculative) + * Element CF Function + * ------------------------------------- + * 55 6 time sync status + * 60-63 10-13 bcd year units + * 65-68 15-18 bcd year tens + * */ /* - * Definitions + * IRIG interface definitions */ -#define MAXUNITS 1 /* max number of irig units */ -#define IRIGFD "/dev/irig" /* name of driver device */ +#define DEVICE "/dev/irig%d" /* device name and unit */ +#define PRECISION (-13) /* precision assumed (100 us) */ +#define REFID "IRIG" /* reference ID */ +#define DESCRIPTION "IRIG Audio Decoder" /* WRU */ -/* - * IRIG interface parameters. - */ -#define IRIGPRECISION (-20) /* precision assumed (1 us) */ -#define IRIGREFID "IRIG" /* reference id */ -#define IRIGDESCRIPTION "IRIG audio decoder" /* who we are */ -#define IRIGHSREFID 0x7f7f0c0a /* 127.127.6.10 refid hi strata */ -#define GMT 0 /* hour offset from Greenwich */ +#define NSAMPLES 3 /* stages of median filter */ #define IRIG_FORMAT 1 /* IRIG timestamp format */ -#define BMAX 40 /* length of decoded timecode */ - -/* - * Hack to avoid excercising the multiplier. I have no pride. - */ -#define MULBY10(x) (((x)<<3) + ((x)<<1)) /* * Imported from ntp_timer module */ -extern U_LONG current_time; /* current time (s) */ +extern u_long current_time; /* current time (s) */ /* * Imported from ntpd module @@ -92,282 +92,115 @@ extern U_LONG current_time; /* current time (s) */ extern int debug; /* global debug flag */ /* - * irig unit control structure. - */ -struct irigunit { - struct peer *peer; /* associated peer structure */ - struct refclockio io; /* given to the I/O handler */ - l_fp lastrec; /* last local time */ - l_fp lastref; /* last timecode time */ - char lastcode[BMAX]; /* decoded timecode */ - char bincode[BMAX]; /* raw irig message */ - int lencode; /* lengthof last timecode */ - U_LONG lasttime; /* last time clock heard from */ - u_char unit; /* unit number for this guy */ - u_char status; /* clock status */ - u_char lastevent; /* last clock event */ - u_char year; /* year of eternity */ - u_short day; /* day of year */ - u_char hour; /* hour of day */ - u_char minute; /* minute of hour */ - u_char second; /* seconds of minute */ - U_LONG yearstart; /* start of current year */ - u_char 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 irigunit *irigunits[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 irig_init P(()); -static int irig_start P((u_int, struct peer *)); -static void irig_shutdown P((int)); -static void irig_report_event P((struct irigunit *, int)); -static void irig_poll P((int, struct peer *)); -static void irig_control P((u_int, struct refclockstat *, struct refclockstat *)); -static void irig_buginfo P((int, struct refclockbug *)); +static int irig_start P((int, struct peer *)); +static void irig_shutdown P((int, struct peer *)); +static void irig_poll P((int, struct peer *)); /* * Transfer vector */ -struct refclock refclock_irig = { - irig_start, irig_shutdown, irig_poll, - irig_control, irig_init, irig_buginfo, NOFLAGS +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 */ }; -/* - * irig_init - initialize internal irig driver data - */ -static void -irig_init() -{ - register int i; - - /* - * Just zero the data arrays - */ - memset((char *) irigunits, 0, sizeof irigunits); - memset((char *) unitinuse, 0, 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; - } -} - /* - * irig_start - open the irig device and initialize data for processing + * irig_start - open the device and initialize data for processing */ static int irig_start(unit, peer) - u_int unit; -struct peer *peer; + int unit; + struct peer *peer; { - register struct irigunit *irig; - register int i; - int fd_irig; - int format = IRIG_FORMAT; + register struct refclockproc *pp; + char device[20]; + int fd; + int format = IRIG_FORMAT; /* - * Check configuration info. - */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "irig_start: unit %d invalid", unit); - return (0); - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "irig_start: unit %d in use", unit); - return (0); - } - /* - * Open IRIG device and set format + * Open audio device and set format */ - fd_irig = open(IRIGFD, O_RDONLY | O_NDELAY, 0777); - if (fd_irig == -1) { - syslog(LOG_ERR, "irig_start: open of %s: %m", IRIGFD); + (void)sprintf(device, DEVICE, unit); + fd = open(device, O_RDONLY | O_NDELAY, 0777); + if (fd == -1) { + syslog(LOG_ERR, "irig_start: open of %s: %m", device); return (0); } - if (ioctl(fd_irig, AUDIO_IRIG_OPEN, 0) < 0) { - syslog(LOG_ERR, - "irig_start: ioctl(%s, AUDIO_IRIG_OPEN): %m", IRIGFD); - close(fd_irig); + if (ioctl(fd, AUDIO_IRIG_OPEN, 0) < 0) { + syslog(LOG_ERR, "irig_start: AUDIO_IRIG_OPEN %m"); + close(fd); return (0); } - if (ioctl(fd_irig, AUDIO_IRIG_SETFORMAT, (char *) &format) < 0) { - syslog(LOG_ERR, - "irig_start: ioctl(%s, AUDIO_IRIG_SETFORMAT): %m", IRIGFD); - close(fd_irig); + if (ioctl(fd, AUDIO_IRIG_SETFORMAT, (char *)&format) < 0) { + syslog(LOG_ERR, "irig_start: AUDIO_IRIG_SETFORMAT %m", + DEVICE); + close(fd); return (0); } + pp = peer->procptr; + pp->io.clock_recv = noentry; + pp->io.srcclock = (caddr_t)peer; + pp->io.datalen = 0; + pp->io.fd = fd; /* - * Allocate unit structure - */ - if (irigunits[unit] != 0) { - irig = irigunits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < MAXUNITS; i++) { - if (!unitinuse[i] && irigunits[i] != 0) - break; - } - if (i < MAXUNITS) { - /* - * Reclaim this one - */ - irig = irigunits[i]; - irigunits[i] = 0; - } else { - irig = (struct irigunit *) - emalloc(sizeof(struct irigunit)); - } - } - memset((char *) irig, 0, sizeof(struct irigunit)); - - irigunits[unit] = irig; - - /* - * Set up the structures - */ - irig->peer = peer; - irig->unit = (u_char) unit; - irig->timestarted = current_time; - - irig->io.clock_recv = noentry; - irig->io.srcclock = (caddr_t) irig; - irig->io.datalen = 0; - irig->io.fd = fd_irig; - - /* - * All done. Initialize a few random peer variables, then - * return success. Note that root delay and root dispersion are - * always zero for this clock. - */ - peer->precision = IRIGPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = 0; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *) &peer->refid, IRIGREFID, 4); - else - peer->refid = htonl(IRIGHSREFID); - unitinuse[unit] = 1; + * Initialize miscellaneous variables + */ + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); return (1); } -/* - * irig_shutdown - shut down a irig clock - */ -static void -irig_shutdown(unit) - int unit; -{ - register struct irigunit *irig; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "irig_shutdown: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "irig_shutdown: unit %d not in use", unit); - return; - } - /* - * Tell the I/O module to turn us off. We're history. - */ - irig = irigunits[unit]; - io_closeclock(&irig->io); - unitinuse[unit] = 0; -} - /* - * irig_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. + * irig_shutdown - shut down the clock */ static void -irig_report_event(irig, code) - struct irigunit *irig; -int code; -{ +irig_shutdown(unit, peer) + int unit; struct peer *peer; +{ + struct refclockproc *pp; - peer = irig->peer; - if (irig->status != (u_char) code) { - irig->status = (u_char) code; - if (code != CEVNT_NOMINAL) - irig->lastevent = (u_char) code; - syslog(LOG_INFO, - "clock %s event %x", ntoa(&peer->srcadr), code); - } + pp = peer->procptr; + io_closeclock(&pp->io); } + /* * irig_poll - called by the transmit procedure */ static void irig_poll(unit, peer) - int unit; -struct peer *peer; + int unit; + struct peer *peer; { - struct irigunit *irig; + struct refclockproc *pp; struct irig_time buf; - register u_char *dpt; - register char *cp, *dp; - l_fp tstmp; - int i; + char *cp, *dp; + u_char *dpt; + int i; - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "irig_poll: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "irig_poll: unit %d not in use", unit); - return; - } - irig = irigunits[unit]; - irig->polls++; - - if (read(irig->io.fd, (char *) &buf, sizeof(buf)) != sizeof(buf)) { - syslog(LOG_ERR, "irig_poll: unit %d: %m", irig->unit); - irig_report_event(irig, CEVNT_FAULT); + pp = peer->procptr; + if (read(pp->io.fd, (char *) &buf, sizeof(buf)) != sizeof(buf)) { + refclock_report(peer, CEVNT_FAULT); return; } + pp->polls++; #ifdef DEBUG if (debug) { - dpt = (u_char *) & buf; + dpt = (u_char *)&buf; printf("irig: "); for (i = 0; i < sizeof(buf); i++) printf("%02x", *dpt++); @@ -375,194 +208,52 @@ struct peer *peer; } #endif - buf.stamp.tv_sec += (U_LONG) JAN_1970; - TVTOTS(&buf.stamp, &irig->lastrec); - dpt = buf.bits; - dp = irig->bincode; - for (i = 0; i < sizeof(buf.bits); i++) { - *dp++ = *dpt++; - } + buf.stamp.tv_sec += JAN_1970; + TVTOTS(&buf.stamp, &pp->lastrec); cp = buf.time; - dp = irig->lastcode; + dp = pp->lastcode; for (i = 0; i < sizeof(buf.time); i++) *dp++ = *cp++; - dp--; - *dp = '\0'; - cp = irig->lastcode; - irig->lencode = dp - cp; + *--dp = '\0'; + pp->lencode = dp - pp->lastcode; #ifdef DEBUG if (debug) - printf("irig: timecode %d %s %s\n", - irig->lencode, ulfptoa(&irig->lastrec, 6), irig->lastcode); + printf("irig: time %s timecode %d %s\n", + ulfptoa(&pp->lastrec, 6), pp->lencode, + pp->lastcode); #endif - - irig->lasttime = current_time; + record_clock_stats(&peer->srcadr, pp->lastcode); /* - * Get irig time and convert to timestamp format. + * Get IRIG time and convert to timestamp format */ - if (irig->lencode < 13 || !isdigit(cp[0]) || !isdigit(cp[1]) || - !isdigit(cp[2]) || - cp[3] != ' ' || !isdigit(cp[4]) || !isdigit(cp[5]) || - cp[6] != ':' || !isdigit(cp[7]) || !isdigit(cp[8]) || - cp[9] != ':' || !isdigit(cp[10]) || !isdigit(cp[11])) { - irig->badformat++; - irig_report_event(irig, CEVNT_BADREPLY); - return; - } - record_clock_stats(&(irig->peer->srcadr), irig->lastcode); - irig->day = cp[0] - '0'; - irig->day = MULBY10(irig->day) + cp[1] - '0'; - irig->day = MULBY10(irig->day) + cp[2] - '0'; - irig->hour = MULBY10(cp[4] - '0') + cp[5] - '0'; - irig->minute = MULBY10(cp[7] - '0') + cp[8] - '0'; - irig->second = MULBY10(cp[10] - '0') + cp[11] - '0'; - if (cp[12] = ' ') - irig->leap = 0; - else - irig->leap = LEAP_NOTINSYNC; - if (irig->day < 1 || irig->day > 366) { - irig->baddata++; - irig_report_event(irig, CEVNT_BADDATE); + if (sscanf(pp->lastcode, "%3d %2d:%2d:%2d", + &pp->day, &pp->hour, &pp->minute, &pp->second) != 4) { + refclock_report(peer, CEVNT_BADREPLY); return; } - if (irig->hour > 23 || irig->minute > 59 || irig->second > 59) { - irig->baddata++; - irig_report_event(irig, CEVNT_BADTIME); - return; + if (pp->lastcode[12] != ' ') { + pp->leap = LEAP_NOTINSYNC; + } else { + pp->leap = 0; + pp->lasttime = current_time; } /* - * 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(irig->day, irig->hour, irig->minute, - irig->second, GMT, irig->lastrec.l_ui, - &irig->yearstart, &irig->lastref.l_ui)) { - irig->baddata++; - irig_report_event(irig, CEVNT_BADTIME); - return; - } - tstmp = irig->lastref; - L_SUB(&tstmp, &irig->lastrec); - irig->coderecv++; - L_ADD(&tstmp, &(fudgefactor[irig->unit])); - refclock_receive(irig->peer, &tstmp, GMT, 0, - &irig->lastrec, &irig->lastrec, irig->leap); -} - -/* - * irig_control - set fudge factors, return statistics - */ -static void -irig_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - register struct irigunit *irig; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "irig_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 - */ - irig = irigunits[unit]; - peer = irig->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *) &peer->refid, - IRIGREFID, 4); - else - peer->refid = htonl(IRIGHSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG1) { - sloppyclockflag[unit] = in->flags & CLK_FLAG1; - } - } - if (out != 0) { - out->type = REFCLK_IRIG_AUDIO; - out->haveflags - = CLK_HAVETIME1 | CLK_HAVEVAL1 | CLK_HAVEVAL2 | CLK_HAVEFLAG1; - out->clockdesc = IRIGDESCRIPTION; - 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]) { - irig = irigunits[unit]; - out->lencode = irig->lencode; - out->lastcode = irig->lastcode; - out->timereset = current_time - irig->timestarted; - out->polls = irig->polls; - out->noresponse = irig->noreply; - out->badformat = irig->badformat; - out->baddata = irig->baddata; - out->lastevent = irig->lastevent; - out->currentstatus = irig->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; - } - } -} - -/* - * irig_buginfo - return clock dependent debugging info - */ -static void -irig_buginfo(unit, bug) - int unit; - register struct refclockbug *bug; -{ - register struct irigunit *irig; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "irig_buginfo: unit %d invalid", unit); + * 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, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); return; } - if (!unitinuse[unit]) - return; - irig = irigunits[unit]; - - bug->nvalues = 8; - bug->ntimes = 2; - if (irig->lasttime != 0) - bug->values[0] = current_time - irig->lasttime; - else - bug->values[0] = 0; - bug->values[2] = (U_LONG) irig->year; - bug->values[3] = (U_LONG) irig->day; - bug->values[4] = (U_LONG) irig->hour; - bug->values[5] = (U_LONG) irig->minute; - bug->values[6] = (U_LONG) irig->second; - bug->values[7] = irig->yearstart; - bug->stimes = 0x1c; - bug->times[0] = irig->lastref; - bug->times[1] = irig->lastrec; + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->leap); } #endif diff --git a/usr.sbin/xntpd/xntpd/refclock_leitch.c b/usr.sbin/xntpd/xntpd/refclock_leitch.c index 330712997eba..753ad7673908 100644 --- a/usr.sbin/xntpd/xntpd/refclock_leitch.c +++ b/usr.sbin/xntpd/xntpd/refclock_leitch.c @@ -54,6 +54,7 @@ * 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) */ @@ -106,17 +107,17 @@ struct leitchunit { l_fp codetime1; l_fp codetime2; l_fp codetime3; - U_LONG yearstart; + u_long yearstart; }; /* * Function prototypes */ static void leitch_init P((void)); -static int leitch_start P((u_int, struct peer *)); -static void leitch_shutdown P((int)); +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((u_int, struct refclockstat *, struct refclockstat *)); +static void leitch_control P((int, struct refclockstat *, struct refclockstat *)); #define leitch_buginfo noentry static void leitch_receive P((struct recvbuf *)); static void leitch_process P((struct leitchunit *)); @@ -127,6 +128,8 @@ static int dysize P((int)); static struct leitchunit leitchunits[MAXUNITS]; static u_char unitinuse[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static U_LONG refid[MAXUNITS]; static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; @@ -144,16 +147,21 @@ struct refclock refclock_leitch = { static void leitch_init() { + 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(unit) -int unit; +leitch_shutdown(unit, peer) + int unit; + struct peer *peer; { #ifdef DEBUG if (debug) @@ -196,40 +204,38 @@ leitch_poll(unit, peer) static void leitch_control(unit, in, out) - u_int unit; + int unit; struct refclockstat *in; struct refclockstat *out; { -#if debug - if (debug) - fprintf(stderr, "leitch_control()\n"); -#endif if (unit >= MAXUNITS) { syslog(LOG_ERR, "leitch_control: unit %d invalid", unit); return; } + if (in) { - /* WE DONT SET ANY THING */ + 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->flags = 0; - out->haveflags = 0; - out->lencode = 0; + out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2; + out->fudgeval1 = (LONG)stratumtouse[unit]; + out->fudgeval2 = refid[unit]; out->lastcode = ""; - out->polls = 0; - out->noresponse = 0; - out->badformat = 0; - out->baddata = 0; - out->timereset = 0; out->clockdesc = LEITCH_DESCRIPTION; - out->fudgetime1.l_uf = 0; - out->fudgetime1.l_ui = 0; - out->fudgetime2.l_uf = 0; - out->fudgetime2.l_ui = 0; - out->currentstatus = 0; - out->lastevent = 0; } } @@ -238,7 +244,7 @@ leitch_control(unit, in, out) */ static int leitch_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { struct leitchunit *leitch; @@ -413,8 +419,8 @@ leitch_start(unit, peer) peer->precision = 0; peer->rootdelay = 0; peer->rootdispersion = 0; - peer->stratum = 0; - peer->refid = htonl(0x41544f4d); /* ATOM */ + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; unitinuse[unit] = 1; return(1); diff --git a/usr.sbin/xntpd/xntpd/refclock_local.c b/usr.sbin/xntpd/xntpd/refclock_local.c index 1323ca91ed62..552e71272ab3 100644 --- a/usr.sbin/xntpd/xntpd/refclock_local.c +++ b/usr.sbin/xntpd/xntpd/refclock_local.c @@ -1,7 +1,8 @@ /* * refclock_local - local pseudo-clock driver */ -#if defined(REFCLOCK) && defined(LOCAL_CLOCK) +#if defined(REFCLOCK) && defined(LOCAL_CLOCK) + #include <stdio.h> #include <ctype.h> #include <sys/time.h> @@ -10,190 +11,129 @@ #include "ntp_refclock.h" #include "ntp_stdlib.h" -static void local_init P((void)); -static int local_start P((u_int, struct peer *)); -static void local_shutdown P((int)); -static void local_poll P((int, struct peer *)); -static void local_control P((u_int, struct refclockstat *, struct refclockstat *)); -#define local_buginfo noentry - -struct refclock refclock_local = { - local_start, local_shutdown, local_poll, - local_control, local_init, local_buginfo, NOFLAGS -}; - /* - * This is a hack to allow a machine to use its own system clock as - * a "reference clock", i.e. to free run against its own clock at - * a non-infinity stratum. This is certainly useful if you want to - * use NTP in an isolated environment with no radio clock (not that - * this is a good idea) to synchronize the machines together. Pick - * a machine that you figure has a good clock and configure it with - * a local reference clock running at stratum 0 (i.e. 127.127.1.0). - * Then point all the other machines at the one you're using as the - * reference. + * 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. * - * The other thing this is good for is if you want to use a particular - * server's clock as the last resort, when all radio time has gone - * away. This is especially good if that server has an ovenized - * oscillator or something which will keep the time stable for extended - * periods, since then all the other machines can benefit from this. - * For this you would configure a local clock at a higher stratum (say - * 3 or 4) to prevent the server's stratum from falling below here. - */ - -/* - * Definitions + * 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. + * + * 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 LCLSTRATUM is set at 3 by default, but + * can be changed by the fudge command and/or the xntpdc utility. The + * reference ID is "LCL" by default, but can be changed using the same + * mechanisms. *NEVER* configure this driver to operate at a stratum + * which might possibly disrupt a client with access to a bona fide + * primary server, unless athe 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. + * + * 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. Both parameters are + * additive; that is, they add increments in time or frequency to the + * present values. The fudge flag1 and fudge flag2 bits set the + * corresponding leap bits; for example, setting flag1 causes a leap + * second to be added at the end of the UTC day. These bits are not + * reset automatically when the leap takes place; they must be turned + * off manually after the leap event. */ -#define NUMUNITS 16 /* 127.127.1.[0-15] */ /* - * Some constant values we stick in the peer structure + * Local interface definitions */ -#define LCLDISPERSION (FP_SECOND/5) /* 200 ms dispersion */ -#define LCLROOTDISPERSION (FP_SECOND/5) /* 200 ms root dispersion */ -#define LCLPRECISION (-5) /* what the heck */ -#define LCLREFID "LCL\0" -#define LCLREFOFFSET 20 /* reftime is 20s behind */ -#define LCLHSREFID 0x7f7f0101 /* 127.127.1.1 refid for hi stratum */ +#define PRECISION (-7) /* about 10 ms precision */ +#define REFID "LCL\0" /* reference ID */ +#define DESCRIPTION "Undisciplined local clock" /* WRU */ -/* - * Description of clock - */ -#define LCLDESCRIPTION "Free running against local system clock" +#define STRATUM 3 /* default stratum */ +#define DISPERSION (FP_SECOND / 100) /* default dispersion (10 ms) */ /* - * Local clock unit control structure. + * Imported from the timer module */ -struct lclunit { - struct peer *peer; /* associated peer structure */ - u_char status; /* clock status */ - u_char lastevent; /* last clock event */ - u_char unit; /* unit number */ - u_char unused; - U_LONG lastupdate; /* last time data received */ - U_LONG polls; /* number of polls */ - U_LONG timestarted; /* time we started this */ -}; - +extern u_long current_time; /* - * Data space for the unit structures. Note that we allocate these on - * the fly, but never give them back. + * Imported from ntp_proto */ -static struct lclunit *lclunits[NUMUNITS]; -static u_char unitinuse[NUMUNITS]; +extern s_char sys_precision; /* - * Imported from the timer module + * Function prototypes */ -extern U_LONG current_time; - -extern l_fp sys_clock_offset; +static int local_start P((int, struct peer *)); +static void local_poll P((int, struct peer *)); /* - * local_init - initialize internal local clock driver data + * Transfer vector */ -static void -local_init() -{ - /* - * Just zero the data arrays - */ - memset((char *)lclunits, 0, sizeof lclunits); - memset((char *)unitinuse, 0, sizeof unitinuse); -} +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 a local reference clock + * local_start - start up the clock */ static int local_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { - register int i; - register struct lclunit *lcl; + register struct refclockproc *pp; - if (unit >= NUMUNITS) { - syslog(LOG_ERR, "local clock: unit number %d invalid (max 15)", - unit); - return 0; - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "local clock: unit number %d in use", unit); - return 0; - } + pp = peer->procptr; /* - * Looks like this might succeed. Find memory for the structure. - * Look to see if there are any unused ones, if not we malloc() - * one. + * Initialize miscellaneous variables */ - if (lclunits[unit] != 0) { - lcl = lclunits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < NUMUNITS; i++) { - if (!unitinuse[i] && lclunits[i] != 0) - break; - } - if (i < NUMUNITS) { - /* - * Reclaim this one - */ - lcl = lclunits[i]; - lclunits[i] = 0; - } else { - lcl = (struct lclunit *)emalloc(sizeof(struct lclunit)); - } - } - memset((char *)lcl, 0, sizeof(struct lclunit)); - lclunits[unit] = lcl; - - /* - * Set up the structure - */ - lcl->peer = peer; - lcl->unit = (u_char)unit; - lcl->timestarted = lcl->lastupdate = current_time; - - /* - * That was easy. Diddle the peer variables and return success. - */ - peer->precision = LCLPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = LCLROOTDISPERSION; - peer->stratum = (u_char)unit; - if (unit <= 1) - memmove((char *)&peer->refid, LCLREFID, 4); - else - peer->refid = htonl(LCLHSREFID); - unitinuse[unit] = 1; - return 1; -} - - -/* - * local_shutdown - shut down a local clock - */ -static void -local_shutdown(unit) - int unit; -{ - if (unit >= NUMUNITS) { - syslog(LOG_ERR, - "local clock: INTERNAL ERROR, unit number %d invalid (max 15)", - unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, - "local clock: INTERNAL ERROR, unit number %d not in use", unit); - return; - } - - unitinuse[unit] = 0; + peer->precision = sys_precision; + pp->clockdesc = DESCRIPTION; + peer->stratum = STRATUM; + memcpy((char *)&pp->refid, REFID, 4); + return (1); } @@ -205,103 +145,26 @@ local_poll(unit, peer) int unit; struct peer *peer; { - l_fp off; - l_fp ts; + struct refclockproc *pp; - if (unit >= NUMUNITS) { - syslog(LOG_ERR, "local clock poll: INTERNAL: unit %d invalid", - unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "local clock poll: INTERNAL: unit %d unused", - unit); - return; - } - if (peer != lclunits[unit]->peer) { - syslog(LOG_ERR, - "local clock poll: INTERNAL: peer incorrect for unit %d", - unit); - return; - } + pp = peer->procptr; + pp->polls++; + pp->lasttime = current_time; /* - * Update clock stat counters + * Ramble through the usual filtering and grooming code, which + * is essentially a no-op and included mostly for pretty + * billboards. We fudge flags as the leap indicators and allow a + * one-time adjustment in time using fudge time1 (s) and + * frequency using fudge time 2 (ppm). */ - lclunits[unit]->polls++; - lclunits[unit]->lastupdate = current_time; - - /* - * This is pretty easy. Give the reference clock support - * a zero offset and our fixed dispersion. Use peer->xmt for - * our receive time. Use peer->xmt - 20 seconds for our - * reference time. - */ - off.l_ui = off.l_uf = 0; - ts = peer->xmt; - ts.l_ui -= LCLREFOFFSET; - refclock_receive(peer, &off, 0, LCLDISPERSION, - &ts, &peer->xmt, 0); + pp->dispersion = DISPERSION; + gettstamp(&pp->lastrec); + refclock_receive(peer, &pp->fudgetime1, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->sloppyclockflag); + adj_frequency(LFPTOFP(&pp->fudgetime2)); + L_CLR(&pp->fudgetime1); + L_CLR(&pp->fudgetime2); } - - -/* - * local_control - set fudge factors, return statistics - */ -static void -local_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - extern s_fp drift_comp; - - if (unit >= NUMUNITS) { - syslog(LOG_ERR, "local clock: unit %d invalid (max %d)", - unit, NUMUNITS-1); - return; - } - - /* - * The time1 fudge factor is the drift compensation register. - * The time2 fudge factor is the offset of the system clock from - * what the protocol has set it to be. Most useful when SLEWALWAYS - * is defined. - */ - if (in != 0) { - if (in->haveflags & CLK_HAVETIME1) - drift_comp = LFPTOFP(&in->fudgetime1); - if (in->haveflags & CLK_HAVETIME2) { - sys_clock_offset.l_ui = in->fudgetime2.l_ui; - sys_clock_offset.l_uf = in->fudgetime2.l_uf; - } - } - if (out != 0) { - out->type = REFCLK_LOCALCLOCK; - out->flags = 0; - out->haveflags = CLK_HAVETIME1; - out->clockdesc = LCLDESCRIPTION; - FPTOLFP(drift_comp, &out->fudgetime1); - out->fudgetime2.l_ui = sys_clock_offset.l_ui; - out->fudgetime2.l_uf = sys_clock_offset.l_uf; - out->fudgeval1 = out->fudgeval2 = 0; - out->lencode = 0; - out->lastcode = ""; - out->badformat = 0; - out->baddata = 0; - out->noresponse = 0; - if (unitinuse[unit]) { - out->polls = lclunits[unit]->polls; - out->timereset = - current_time - lclunits[unit]->timestarted; - out->lastevent = lclunits[unit]->lastevent; - out->currentstatus = lclunits[unit]->status; - } else { - out->polls = 0; - out->timereset = 0; - out->currentstatus = out->lastevent = CEVNT_NOMINAL; - } - } -} #endif /* REFCLOCK */ diff --git a/usr.sbin/xntpd/xntpd/refclock_moto.c b/usr.sbin/xntpd/xntpd/refclock_moto.c new file mode 100644 index 000000000000..2e888bc40913 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_moto.c @@ -0,0 +1,2 @@ +#if defined(REFCLOCK) && defined(NMEA) +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_msfees.c b/usr.sbin/xntpd/xntpd/refclock_msfees.c index c2a882abb919..17e42351d472 100644 --- a/usr.sbin/xntpd/xntpd/refclock_msfees.c +++ b/usr.sbin/xntpd/xntpd/refclock_msfees.c @@ -87,7 +87,7 @@ * 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, + * Hence remember the last cycle interval, * * Can run in any one of: * @@ -308,6 +308,8 @@ static l_fp onesec; /* = { 1, 0 }; */ /* Imported from the timer module */ extern u_long current_time; +extern s_char sys_precision; + #ifdef DEBUG static int debug; #endif @@ -328,33 +330,6 @@ static int debug; #define USECS 1000000 #define MINSTEP 5 /* some systems increment uS on each call */ #define MAXLOOPS (USECS/9) -static int ees_get_precision() -{ - struct timeval tp; - struct timezone tzp; - long last; - int i; - long diff; - long val; - gettimeofday(&tp, &tzp); - - last = tp.tv_usec; - for (i=0; i< 100000; i++) { - gettimeofday(&tp, &tzp); - diff = tp.tv_usec - last; - if (diff < 0) diff += USECS; - if (diff > MINSTEP) break; - last = tp.tv_usec; - } - syslog(LOG_INFO, - "I: ees: precision calculation given %duS after %d loop%s", - diff, i, (i==1) ? "" : "s"); - - if (i == 0) return -20 /* assume 1uS */; - if (i >= MAXLOOPS) return EESPRECISION /* Lies ! */; - for (i=0, val=USECS; val > 0; i--, val /= 2) if (diff > val) return i; - return EESPRECISION /* Lies ! */; -} static void dump_buf(coffs, from, to, text) l_fp *coffs; @@ -407,18 +382,20 @@ static void msfees_init() /* msfees_start - open the EES devices and initialize data for processing */ static int msfees_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { register struct eesunit *ees; register int i; int fd232 = -1; char eesdev[20]; - struct termios ttyb, *ttyp; + struct termios ttyb, *ttyp; static void ees_receive(); extern int io_addclock(); extern void io_closeclock(); extern char *emalloc(); + struct refclockproc *pp; + pp = peer->procptr; if (unit >= MAXUNITS) { syslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)", @@ -462,24 +439,24 @@ static int msfees_start(unit, peer) ttyp = &ttyb; if (tcgetattr(fd232, ttyp) < 0) { - syslog(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; + syslog(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) { - syslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev); - goto screwed; - } + if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { + syslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev); + goto screwed; + } - if (tcflush(fd232, TCIOFLUSH) < 0) { - syslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev); - goto screwed; - } + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev); + goto screwed; + } inherent_delay[unit].l_uf = INH_DELAY_PPS; @@ -524,7 +501,7 @@ static int msfees_start(unit, peer) * receiving stuff. */ - { + { int rc1; /* Pop any existing onews first ... */ while (ioctl(fd232, I_POP, 0 ) >= 0) ; @@ -554,18 +531,21 @@ static int msfees_start(unit, peer) /* All done. Initialize a few random peer variables, then * return success. */ - peer->precision = ees_get_precision(); + peer->precision = sys_precision; peer->stratum = stratumtouse[unit]; peer->rootdelay = 0; /* ++++ */ peer->rootdispersion = 0; /* ++++ */ if (stratumtouse[unit] <= 1) { - memmove((char *)&peer->refid, EESREFID, 4); + memcpy((char *)&pp->refid, EESREFID, 4); if (unit > 0 && unit < 10) - ((char *)&peer->refid)[3] = '0' + unit; + ((char *)&pp->refid)[3] = '0' + unit; } else { peer->refid = htonl(EESHSREFID); } unitinuse[unit] = 1; + pp->unitptr = (caddr_t) &eesunits[unit]; + pp->clockdesc = EESDESCRIPTION; + pp->nstages = MAXSTAGE; syslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit); return (1); @@ -577,8 +557,9 @@ screwed: /* msfees_shutdown - shut down a EES clock */ -static void msfees_shutdown(unit) +static void msfees_shutdown(unit, peer) int unit; + struct peer *peer; { register struct eesunit *ees; extern void io_closeclock(); @@ -709,7 +690,7 @@ static void ees_receive(rbufp) * continue on. */ cp = ees->lastcode; break; - + default: syslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d", ees->unit, ees->codestate); @@ -818,7 +799,7 @@ static void ees_receive(rbufp) 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->hour > 23 || ees->minute > 59 || ees->second > 59) { ees->baddata++; ees->reason = CODEREASON + 12; ees_event(ees, CEVNT_BADDATE); @@ -861,11 +842,11 @@ static void ees_receive(rbufp) /* Number of seconds since the last step */ sincelast = this_uisec - ees->last_step; - memset(&ppsclockev, 0, sizeof ppsclockev); + memset((char *) &ppsclockev, 0, sizeof ppsclockev); rc = ioctl(ees->io.fd, CIOGETEV, (char *) &ppsclockev); if (debug & DB_PRINT_EV) fprintf(stderr, - "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08x %08x %d\n", + "[%x] CIOGETEV u%d %d (%lx %d) gave %d (%d): %08lx %08lx %ld\n", DB_PRINT_EV, ees->unit, ees->io.fd, CIOGETEV, is_pps(ees), rc, errno, ptr[0], ptr[1], ptr[2]); @@ -881,7 +862,7 @@ static void ees_receive(rbufp) /* allow for single loss of PPS only */ if (pps_step != 1 && pps_step != 2) - fprintf(stderr, "PPS step: %d too far off %d (%d)\n", + 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"); @@ -892,7 +873,7 @@ static void ees_receive(rbufp) diff = pps_arrvstamp; conv = 0; L_SUB(&diff, &ees->arrvtime); -if (debug & DB_PRINT_CDT) printf("[%x] Have %x.%08x and %x.%08x -> %x.%08x @ %s", +if (debug & DB_PRINT_CDT) printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s", DB_PRINT_CDT, ees->arrvtime.l_ui, ees->arrvtime.l_uf, pps_arrvstamp.l_ui, pps_arrvstamp.l_uf, diff.l_ui, diff.l_uf, @@ -929,7 +910,7 @@ syslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s", } ees->last_pps_no = ppsclockev.serial; if (debug & DB_PRINT_CDTC) printf( - "[%x] %08x %08x %d u%d (%d %d)\n", + "[%x] %08lx %08lx %d u%d (%d %d)\n", DB_PRINT_CDTC, pps_arrvstamp.l_ui, pps_arrvstamp.l_uf, conv, ees->unit, call_pps_sample, pps_step); @@ -951,7 +932,7 @@ syslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s", /* Dump the deltas each minute */ if (debug & DB_DUMP_DELTAS) - { if (0 <= ees->second && + { 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) { @@ -964,8 +945,8 @@ syslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s", while (*ptr) ptr++; } syslog(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), + 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; } @@ -1056,7 +1037,7 @@ printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", sum = 4 * 60; } ees->last_step = this_uisec; -printf("MSF%d: d=%3d.%04d@%d :%d:%d:$%d:%d:%d\n", +printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n", ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs); syslog(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); @@ -1065,7 +1046,7 @@ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, old_sincelast, ee /* OK, so not a 4ms step at a minute boundry */ else { if (suspect_4ms_step) syslog(LOG_ERR, - "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]", + "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), @@ -1079,7 +1060,7 @@ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, old_sincelast, ee static ees_step_notes = EES_STEP_NOTES; if (ees_step_notes > 0) { ees_step_notes--; -printf("MSF%d: D=%3d.%04d@%02d :%d%s\n", +printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n", ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !"); syslog(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 !"); @@ -1114,7 +1095,7 @@ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) 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: %3d/%03d -> d=%11d (%d|%d)\n", + "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n", ees->unit, sec_of_ramp, ees->last_step_secs, fsecs, pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs); @@ -1150,7 +1131,7 @@ ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]); L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]); - if (call_pps_sample && !(debug & DB_NO_PPS)) { + if (call_pps_sample && !(debug & DB_NO_PPS)) { /* Sigh -- it expects its args negated */ L_NEG(&pps_arrvstamp); (void) pps_sample(&pps_arrvstamp); @@ -1202,7 +1183,7 @@ static void ees_process(ees) u_fp dispersion; /* ++++ */ int lostsync, isinsync; int samples = ees->nsamples; - int samplelog; + int samplelog = 0; /* keep "gcc -Wall" happy ! */ int samplereduce = (samples + 1) / 2; /* Reset things to zero so we don't have to worry later */ @@ -1238,7 +1219,7 @@ static void ees_process(ees) * 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 + * identity (c-b)-(b-a) == 2*b-a-c */ tmp = coffs[(noff + i)/2]; L_ADD(&tmp, &tmp); @@ -1262,7 +1243,7 @@ static void ees_process(ees) 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. @@ -1416,7 +1397,7 @@ static void msfees_leap() /* msfees_control - set fudge factors, return statistics */ static void msfees_control(unit, in, out) - u_int unit; + int unit; struct refclockstat *in; struct refclockstat *out; { @@ -1443,19 +1424,20 @@ static void msfees_control(unit, in, out) * will wait for the next timecode */ struct peer *peer = ees->peer; + struct refclockproc *pp = peer->procptr; peer->stratum = stratumtouse[unit]; if (stratumtouse[unit] <= 1) { - memmove((char *)&peer->refid, + memmove((char *)&pp->refid, EESREFID, 4); if (unit>0 && unit<10) - ((char *)&peer->refid)[3] = + ((char *)&pp->refid)[3] = '0' + unit; } else peer->refid = htonl(EESHSREFID); } } if (in->haveflags & CLK_HAVEVAL2) { - printf("Debug: %x -> %x\n", debug, in->fudgeval2); + printf("Debug: %x -> %lx\n", debug, in->fudgeval2); syslog(LOG_ERR, "MSF%d: debug %x -> %x", unit, debug, in->fudgeval2); debug = in->fudgeval2; @@ -1478,14 +1460,17 @@ static void msfees_control(unit, in, out) } if (out != 0) { + struct peer *peer = ees->peer; + struct refclockproc *pp = peer->procptr; out->type = REFCLK_MSF_EES; out->haveflags = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1|CLK_HAVEFLAG3|CLK_HAVEFLAG4; - out->clockdesc = EESDESCRIPTION; + out->clockdesc = pp->clockdesc; out->fudgetime1 = fudgefactor[unit]; out->fudgetime2 = os_delay[unit]; out->fudgeval1 = (long)stratumtouse[unit]; - out->fudgeval2 = debug; + /*out->fudgeval2= debug*/; + memmove((char *)&out->fudgeval2, (char *)&pp->refid, 4); out->flags = sloppyclockflag[unit]; if (unitinuse[unit]) { out->flags |= ees->dump_vals | ees->usealldata; diff --git a/usr.sbin/xntpd/xntpd/refclock_mx4200.c b/usr.sbin/xntpd/xntpd/refclock_mx4200.c index 3c0d097a8d9e..2cfee5293c3e 100644 --- a/usr.sbin/xntpd/xntpd/refclock_mx4200.c +++ b/usr.sbin/xntpd/xntpd/refclock_mx4200.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. */ -#if defined(REFCLOCK) && (defined(MX4200) || defined(MX4200CLK) || defined(MX4200PPS)) +#if defined(REFCLOCK) && defined(PPS) && (defined(MX4200) || defined(MX4200CLK) || defined(MX4200PPS)) #if !defined(lint) && !defined(__GNUC__) static char rcsid[] = @@ -102,7 +102,6 @@ static char rcsid[] = #define MX4200PRECISION (-18) /* precision assumed (about 4 us) */ #define MX4200REFID "GPS" /* reference id */ #define MX4200DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */ -#define MX4200HSREFID 0x7f7f0a0a /* 127.127.10.10 refid for hi strata */ #define DEFFUDGETIME 0 /* default fudge time (ms) */ /* Leap stuff */ @@ -216,6 +215,7 @@ static u_char unitinuse[MAXUNITS]; static l_fp fudgefactor[MAXUNITS]; static u_char stratumtouse[MAXUNITS]; static u_char sloppyclockflag[MAXUNITS]; +static U_LONG refid[MAXUNITS]; static const char pmvxg[] = "PMVXG"; @@ -223,13 +223,13 @@ static const char pmvxg[] = "PMVXG"; * Function prototypes */ static void mx4200_init P((void)); -static int mx4200_start P((u_int, struct peer *)); -static void mx4200_shutdown P((int)); +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_process P((struct mx4200unit *)); static void mx4200_report_event P((struct mx4200unit *, int)); static void mx4200_poll P((int, struct peer *)); -static void mx4200_control P((u_int, struct refclockstat *, struct refclockstat *)); +static void mx4200_control P((int, struct refclockstat *, struct refclockstat *)); static void mx4200_buginfo P((int, struct refclockbug *)); static char * mx4200_parse P((char *, struct calendar *, int *, int *)); @@ -273,6 +273,7 @@ mx4200_init() fudgefactor[i].l_uf = DEFFUDGETIME; stratumtouse[i] = 0; sloppyclockflag[i] = 0; + memcpy((char *)&refid[i], MX4200REFID, 4); } } @@ -316,7 +317,7 @@ checkdfile() */ static int mx4200_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { register struct mx4200unit *mx4200; @@ -511,10 +512,7 @@ mx4200_start(unit, peer) peer->rootdelay = 0; peer->rootdispersion = 0; peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, MX4200REFID, 4); - else - peer->refid = htonl(MX4200HSREFID); + peer->refid = refid[unit]; unitinuse[unit] = 1; /* Insure the receiver is properly configured */ @@ -537,8 +535,9 @@ screwed: * mx4200_shutdown - shut down a MX4200 clock */ static void -mx4200_shutdown(unit) +mx4200_shutdown(unit, peer) int unit; + struct peer *peer; { register struct mx4200unit *mx4200; @@ -830,7 +829,7 @@ mx4200_receive(rbufp) fprintf(df, "%s\t%s", umfptoa(tmp_ui, tmp_uf, 6), mfptoa(t.l_ui, t.l_uf, 6)); if (debug > 3) - fprintf(df, "\t(gps: %lu)", gpstime); + fprintf(df, "\t(gps: %lu)", (u_long)gpstime); if (leapsec != 0) fprintf(df, "\t(leap sec %+d)", leapsec); if (!valid) @@ -1024,7 +1023,7 @@ mx4200_process(mx4200) */ static void mx4200_control(unit, in, out) - u_int unit; + int unit; struct refclockstat *in; struct refclockstat *out; { @@ -1038,40 +1037,30 @@ mx4200_control(unit, in, out) 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 - */ - mx4200 = mx4200units[unit]; - peer = mx4200->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, - MX4200REFID, 4); - else - peer->refid = htonl(MX4200HSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG1) { + if (in->haveflags & CLK_HAVEVAL1) + stratumtouse[unit] = (u_char)(in->fudgeval1); + if (in->haveflags & CLK_HAVEVAL2) + refid[unit] = in->fudgeval2; + if (in->haveflags & CLK_HAVEFLAG1) sloppyclockflag[unit] = in->flags & CLK_FLAG1; + if (unitinuse[unit]) { + struct peer *peer; + + peer = mx4200units[unit]->peer; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; } } if (out != 0) { + memset((char *)out, 0, sizeof (struct refclockstat)); out->type = REFCLK_GPS_MX4200; - out->haveflags - = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1; + out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 | CLK_HAVEVAL2 | + CLK_HAVEFLAG1; out->clockdesc = MX4200DESCRIPTION; out->fudgetime1 = fudgefactor[unit]; - out->fudgetime2.l_ui = 0; - out->fudgetime2.l_uf = 0; out->fudgeval1 = (LONG)stratumtouse[unit]; - out->fudgeval2 = 0; + out->fudgeval2 = refid[unit];; out->flags = sloppyclockflag[unit]; if (unitinuse[unit]) { mx4200 = mx4200units[unit]; @@ -1085,13 +1074,6 @@ mx4200_control(unit, in, out) out->badformat = mx4200->badformat; out->baddata = mx4200->baddata; out->timereset = current_time - mx4200->timestarted; - } 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; } } } diff --git a/usr.sbin/xntpd/xntpd/refclock_new/refclock_datum.c b/usr.sbin/xntpd/xntpd/refclock_new/refclock_datum.c new file mode 100644 index 000000000000..2ce53a7d2c43 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_new/refclock_datum.c @@ -0,0 +1,593 @@ +/* + * refclock_datum.c - clock driver for the Datum/Bancomm + * bc635VME Time and Frequency Processor. + * R. Schmidt, Time Service, US Naval Obs. May 94 + * modelled after the TPRO NTP driver. + * + * This requires the Datum HP-UX V9.01 kernel driver and the HP-UX vme2 + * driver subsystem. It has been tested on an HP9000/747i at HP-UX 9.03. + * There are no restrictions on release and use of the following code. + * The refclock type has been defined as 16. + */ +#if defined(REFCLOCK) && defined(DATUM) +#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 "/etc/conf/machine/vme2.h" +#include "/etc/conf/h/io.h" +#include "ntp_datum.h" +#include "ntp_stdlib.h" + +/* + * Definitions + */ +#define MAXUNITS 1 /* max number of VME units */ +#define BMAX 50 /* timecode buffer length */ + +/* + * VME interface parameters. The "IRIG" can be changed to "GPS" for the + * VME-GPS. + */ +#define VMEPRECISION (-21) /* precision assumed (1 us) */ +#define VMEREFID "IRIG" /* reference id */ +#define VMEDESCRIPTION "Datum/ VME IRIG-B Reader" /* who we are */ +#define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */ +#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. + */ +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 P(()); +static int vme_start P((u_int, struct peer *)); +static void vme_shutdown P((int)); +static void vme_report_event P((struct vmeunit *, int)); +static void vme_receive P((struct recvbuf *)); +static void vme_poll P((int unit, struct peer *)); +static void vme_control P((u_int, struct refclockstat *, struct refclockstat *)); +static void vme_buginfo P((int, struct refclockbug *)); +struct vmedate *get_datumtime(); + +/* + * Transfer vector + */ +struct refclock refclock_vme = { + 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() +{ + 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(unit, peer) + u_int unit; + struct peer *peer; +{ + register struct vmeunit *vme; + register int i; + int dummy; + char vmedev[20]; + + /* + * Check configuration info. + */ + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "vme_start: unit %d invalid", unit); + return (0); + } + if (unitinuse[unit]) { + syslog(LOG_ERR, "vme_start: unit %d in use", unit); + return (0); + } + + /* + * Open VME device + */ + + if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) { + syslog(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 ) ) + syslog(LOG_ERR, "vme_start: RUNLOCK failed %m"); + + regvalue = 0; /* More esoteric stuff to do... */ + if( ioctl( fd_vme, WCR0, ®value ) ) + syslog(LOG_ERR, "vme_start: WCR0 failed %m"); + } + + /* + * 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. Note that root delay and root dispersion are + * always zero for this clock. + */ + peer->precision = VMEPRECISION; + peer->rootdelay = 0; + peer->rootdispersion = 0; + peer->stratum = stratumtouse[unit]; + if (stratumtouse[unit] <= 1) + bcopy(VMEREFID, (char *)&peer->refid, 4); + else + peer->refid = htonl(VMEHSREFID); + unitinuse[unit] = 1; + return (1); +} + + +/* + * vme_shutdown - shut down a VME clock + */ +static void +vme_shutdown(unit) + int unit; +{ + register struct vmeunit *vme; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit); + return; + } + + /* + * Tell the I/O module to turn us off. We're history. + */ + 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(vme, code) + 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; + syslog(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(rbufp) + struct recvbuf *rbufp; +{ +} + +/* + * vme_poll - called by the transmit procedure + */ +static void +vme_poll(unit, peer) + 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) { + syslog(LOG_ERR, "vme_poll: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(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_datumtime()) == NULL ) { + vme_report_event(vme, CEVNT_BADREPLY); + return; + } + + gettstamp(&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); + syslog(LOG_ERR, "refclock_datum: 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, &tstmp, GMT, 0, + &vme->lastrec, &vme->lastrec, vme->leap); +} + +/* + * vme_control - set fudge factors, return statistics + */ +static void +vme_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct vmeunit *vme; + + if (unit >= MAXUNITS) { + syslog(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) + bcopy(VMEREFID, (char *)&peer->refid, + 4); + else + peer->refid = htonl(VMEHSREFID); + } + } + if (in->haveflags & CLK_HAVEFLAG1) { + sloppyclockflag[unit] = in->flags & CLK_FLAG1; + } + } + + if (out != 0) { +/* out->type = REFCLK_IRIG_VME; */ + out->type = 16; /* made up by RES */ + 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(unit, bug) + int unit; + register struct refclockbug *bug; +{ + register struct vmeunit *vme; + + if (unit >= MAXUNITS) { + syslog(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; +} + +struct vmedate *get_datumtime() +{ + unsigned short status; + char cbuf[7]; + struct vmedate *time_vme; + struct btfp_time vts; + time_vme = (struct vmedate *)malloc(sizeof(struct vmedate )); + + if( ioctl(fd_vme, READTIME, &vts)) + syslog(LOG_ERR, "get_datumtime error: %m"); + +/* sprintf converts BCD to ASCII */ +/* Get doy */ + sprintf(cbuf,"%3.3x\0", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + + ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); + + if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) ) + time_vme->doy = (unsigned short)atoi(cbuf); + else + time_vme->doy = (unsigned short) 0; + +/* Get hour */ + sprintf(cbuf,"%2.2x\0", 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\0", (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\0", 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\0", 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; + +/* 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); +} +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_new/refclock_gpstm.c b/usr.sbin/xntpd/xntpd/refclock_new/refclock_gpstm.c new file mode 100644 index 000000000000..deab7f46ef6b --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_new/refclock_gpstm.c @@ -0,0 +1,999 @@ +/* + * refclock_gpstm - clock driver for the Kinimetrics Truetime GPSTM/TMD rcvr + * Version 1.0 (from Version 2.0 of the GOES driver, as of 03Jan94) + */ + +#if defined(REFCLOCK) && (defined(GPSTM) || defined(GPSTMCLK) \ + || defined(GPSTMPPS)) + +#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 SYS_BSDI +#undef HAVE_BSD_TTYS +#include <sys/ioctl.h> +#endif + +#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(GPSTMCLK) +#include <clkdefs.h> +#endif /* GPSTMCLK */ +#endif /* STREAM */ + +#if defined(GPSTMPPS) +#include <sys/ppsclock.h> +#endif /* GPSTMPPS */ + +#include "ntp_stdlib.h" + +/* + * Support for Kinemetrics Truetime GPS-TM/TMD Receiver + * + * Most of this code is copied from refclock_goes.c with thanks. + * + * the time code looks like follows: + * + * ADDD:HH:MM:SSQCL + * A - control A + * Q Quality indication: indicates possible error of + * ? +/- 500 milliseconds # +/- 50 milliseconds + * * +/- 5 milliseconds . +/- 1 millisecond + * space less than 1 millisecond + * C - Carriage return + * L - Line feed + * The carriage return start bit begins on 0 seconds and extends to 1 bit time. + * + * Flag1 set to 1 will silence the clock side of xntpd, just reading the + * clock without trying to write to it. This is usefull if several + * xntpds listen to the same clock. This has not been tested yet... + */ + +/* + * Definitions + */ +#define MAXUNITS 4 /* max number of GPSTM units */ +#define GPSTM232 "/dev/gpstm%d" +#define SPEED232 B9600 /* 9600 baud */ + +/* + * Radio interface parameters + */ +#define MAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */ +#define PRECISION (-20) /* precision assumed (about 1 ms) */ +#define REFID "GPS\0" /* reference id */ +#define DESCRIPTION "Kinemetrics GPS-TM/TMD Receiver" /* who we are */ +#define GMT 0 /* hour offset from Greenwich */ +#define NCODES 3 /* stages of median filter */ +#define BMAX 99 /* timecode buffer length */ +#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */ +#define TIMEOUT 180 /* ping the clock if it's silent this long */ + +/* + * used by the state machine + */ +enum gpstm_event {e_Init, e_F18, e_F50, e_F51, e_TS}; +static enum {Base, Start, F18, F50, F51, F08} State[MAXUNITS]; +static void gpstm_doevent P((int, enum gpstm_event)); +static void gpstm_initstate P((int)); + +/* + * Hack to avoid excercising the multiplier. I have no pride. + */ +#define MULBY10(x) (((x)<<3) + ((x)<<1)) + +/* + * Imported from the timer module + */ +extern u_long current_time; +extern struct event timerqueue[]; + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * GPSTM unit control structure + */ +struct gpstm_unit { + struct peer *peer; /* associated peer structure */ + struct refclockio io; /* given to the I/O handler */ + l_fp lastrec; /* last receive time */ + l_fp lastref; /* last timecode time */ + l_fp offset[NCODES]; /* recent sample offsets */ + char lastcode[BMAX]; /* last timecode received */ + u_short polled; /* Hand in a time sample? */ + u_char lencode; /* length of last timecode */ + u_long lasttime; /* last time clock heard from */ + 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 year; /* year of eternity */ + u_short day; /* day of year */ + u_char hour; /* hour of day */ + u_char minute; /* minute of hour */ + u_char second; /* seconds of minute */ + u_char leap; /* leap indicators */ + u_short msec; /* millisecond of second */ + u_char quality; /* quality character */ + u_long yearstart; /* start of current year */ + /* + * 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 gpstm_unit *gpstm_units[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 fudgefactor1[MAXUNITS]; +static l_fp fudgefactor2[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static u_char readonlyclockflag[MAXUNITS]; +static u_long refid[MAXUNITS]; + +/* + * Function prototypes + */ +static void gpstm_init P((void)); +static int gpstm_start P((u_int, struct peer *)); +static void gpstm_shutdown P((int)); +static void gpstm_rep_event P((struct gpstm_unit *, int)); +static void gpstm_receive P((struct recvbuf *)); +static char gpstm_process P((struct gpstm_unit *, l_fp *, u_fp *)); +static void gpstm_poll P((int, struct peer *)); +static void gpstm_control P((u_int, struct refclockstat *, + struct refclockstat *)); +static void gpstm_buginfo P((int, struct refclockbug *)); +static void gpstm_send P((struct gpstm_unit *, char *)); + +struct refclock refclock_gpstm = { + gpstm_start, gpstm_shutdown, gpstm_poll, + gpstm_control, gpstm_init, gpstm_buginfo, NOFLAGS +}; + +/* + * gpstm_init - initialize internal driver data + */ +static void +gpstm_init() +{ + register int i; + /* + * Just zero the data arrays + */ + memset((char *)gpstm_units, 0, sizeof gpstm_units); + memset((char *)unitinuse, 0, sizeof unitinuse); + + /* + * Initialize fudge factors to default. + */ + for (i = 0; i < MAXUNITS; i++) { + fudgefactor1[i].l_ui = 0; + fudgefactor1[i].l_uf = 0; + fudgefactor2[i].l_ui = 0; + fudgefactor2[i].l_uf = 0; + stratumtouse[i] = 0; + readonlyclockflag[i] = 0; + memcpy((char *)&refid[i], REFID, 4); + } +} + + +/* + * gpstm_start - open the device and initialize data for processing + */ +static int +gpstm_start(unit, peer) + u_int unit; + struct peer *peer; +{ + register struct gpstm_unit *gpstm; + register int i; + int fd232; + char dev[20]; + + /* + * Check configuration info + */ + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_start: unit %d invalid", unit); + return 0; + } + if (unitinuse[unit]) { + syslog(LOG_ERR, "gpstm_start: unit %d in use", unit); + return 0; + } + + /* + * Open serial port + */ + (void) sprintf(dev, GPSTM232, unit); + fd232 = open(dev, O_RDWR, 0777); + if (fd232 == -1) { + syslog(LOG_ERR, "gpstm_start: open of %s: %m", dev); + return 0; + } + +#if defined(HAVE_SYSV_TTYS) + /* + * System V serial line parameters (termio interface) + * + */ + { struct termio ttyb; + if (ioctl(fd232, TCGETA, &ttyb) < 0) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TCGETA): %m", dev); + 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) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TCSETA): %m", dev); + goto screwed; + } + } +#endif /* HAVE_SYSV_TTYS */ +#if defined(HAVE_TERMIOS) + /* + * POSIX serial line parameters (termios interface) + * + * The GPSTMCLK option provides timestamping at the driver level. + * It requires the tty_clk streams module. + * + * The GPSTMPPS 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 SunOS 4.1.1 or + * later. + */ + { struct termios ttyb, *ttyp; + ttyp = &ttyb; + + if (tcgetattr(fd232, ttyp) < 0) { + syslog(LOG_ERR, + "gpstm_start: tcgetattr(%s): %m", dev); + 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) { + syslog(LOG_ERR, + "gpstm_start: tcsetattr(%s): %m", dev); + goto screwed; + } + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(LOG_ERR, + "gpstm_start: tcflush(%s): %m", dev); + goto screwed; + } +#if defined(STREAM) +#if defined(GPSTMCLK) + if (ioctl(fd232, I_PUSH, "clk") < 0) + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, I_PUSH, clk): %m", dev); + if (ioctl(fd232, CLK_SETSTR, "\n") < 0) + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, CLK_SETSTR): %m", dev); +#endif /* GPSTMCLK */ +#if defined(GPSTMPPS) + if (ioctl(fd232, I_PUSH, "ppsclock") < 0) + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, I_PUSH, ppsclock): %m", dev); + else + fdpps = fd232; +#endif /* GPSTMPPS */ +#endif /* STREAM */ + } +#endif /* HAVE_TERMIOS */ +#if defined(HAVE_BSD_TTYS) + /* + * 4.3bsd serial line parameters (sgttyb interface) + * + * The GPSTMCLK option provides timestamping at the driver level. + * It requires the tty_clk line discipline and 4.3bsd or later. + */ + { struct sgttyb ttyb; +#if defined(GPSTMCLK) + int ldisc = CLKLDISC; +#endif /* GPSTMCLK */ + + if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TIOCGETP): %m", dev); + goto screwed; + } + ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; +#if defined(GPSTMCLK) + 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 /* GPSTMCLK */ + if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TIOCSETP): %m", dev); + goto screwed; + } +#if defined(GPSTMCLK) + if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TIOCSETD): %m", dev); + goto screwed; + } +#endif /* GPSTMCLK */ + } +#endif /* HAVE_BSD_TTYS */ + + /* + * Allocate unit structure + */ + if (gpstm_units[unit] != 0) { + gpstm = gpstm_units[unit]; /* The one we want is okay */ + } else { + for (i = 0; i < MAXUNITS; i++) { + if (!unitinuse[i] && gpstm_units[i] != 0) + break; + } + if (i < MAXUNITS) { + /* + * Reclaim this one + */ + gpstm = gpstm_units[i]; + gpstm_units[i] = 0; + } else { + gpstm = (struct gpstm_unit *) + emalloc(sizeof(struct gpstm_unit)); + } + } + memset((char *)gpstm, 0, sizeof(struct gpstm_unit)); + gpstm_units[unit] = gpstm; + + /* + * Set up the structures + */ + gpstm->peer = peer; + gpstm->unit = (u_char)unit; + gpstm->timestarted = current_time; + + gpstm->io.clock_recv = gpstm_receive; + gpstm->io.srcclock = (caddr_t)gpstm; + gpstm->io.datalen = 0; + gpstm->io.fd = fd232; + if (!io_addclock(&gpstm->io)) { + goto screwed; + } + + /* + * All done. Initialize a few random peer variables, then + * return success. + */ + peer->precision = PRECISION; + peer->rootdelay = 0; + peer->rootdispersion = 0; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + unitinuse[unit] = 1; + gpstm_initstate(unit); + return 1; + + /* + * Something broke; abandon ship + */ +screwed: + (void) close(fd232); + return 0; +} + +/* + * gpstm_shutdown - shut down a clock + */ +static void +gpstm_shutdown(unit) + int unit; +{ + register struct gpstm_unit *gpstm; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_shutdown: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "gpstm_shutdown: unit %d not in use", unit); + return; + } + + /* + * Tell the I/O module to turn us off. We're history. + */ + gpstm = gpstm_units[unit]; + io_closeclock(&gpstm->io); + unitinuse[unit] = 0; +} + + +/* + * gpstm_rep_event - note the occurance of an event + */ +static void +gpstm_rep_event(gpstm, code) + struct gpstm_unit *gpstm; + int code; +{ + struct peer *peer; + + peer = gpstm->peer; + if (gpstm->status != (u_char)code) { + gpstm->status = (u_char)code; + if (code != CEVNT_NOMINAL) + gpstm->lastevent = (u_char)code; + syslog(LOG_INFO, + "clock %s event %x\n", ntoa(&peer->srcadr), code); +#ifdef DEBUG + if (debug) { + printf("gpstm_rep_event(gpstm%d, code %d)\n", + gpstm->unit, code); + } +#endif + } + if (code == CEVNT_BADREPLY) + gpstm_initstate(gpstm->unit); +} + + +/* + * gpstm_receive - receive data from the serial interface on a clock + */ +static void +gpstm_receive(rbufp) + struct recvbuf *rbufp; +{ + register int i; + register struct gpstm_unit *gpstm; + register u_char *dpt; + register char *cp; + register u_char *dpend; + l_fp tstmp; + u_fp dispersion; + + /* + * Get the clock this applies to and a pointers to the data + */ + gpstm = (struct gpstm_unit *)rbufp->recv_srcclock; + dpt = (u_char *)&rbufp->recv_space; + + /* + * Edit timecode to remove control chars + */ + dpend = dpt + rbufp->recv_length; + cp = gpstm->lastcode; + while (dpt < dpend) { + if ((*cp = 0x7f & *dpt++) >= ' ') cp++; +#ifdef GPSTMCLK + else if (*cp == '\r') { + if (dpend - dpt < 8) { + /* short timestamp */ + return; + } + if (!buftvtots(dpt,&gpstm->lastrec)) { + /* screwy timestamp */ + return; + } + dpt += 8; + } +#endif + } + *cp = '\0'; + gpstm->lencode = cp - gpstm->lastcode; + if (gpstm->lencode == 0) + return; +#ifndef GPSTMCLK + gpstm->lastrec = rbufp->recv_time; +#endif /* GPSTMCLK */ +#if !defined(GPSTMCLK) && !defined(GPSTMPPS) && defined(TIOCMODT) + do { + auto struct timeval cur, now; + register long usec; + + if (ioctl(gpstm->io.fd, TIOCMODT, &cur) < 0) { + syslog(LOG_ERR, "TIOCMODT: %m"); +#ifdef DEBUG + if (debug) perror("TIOCMODT"); + break; +#endif + } + if (cur.tv_sec == 0) { + /* no timestamps yet */ + if (debug) printf("MODT tv_sec == 0\n"); + break; + } + + gettimeofday(&now, NULL); + usec = 1000000 * (now.tv_sec - cur.tv_sec) + + (now.tv_usec - cur.tv_usec); +#ifdef DEBUG + if (debug) printf("lastmodem: delay=%d us\n", usec); +#endif + if (usec < 0 || usec > 10000) { + /* time warp or stale timestamp */ + break; + } + if (!buftvtots((char *)&cur, &gpstm->lastrec)) { + /* screwy timestamp */ + break; + } + } while (0); +#endif /*TIOCMODT*/ + +#ifdef DEBUG + if (debug) + printf("gpstm: timecode %d %s\n", + gpstm->lencode, gpstm->lastcode); +#endif + + cp = gpstm->lastcode; + gpstm->leap = 0; + if ((cp[0] == 'F' && isdigit(cp[1]) && isdigit(cp[2])) + || (cp[0] == ' ' && cp[1] == 'T' && cp[2] == 'R')) { + enum gpstm_event event; + + syslog(LOG_NOTICE, "gpstm%d: \"%s\"", gpstm->unit, cp); + if (cp[1] == '5' && cp[2] == '0') + event = e_F50; + else if (cp[1] == '5' && cp[2] == '1') + event = e_F51; + else if (!strncmp(" TRUETIME Mk III", cp, 16)) + event = e_F18; + else { + gpstm_rep_event(gpstm, CEVNT_BADREPLY); + return; + } + gpstm_doevent(gpstm->unit, event); + return; + } else if (gpstm->lencode == 13) { + /* + * Check timecode format 0 + */ + if (!isdigit(cp[0]) /* day of year */ + || !isdigit(cp[1]) + || !isdigit(cp[2]) + || cp[3] != ':' /* : separator */ + || !isdigit(cp[4]) /* hours */ + || !isdigit(cp[5]) + || cp[6] != ':' /* : separator */ + || !isdigit(cp[7]) /* minutes */ + || !isdigit(cp[8]) + || cp[9] != ':' /* : separator */ + || !isdigit(cp[10]) /* seconds */ + || !isdigit(cp[11])) + { + gpstm->badformat++; + gpstm_rep_event(gpstm, CEVNT_BADREPLY); + return; + } + + /* + * Convert format 0 and check values + */ + gpstm->year = 0; /* fake */ + gpstm->day = cp[0] - '0'; + gpstm->day = MULBY10(gpstm->day) + cp[1] - '0'; + gpstm->day = MULBY10(gpstm->day) + cp[2] - '0'; + gpstm->hour = MULBY10(cp[4] - '0') + cp[5] - '0'; + gpstm->minute = MULBY10(cp[7] - '0') + cp[8] - '0'; + gpstm->second = MULBY10(cp[10] - '0') + cp[11] - '0'; + gpstm->msec = 0; + + if (cp[12] != ' ' && cp[12] != '.' && cp[12] != '*') + gpstm->leap = LEAP_NOTINSYNC; + else + gpstm->lasttime = current_time; + + if (gpstm->day < 1 || gpstm->day > 366) { + gpstm->baddata++; + gpstm_rep_event(gpstm, CEVNT_BADDATE); + return; + } + if (gpstm->hour > 23 || gpstm->minute > 59 + || gpstm->second > 59) { + gpstm->baddata++; + gpstm_rep_event(gpstm, CEVNT_BADTIME); + return; + } + gpstm_doevent(gpstm->unit, e_TS); + } else { + gpstm_rep_event(gpstm, CEVNT_BADREPLY); + 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 (!gpstm->polled) + return; + + /* + * Now, compute the reference time value. Use the heavy + * machinery for the seconds and the millisecond field for the + * fraction when present. + * + * this code does not yet know how to do the years + */ + tstmp = gpstm->lastrec; + if (!clocktime(gpstm->day, gpstm->hour, gpstm->minute, + gpstm->second, GMT, tstmp.l_ui, + &gpstm->yearstart, &gpstm->lastref.l_ui)) + { + gpstm->baddata++; + gpstm_rep_event(gpstm, CEVNT_BADTIME); + return; + } + MSUTOTSF(gpstm->msec, gpstm->lastref.l_uf); + + i = ((int)(gpstm->coderecv)) % NCODES; + gpstm->offset[i] = gpstm->lastref; + L_SUB(&gpstm->offset[i], &tstmp); + if (gpstm->coderecv == 0) + for (i = 1; i < NCODES; i++) + gpstm->offset[i] = gpstm->offset[0]; + + gpstm->coderecv++; + + /* + * Process the median filter, and pass the + * offset and dispersion along. 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 (!gpstm_process(gpstm, &tstmp, &dispersion)) { + gpstm->baddata++; + gpstm_rep_event(gpstm, CEVNT_BADTIME); + return; + } + refclock_receive(gpstm->peer, &tstmp, GMT, dispersion, + &gpstm->lastrec, &gpstm->lastrec, gpstm->leap); + + /* + * We have succedded in answering the poll. Turn off the flag + */ + gpstm->polled = 0; +} + +/* + * gpstm_send - time to send the clock a signal to cough up a time sample + */ +static void +gpstm_send(gpstm, cmd) + struct gpstm_unit *gpstm; + char *cmd; +{ +#ifdef DEBUG + if (debug) { + printf("gpstm_send(gpstm%d): %s\n", gpstm->unit, cmd); + } +#endif + if (!readonlyclockflag[gpstm->unit]) { + register int len = strlen(cmd); + + if (write(gpstm->io.fd, cmd, len) != len) { + syslog(LOG_ERR, "gpstm_send: unit %d: %m", + gpstm->unit); + gpstm_rep_event(gpstm, CEVNT_FAULT); + } + } +} + +/* + * state machine for initializing the clock + */ + +static void +gpstm_doevent(unit, event) + int unit; + enum gpstm_event event; +{ + struct gpstm_unit *gpstm = gpstm_units[unit]; + +#ifdef DEBUG + if (debug) { + printf("gpstm_doevent(gpstm%d, %d)\n", unit, (int)event); + } +#endif + if (event == e_TS && State[unit] != F51 && State[unit] != F08) { + gpstm_send(gpstm, "\03\r"); + } + + switch (event) { + case e_Init: + gpstm_send(gpstm, "F18\r"); + State[unit] = Start; + break; + case e_F18: + gpstm_send(gpstm, "F50\r"); + State[unit] = F18; + break; + case e_F50: + gpstm_send(gpstm, "F51\r"); + State[unit] = F50; + break; + case e_F51: + gpstm_send(gpstm, "F08\r"); + State[unit] = F51; + break; + case e_TS: + /* nothing to send - we like this mode */ + State[unit] = F08; + break; + } +} + +static void +gpstm_initstate(unit) + int unit; +{ + State[unit] = Base; /* just in case */ + gpstm_doevent(unit, e_Init); +} + +/* + * gpstm_process - process a pile of samples from the clock + */ +static char +gpstm_process(gpstm, offset, dispersion) + struct gpstm_unit *gpstm; + l_fp *offset; + u_fp *dispersion; +{ + register int i, j; + register u_long tmp_ui, tmp_uf; + int not_median1 = -1; /* XXX correct? */ + int not_median2 = -1; /* XXX correct? */ + int median; + u_fp disp_tmp, disp_tmp2; + + /* + * This code implements a three-stage median filter. First, we + * check if the samples are within 125 ms of each other. If not, + * dump the sample set. We take the median of the three offsets + * and use that as the sample offset. We take the maximum + * difference and use that as the sample dispersion. There + * probably is not much to be gained by a longer filter, since + * the clock filter in ntp_proto should do its thing. + */ + disp_tmp2 = 0; + for (i = 0; i < NCODES-1; i++) { + for (j = i+1; j < NCODES; j++) { + tmp_ui = gpstm->offset[i].l_ui; + tmp_uf = gpstm->offset[i].l_uf; + M_SUB(tmp_ui, tmp_uf, gpstm->offset[j].l_ui, + gpstm->offset[j].l_uf); + if (M_ISNEG(tmp_ui, tmp_uf)) { + M_NEG(tmp_ui, tmp_uf); + } + if (tmp_ui != 0 || tmp_uf > CODEDIFF) { + return 0; + } + disp_tmp = MFPTOFP(0, tmp_uf); + if (disp_tmp > disp_tmp2) { + disp_tmp2 = disp_tmp; + not_median1 = i; + not_median2 = j; + } + } + } + + /* + * It seems as if all are within 125 ms of each other. + * Now to determine the median of the three. Whlie the + * 125 ms check was going on, we also subtly catch the + * dispersion and set-up for a very easy median calculation. + * The largest difference between any two samples constitutes + * the dispersion. The sample not involve in the dispersion is + * the median sample. EASY! + */ + if (gpstm->lasttime == 0 || disp_tmp2 > MAXDISPERSE) + disp_tmp2 = MAXDISPERSE; + if (not_median1 == 0) { + if (not_median2 == 1) + median = 2; + else + median = 1; + } else { + median = 0; + } + *offset = gpstm->offset[median]; + *dispersion = disp_tmp2; + return 1; +} + +/* + * gpstm_poll - called by the transmit procedure + */ +static void +gpstm_poll(unit, peer) + int unit; + struct peer *peer; +{ + struct gpstm_unit *gpstm; + + /* + * 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 (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_poll: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "gpstm_poll: unit %d not in use", unit); + return; + } + gpstm = gpstm_units[unit]; + if ((current_time - gpstm->lasttime) > 150) { + gpstm->noreply++; + gpstm_rep_event(gpstm_units[unit], CEVNT_TIMEOUT); + gpstm_initstate(gpstm->unit); + } + + /* + * polled every 64 seconds. Ask our receiver to hand in a timestamp. + */ + gpstm->polled = 1; + gpstm->polls++; +} + +/* + * gpstm_control - set fudge factors, return statistics + */ +static void +gpstm_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct gpstm_unit *gpstm; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_control: unit %d invalid", unit); + return; + } + + if (in != 0) { + if (in->haveflags & CLK_HAVETIME1) + fudgefactor1[unit] = in->fudgetime1; + if (in->haveflags & CLK_HAVETIME2) + fudgefactor2[unit] = in->fudgetime2; + if (in->haveflags & CLK_HAVEVAL1) + stratumtouse[unit] = (u_char)(in->fudgeval1); + if (in->haveflags & CLK_HAVEVAL2) + refid[unit] = in->fudgeval2; + if (in->haveflags & CLK_HAVEFLAG1) + readonlyclockflag[unit] = in->flags & CLK_FLAG1; + if (unitinuse[unit]) { + struct peer *peer; + + peer = gpstm_units[unit]->peer; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + } + } + + if (out != 0) { + memset((char *)out, 0, sizeof (struct refclockstat)); + out->type = REFCLK_GPSTM_TRUETIME; + out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 | + CLK_HAVEVAL2 | CLK_HAVEFLAG1; + out->clockdesc = DESCRIPTION; + out->fudgetime1 = fudgefactor1[unit]; + out->fudgetime2 = fudgefactor2[unit]; + out->fudgeval1 = stratumtouse[unit]; + out->fudgeval2 = refid[unit]; + out->flags = readonlyclockflag[unit]; + if (unitinuse[unit]) { + gpstm = gpstm_units[unit]; + out->lencode = gpstm->lencode; + out->lastcode = gpstm->lastcode; + out->timereset = current_time - gpstm->timestarted; + out->polls = gpstm->polls; + out->noresponse = gpstm->noreply; + out->badformat = gpstm->badformat; + out->baddata = gpstm->baddata; + out->lastevent = gpstm->lastevent; + out->currentstatus = gpstm->status; + } + } +} + +/* + * gpstm_buginfo - return clock dependent debugging info + */ +static void +gpstm_buginfo(unit, bug) + int unit; + register struct refclockbug *bug; +{ + register struct gpstm_unit *gpstm; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_buginfo: unit %d invalid", unit); + return; + } + + if (!unitinuse[unit]) + return; + gpstm = gpstm_units[unit]; + + bug->nvalues = 11; + bug->ntimes = 5; + if (gpstm->lasttime != 0) + bug->values[0] = current_time - gpstm->lasttime; + else + bug->values[0] = 0; + bug->values[1] = gpstm->reason; + bug->values[2] = gpstm->year; + bug->values[3] = gpstm->day; + bug->values[4] = gpstm->hour; + bug->values[5] = gpstm->minute; + bug->values[6] = gpstm->second; + bug->values[7] = gpstm->msec; + bug->values[8] = gpstm->noreply; + bug->values[9] = gpstm->yearstart; + bug->values[10] = gpstm->quality; + bug->stimes = 0x1c; + bug->times[0] = gpstm->lastref; + bug->times[1] = gpstm->lastrec; + bug->times[2] = gpstm->offset[0]; + bug->times[3] = gpstm->offset[1]; + bug->times[4] = gpstm->offset[2]; +} + +#endif /*GPSTM et al*/ diff --git a/usr.sbin/xntpd/xntpd/refclock_new/refclock_leitch.c b/usr.sbin/xntpd/xntpd/refclock_new/refclock_leitch.c new file mode 100644 index 000000000000..b2a0e7fa76bf --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_new/refclock_leitch.c @@ -0,0 +1,718 @@ +/* + * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock + */ +#if defined(REFCLOCK) && (defined(LEITCH) || defined(LEITCHCLK) || defined(LEITCHPPS)) + +#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" + +#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 +#ifdef STREAM +#include <stropts.h> +#if defined(LEITCHCLK) +#include <sys/clkdefs.h> +#endif /* LEITCHCLK */ +#endif /* STREAM */ + +#if defined (LEITCHPPS) +#include <sys/ppsclock.h> +#endif /* LEITCHPPS */ + +#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 \ + syslog(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 + +extern struct event timerqueue[]; + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * LEITCH unit control structure + */ +struct leitchunit { + struct peer *peer; + struct event leitchtimer; + 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((u_int, struct peer *)); +static void leitch_shutdown P((int)); +static void leitch_poll P((int, struct peer *)); +static void leitch_control P((u_int, struct refclockstat *, struct refclockstat *)); +#define leitch_buginfo noentry +static void leitch_receive P((struct recvbuf *)); +static void leitch_process P((struct leitchunit *)); +static void leitch_timeout P((struct peer *)); +static int leitch_get_date P((struct recvbuf *, struct leitchunit *)); +static int leitch_get_time P((struct recvbuf *, struct leitchunit *, int)); +static int dysize P((int)); + +static struct leitchunit leitchunits[MAXUNITS]; +static u_char unitinuse[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static u_long 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() +{ + 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(unit) +int unit; +{ +#ifdef DEBUG + if (debug) + fprintf(stderr, "leitch_shutdown()\n"); +#endif +} + +/* + * leitch_poll - called by the transmit procedure + */ +static void +leitch_poll(unit, peer) + 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(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + if (unit >= MAXUNITS) { + syslog(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 = stratumtouse[unit]; + out->fudgeval2 = refid[unit]; + out->lastcode = ""; + out->clockdesc = LEITCH_DESCRIPTION; + } +} + +/* + * leitch_start - open the LEITCH devices and initialize data for processing + */ +static int +leitch_start(unit, peer) + u_int unit; + struct peer *peer; +{ + struct leitchunit *leitch; + int fd232; + char leitchdev[20]; + + /* + * Check configuration info. + */ + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "leitch_start: unit %d invalid", unit); + return (0); + } + + if (unitinuse[unit]) { + syslog(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) { + syslog(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) { + syslog(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) { + syslog(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. + * + * The LEITCHPPS 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 SunOS 4.1.1 or + * later. + */ + { struct termios ttyb, *ttyp; + + ttyp = &ttyb; + if (tcgetattr(fd232, ttyp) < 0) { + syslog(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) { + syslog(LOG_ERR, + "leitch_start: tcsetattr(%s): %m", leitchdev); + goto screwed; + } + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(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) + syslog(LOG_ERR, + "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev); + if (ioctl(fd232, CLK_SETSTR, "\n") < 0) + syslog(LOG_ERR, + "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev); +#endif /* LEITCHCLK */ +#if defined(LEITCHPPS) + if (ioctl(fd232, I_PUSH, "ppsclock") < 0) + syslog(LOG_ERR, + "leitch_start: ioctl(%s, I_PUSH, ppsclock): %m", leitchdev); + else + fdpps = fd232; +#endif /* LEITCHPPS */ +#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) { + syslog(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) { + syslog(LOG_ERR, + "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev); + goto screwed; + } +#if defined(LEITCHCLK) + if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { + syslog(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. Note that root delay and root dispersion are + * always zero for this clock. + */ + peer->precision = 0; + peer->rootdelay = 0; + peer->rootdispersion = 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(rbufp) + 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, 0, 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, 0, 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, 0, 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: + syslog(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(leitch) + struct leitchunit *leitch; +{ + l_fp off; + s_fp delay; + l_fp codetime; + l_fp tmp_fp; + int isinsync = 1; + u_fp dispersion = 10; + + delay = 20; + + codetime = leitch->codetime3; + + off = leitch->reftime1; + L_SUB(&off,&leitch->codetime1); + +#ifdef DEBUG + if (debug) + fprintf(stderr,"%lu %lu %lu %lu %ld %ld\n", + (u_long)leitch->codetime1.l_ui, + (u_long)leitch->codetime1.l_uf, + (u_long)leitch->reftime1.l_ui, + (u_long)leitch->reftime1.l_uf, + (u_long)off.l_i, + (u_long)off.l_f); +#endif + tmp_fp = leitch->reftime2; + L_SUB(&tmp_fp,&leitch->codetime2); + if (L_ISGEQ(&off,&tmp_fp)) + off = tmp_fp; +#ifdef DEBUG + if (debug) + fprintf(stderr,"%lu %lu %lu %lu %ld %ld\n", + (u_long)leitch->codetime2.l_ui, + (u_long)leitch->codetime2.l_uf, + (u_long)leitch->reftime2.l_ui, + (u_long)leitch->reftime2.l_uf, + (u_long)off.l_i, + (u_long)off.l_f); +#endif + tmp_fp = leitch->reftime3; + L_SUB(&tmp_fp,&leitch->codetime3); + + if (L_ISGEQ(&off,&tmp_fp)) + off = tmp_fp; + +#ifdef DEBUG + if (debug) + fprintf(stderr,"%lu %lu %lu %lu %ld %ld\n", + (u_long)leitch->codetime3.l_ui, + (u_long)leitch->codetime3.l_uf, + (u_long)leitch->reftime3.l_ui, + (u_long)leitch->reftime3.l_uf, + (u_long)off.l_i, + (u_long)off.l_f); +#endif + refclock_receive(leitch->peer, &off, 0, dispersion, &codetime, + &codetime, isinsync); +} + +/* + * leitch_timeout + */ +static void +leitch_timeout(fp) + struct peer *fp; +{ + +#ifdef DEBUG + if (debug) + fprintf(stderr, "leitch_timeout()\n"); +#endif + +#ifdef NOTYET + { struct leitchunit *leitch = (struct leitchunit *)fp; + + switch(leitch->state) { + case STATE_IDLE: + leitch_send(leitch,"D\r"); + leitch->state = STATE_DATE; + break; + case STATE_DATE: + leitch_send(leitch,"T\r"); + leitch->state = STATE_TIME1; + break; + case STATE_TIME1: + case STATE_TIME2: + case STATE_TIME3: + default: + break; + } + + leitch->leitchtimer.event_time += 30; + TIMER_ENQUEUE(timerqueue, &leitch->leitchtimer); + } +#endif /* NOTYET */ +} + +/* + * dysize + */ +static int +dysize(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(rbufp,leitch) + struct recvbuf *rbufp; + struct leitchunit *leitch; +{ + int i; + + if (rbufp->recv_length < 6) + return(0); +#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 ((dysize((leitch->year>90?1900:2000)+leitch->year)==365) && + leitch->month > 2) + leitch->yearday--; + + return(1); +} + +/* + * leitch_get_time + */ +static int +leitch_get_time(rbufp,leitch,which) + 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); +} + +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_new/refclock_msfees.c b/usr.sbin/xntpd/xntpd/refclock_new/refclock_msfees.c new file mode 100644 index 000000000000..5a6307c3b6f6 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_new/refclock_msfees.c @@ -0,0 +1,1575 @@ +/* refclock_ees - clock driver for the EES M201 receiver */ + +#if defined(REFCLOCK) && defined(MSFEESPPS) && defined(STREAM) + +/* 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> +#include <sys/time.h> + +#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 */ +#include <termios.h> +#include <stropts.h> +#include <sys/ppsclock.h> +#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] = ees->status; + bug->values[3] = ees->lastevent; + bug->values[4] = ees->reason; + bug->values[5] = ees->nsamples; + bug->values[6] = ees->codestate; + bug->values[7] = ees->day; + bug->values[8] = ees->hour; + bug->values[9] = ees->minute; + bug->values[10] = ees->second; + bug->values[11] = 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 }; */ + +/* Imported from the timer module */ +extern u_long current_time; + +#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) +static int ees_get_precision() +{ + struct timeval tp; + struct timezone tzp; + long last; + int i; + long diff; + long val; + gettimeofday(&tp, &tzp); + + last = tp.tv_usec; + for (i=0; i< 100000; i++) { + gettimeofday(&tp, &tzp); + diff = tp.tv_usec - last; + if (diff < 0) diff += USECS; + if (diff > MINSTEP) break; + last = tp.tv_usec; + } + syslog(LOG_INFO, + "I: ees: precision calculation given %duS after %d loop%s", + diff, i, (i==1) ? "" : "s"); + + if (i == 0) return -20 /* assume 1uS */; + if (i >= MAXLOOPS) return EESPRECISION /* Lies ! */; + for (i=0, val=USECS; val > 0; i--, val /= 2) if (diff > val) return i; + return EESPRECISION /* Lies ! */; +} + +static void dump_buf(coffs, from, to, text) +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) syslog(LOG_DEBUG, "D: %s", ptr=buff); + sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295); + } + syslog(LOG_DEBUG, "D: %s", buff); +} + +/* msfees_init - initialize internal ees driver data */ +static void msfees_init() +{ + 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(unit, peer) + u_int unit; + struct peer *peer; +{ + register struct eesunit *ees; + register int i; + int fd232 = -1; + char eesdev[20]; + struct termios ttyb, *ttyp; + static void ees_receive(); + extern int io_addclock(); + extern void io_closeclock(); + extern char *emalloc(); + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)", + unit, MAXUNITS-1); + return 0; + } + if (unitinuse[unit]) { + syslog(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) { + syslog(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) { + syslog(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) { + syslog(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) { + syslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev); + goto screwed; + } + + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(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. + */ + + { + 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) { + syslog(LOG_ERR, + "ees clock: Push of `%s' and `%s' to %s failed %m", + STREAM_PP1, STREAM_PP2, eesdev); + goto screwed; + } + else { + syslog(LOG_INFO, "I: ees clock: PUSHed %s on %s", + (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev); + ees->ttytype |= T_PPS; + } + } + + /* Add the clock */ + if (!io_addclock(&ees->io)) { + /* Oh shit. Just close and return. */ + syslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev); + goto screwed; + } + + + /* All done. Initialize a few random peer variables, then + * return success. */ + peer->precision = ees_get_precision(); + peer->stratum = stratumtouse[unit]; + peer->rootdelay = 0; /* ++++ */ + peer->rootdispersion = 0; /* ++++ */ + if (stratumtouse[unit] <= 1) { + memmove((char *)&peer->refid, EESREFID, 4); + if (unit > 0 && unit < 10) + ((char *)&peer->refid)[3] = '0' + unit; + } else { + peer->refid = htonl(EESHSREFID); + } + unitinuse[unit] = 1; + syslog(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(unit) + int unit; +{ + register struct eesunit *ees; + extern void io_closeclock(); + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, + "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)", + unit, MAXUNITS); + return; + } + if (!unitinuse[unit]) { + syslog(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(ees, code) + 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(rbufp) + 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; + static void ees_process(); + 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; + extern errno; + int rc; + + /* 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) { + syslog(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) syslog(LOG_INFO, + "I: ees clock %d: %d == %d: await more", + ees->unit, dpt, dpend); + return; + } + + /* This shouldn't happen ... ! */ + if ((*dpt & 0x7f) != 0x7f) { + syslog(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: + syslog(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(&ppsclockev, 0, sizeof ppsclockev); + + rc = ioctl(ees->io.fd, CIOGETEV, (char *) &ppsclockev); + if (debug & DB_PRINT_EV) fprintf(stderr, + "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08x %08x %d\n", + DB_PRINT_EV, ees->unit, ees->io.fd, CIOGETEV, 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) syslog(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 %d (%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 %x.%08x and %x.%08x -> %x.%08x @ %s", + DB_PRINT_CDT, ees->arrvtime.l_ui, ees->arrvtime.l_uf, + pps_arrvstamp.l_ui, pps_arrvstamp.l_uf, + diff.l_ui, 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); +syslog(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, + 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] %08x %08x %d u%d (%d %d)\n", + DB_PRINT_CDTC, pps_arrvstamp.l_ui, + 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 *ptr=text; + int i; + for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) { + sprintf(ptr, " %d.%04d", + msec(deltas[i]), subms(deltas[i])); + while (*ptr) ptr++; + } + syslog(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 delta = (this_step - other_step); + if (delta < 0) delta = - delta; + if (third_step == 0 && ( + (delta == 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++; + } +syslog(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]) +syslog(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=%3d.%04d@%d :%d:%d:$%d:%d:%d\n", +ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs); +syslog(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) syslog(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), + msec(delta_f_abs), + 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 ees_step_notes = EES_STEP_NOTES; + if (ees_step_notes > 0) { + ees_step_notes--; +printf("MSF%d: D=%3d.%04d@%02d :%d%s\n", +ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !"); +syslog(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) syslog(LOG_ERR, + "[%x] MSF%d: %3d/%03d -> d=%11d (%d|%d)", + 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: %3d/%03d -> d=%11d (%d|%d)\n", + ees->unit, sec_of_ramp, ees->last_step_secs, fsecs, + 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) syslog(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); + (void) pps_sample(&pps_arrvstamp); + } + + /* Subtract off the local clock time stamp */ + L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime); + if (debug & DB_LOG_SAMPLES) syslog(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! */ +} + + +static void set_x(fp_offset) +l_fp *fp_offset; +{ + step_systime_real(fp_offset); +} + + +/* offcompare - auxiliary comparison routine for offset sort */ + +static int +offcompare(a, b) +l_fp *a, *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(ees) + struct eesunit *ees; +{ + static last_samples = -1; + register int i, j; + register int noff; + register l_fp *coffs = ees->codeoffsets; + l_fp offset, tmp; + u_fp dispersion; /* ++++ */ + int lostsync, isinsync; + int samples = ees->nsamples; + int samplelog; + int samplereduce = (samples + 1) / 2; + + /* 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)) { + syslog(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, 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)) syslog( + (debug & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO, + "I: [%x] Offset=%06d (%d), disp=%06d%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))) + { syslog(LOG_INFO, "I: %x != %x (%x %x), 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)) syslog( + (debug & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO, + "I: [%x] Smooth data: %d -> %d, dispersion now %d", + debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE), + ((long) offset.l_uf) / 4295, new / 4295, + (dispersion * 1526) / 100); + offset.l_uf = new; + } + else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) syslog( + (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, + "[%x] No smooth as delta not %d < %d < %d", + debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE), + - FRACT_SEC(100), diff, FRACT_SEC(100)); + } + else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) syslog( + (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) { + syslog(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; + set_x(&offset); + L_CLR(&offset); + } + refclock_receive(ees->peer, + &offset, + 0, /* delay */ + dispersion, + &ees->reftime, + &ees->lastsampletime, /* receive time */ + (isinsync) ? 0 : LEAP_NOTINSYNC); + ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL); +} + +/* msfees_poll - called by the transmit procedure */ +static void msfees_poll(unit, peer) + int unit; + char *peer; +{ + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid", + unit); + return; + } + if (!unitinuse[unit]) { + syslog(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); +} + +/* msfees_leap - called when a leap second occurs */ +static void msfees_leap() +{ + register int i; + + /* This routine should be entered a few seconds after + * midnight UTC when a leap second occurs. To ensure we + * don't believe foolish time from the clock(s) we set a + * 40 minute hold on them. It shouldn't take anywhere + * near this amount of time to adjust if the clock is getTING + * data, but doing anything else is complicated. + */ + for (i = 0; i < MAXUNITS; i++) if (unitinuse[i]) + eesunits[i]->leaphold = current_time + EESLEAPHOLD; +} + +/* msfees_control - set fudge factors, return statistics */ +static void msfees_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct eesunit *ees = eesunits[unit]; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "ees clock: unit %d invalid (max %d)", + unit, MAXUNITS-1); + return; + } + + if (in != 0) { + if (in->haveflags & CLK_HAVETIME1) + fudgefactor[unit] = in->fudgetime1; + if (in->haveflags & CLK_HAVETIME2) + os_delay[unit] = in->fudgetime2; + offset_fudge[unit] = os_delay[unit]; + L_ADD(&offset_fudge[unit], &fudgefactor[unit]); + L_ADD(&offset_fudge[unit], &inherent_delay[unit]); + if (in->haveflags & CLK_HAVEVAL1) { + stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf); + if (unitinuse[unit]) { + /* Should actually reselect clock, but + * will wait for the next timecode + */ + struct peer *peer = ees->peer; + peer->stratum = stratumtouse[unit]; + if (stratumtouse[unit] <= 1) { + memmove((char *)&peer->refid, + EESREFID, 4); + if (unit>0 && unit<10) + ((char *)&peer->refid)[3] = + '0' + unit; + } + else peer->refid = htonl(EESHSREFID); + } + } + if (in->haveflags & CLK_HAVEVAL2) { + printf("Debug: %x -> %x\n", debug, in->fudgeval2); + syslog(LOG_ERR, "MSF%d: debug %x -> %x", + unit, debug, in->fudgeval2); + debug = in->fudgeval2; + } + if (in->haveflags & CLK_HAVEFLAG1) { + sloppyclockflag[unit] = in->flags & CLK_FLAG1; + } + if (in->haveflags & CLK_HAVEFLAG2) { + ees->fix_pending++; + /* if (in->flags & CLK_FLAG2 && unitinuse[unit]) + ees->leaphold = 0; */ + } + if (in->haveflags & CLK_HAVEFLAG3 && unitinuse[unit]) { + printf("dump_vals: %x -> %x\n", ees->dump_vals, in->flags & CLK_FLAG3); + ees->dump_vals = in->flags & CLK_FLAG3; + } + if (in->haveflags & CLK_HAVEFLAG4 && unitinuse[unit]) { + ees->usealldata = in->flags & CLK_FLAG4; + } + } + + if (out != 0) { + out->type = REFCLK_MSF_EES; + out->haveflags + = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1|CLK_HAVEFLAG3|CLK_HAVEFLAG4; + out->clockdesc = EESDESCRIPTION; + out->fudgetime1 = fudgefactor[unit]; + out->fudgetime2 = os_delay[unit]; + out->fudgeval1 = stratumtouse[unit]; + /*out->fudgeval2= debug*/; + memmove((char *)&out->fudgeval2, EESREFID, 4); + if (unit > 0 && unit < 10) + ((char *)&out->fudgeval2)[3] = '0' + unit; + out->flags = sloppyclockflag[unit]; + if (unitinuse[unit]) { + out->flags |= ees->dump_vals | ees->usealldata; + out->lencode = ees->lencode; + out->lastcode = ees->lastcode; + out->timereset = current_time - ees->timestarted; + out->polls = 0; /* we don't poll */ + out->noresponse = 0; /* ditto */ + out->badformat = ees->badformat; + out->baddata = ees->baddata; + out->lastevent = ees->lastevent; + out->currentstatus = ees->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; + } + } +} + + +/* msfees_buginfo - return clock dependent debugging info */ +static void msfees_buginfo(unit, bug) + int unit; + register struct refclockbug *bug; +{ + register struct eesunit *ees; + + bug->nvalues = bug->ntimes = 0; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "ees clock: unit %d invalid (max %d)", + unit, MAXUNITS-1); + return; + } + + if (!unitinuse[unit]) + return; + ees = eesunits[unit]; + + bug->nvalues = 16; + bug->svalues = 0x0800; + bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0; + bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0; + bug->values[2] = ees->status; + bug->values[3] = ees->lastevent; + bug->values[4] = ees->reason; + bug->values[5] = ees->nsamples; + bug->values[6] = ees->codestate; + bug->values[7] = ees->day; + bug->values[8] = ees->hour; + bug->values[9] = ees->minute; + bug->values[10] = ees->second; + bug->values[11] = 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->ntimes = 11; + bug->stimes = 0x3f8; + 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].l_ui = ees->yearstart; + bug->times[10].l_uf = 0; +} + +struct refclock refclock_msfees = { + msfees_start, msfees_shutdown, msfees_poll, + msfees_control, msfees_init, msfees_buginfo, NOFLAGS +}; +#endif /* defined(REFCLOCK) && defined(MSFEESPPS) && defined(STREAM) */ diff --git a/usr.sbin/xntpd/xntpd/refclock_new/refclock_mx4200.c b/usr.sbin/xntpd/xntpd/refclock_new/refclock_mx4200.c new file mode 100644 index 000000000000..caf5951c55ed --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_new/refclock_mx4200.c @@ -0,0 +1,977 @@ +/* + * 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. + */ + +#if defined(REFCLOCK) && defined(MX4200) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_calendar.h" +#include "ntp_unixtime.h" + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <sys/ppsclock.h> + +#include "mx4200.h" +#include "ntp_stdlib.h" + +/* + * This driver supports the Magnavox Model MX4200 GPS Receiver. + */ + +/* + * Definitions + */ +#define MAXUNITS 2 /* max number of mx4200 units */ +#define MX4200FD "/dev/gps%d" +#define SPEED232 B4800 /* baud */ + +/* + * The number of raw samples which we acquire to derive a single estimate. + */ +#define NSTMPS 64 + +/* + * Radio interface parameters + */ +#define MX4200PRECISION (-18) /* precision assumed (about 4 us) */ +#define MX4200REFID "GPS" /* reference id */ +#define MX4200DESCRIPTION "Magnavox MX4200 GPS Receiver" /* WRU */ +#define DEFFUDGETIME 0 /* default fudge time (ms) */ + +/* Leap stuff */ +extern u_long leap_hoursfromleap; +extern u_long leap_happened; +static int leap_debug; + +/* + * mx4200_reset - reset the count back to zero + */ +#define mx4200_reset(up) \ + do { \ + (up)->nsamples = 0; \ + } while (0) + +/* + * Imported from the timer module + */ +extern u_long current_time; +extern struct event timerqueue[]; + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * MX4200 unit control structure. + */ +struct mx4200unit { + struct peer *peer; /* associated peer structure */ + struct refclockio io; /* given to the I/O handler */ + u_long gpssamples[NSTMPS]; /* the GPS time samples */ + l_fp unixsamples[NSTMPS]; /* the UNIX time samples */ + + + l_fp lastsampletime; /* time of last estimate */ + u_int lastserial; /* last pps serial number */ +#ifdef notdef + l_fp lastrec; /* last receive time */ + l_fp lastref; /* last timecode time */ +#endif + char lastcode[RX_BUFF_SIZE]; /* last timecode received */ + u_long lasttime; /* last time clock heard from */ + u_char nsamples; /* number of samples we've collected */ + 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 lencode; /* length of last timecode */ + u_char year; /* year of eternity */ + u_short monthday; /* day of month */ + u_char hour; /* hour of day */ + u_char minute; /* minute of hour */ + u_char second; /* seconds of minute */ + u_char leap; /* leap indicators */ + /* + * Status tallies + */ +#ifdef notdef + u_long polls; /* polls sent */ + u_long noresponse; /* number of nonresponses */ +#endif + u_long badformat; /* bad format */ + u_long baddata; /* bad data */ + u_long timestarted; /* time we started this */ +}; + +/* + * We demand that consecutive PPS samples are more than 0.995 seconds + * and less than 1.005 seconds apart. + */ +#define PPSLODIFF_UI 0 /* 0.900 as an l_fp */ +#define PPSLODIFF_UF 0xe6666610 + +#define PPSHIDIFF_UI 1 /* 1.100 as an l_fp */ +#define PPSHIDIFF_UF 0x19999990 + +/* + * reason codes + */ +#define PPSREASON 20 +#define CODEREASON 40 +#define PROCREASON 60 + +/* + * Data space for the unit structures. Note that we allocate these on + * the fly, but never give them back. + */ +static struct mx4200unit *mx4200units[MAXUNITS]; + +static const char pmvxg[] = "PMVXG"; + +/* + * 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_process P((struct mx4200unit *)); +static void mx4200_poll P((int, struct peer *)); + +static char * mx4200_parse P((char *, struct calendar *, int *, int *)); +static int mx4200_needconf P((char *)); +static void mx4200_config P((struct mx4200unit *)); +static void mx4200_send P((int, const char *, ...)); +static int mx4200_cmpl_fp P((void *, void *)); +static u_char cksum P((char *, u_int)); + +#ifdef DEBUG +static void opendfile P((int)); +static void checkdfile P((void)); +#endif /* DEBUG */ + +/* + * 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 */ +}; + +#ifdef DEBUG +static char dfile[] = "/var/tmp/MX4200.debug"; +static FILE *df = NULL; + +static void +opendfile(create) + int create; +{ + if (!create && access(dfile, F_OK) < 0) { + syslog(LOG_ERR, "mx4200: open %s: %m", dfile); + return; + } + df = fopen(dfile, "a"); + if (df == NULL) + syslog(LOG_ERR, "mx4200: open %s: %m", dfile); + else if (setvbuf(df, NULL, _IOLBF, 0) < 0) + syslog(LOG_ERR, "mx4200: setvbuf %s: %m", dfile); +} + +static void +checkdfile() +{ + + if (df == NULL) + return; + + if (access(dfile, F_OK) < 0) { + fclose(df); + opendfile(1); + } +} + +#endif + + +/* + * mx4200_start - open the devices and initialize data for processing + */ +static int +mx4200_start(unit, peer) + int unit; + struct peer *peer; +{ + register struct mx4200unit *up; + struct refclockproc *pp; + int fd; + char device[20]; + + /* + * Open serial port + */ + (void)sprintf(device, MX4200FD, unit); + if (!(fd = refclock_open(device, SPEED232, 0))) + return (0); + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct mx4200unit *) + emalloc(sizeof(struct mx4200unit)))) { + (void) close(fd); + return (0); + } + memset((char *)up, 0, sizeof(struct mx4200unit)); + up->io.clock_recv = mx4200_receive; + up->io.srcclock = (caddr_t)up; + up->io.datalen = 0; + up->io.fd = fd; + if (!io_addclock(&up->io)) { + (void) close(fd); + free(up); + return (0); + } + up->peer = peer; + pp = peer->procptr; + pp->unitptr = (caddr_t)up; + + /* + * Initialize miscellaneous variables + */ + peer->precision = MX4200PRECISION; + pp->clockdesc = MX4200DESCRIPTION; + memcpy((char *)&pp->refid, MX4200REFID, 4); + + /* Insure the receiver is properly configured */ + mx4200_config(up); + +#ifdef DEBUG + opendfile(0); +#endif + return (1); +} + + +/* + * mx4200_shutdown - shut down the clock + */ +static void +mx4200_shutdown(unit, peer) + int unit; + struct peer *peer; +{ + register struct mx4200unit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct mx4200unit *)pp->unitptr; + io_closeclock(&up->io); + free(up); +} + + +static void +mx4200_config(up) + register struct mx4200unit *up; +{ + register int fd = up->io.fd; + +syslog(LOG_DEBUG, "mx4200_config"); + + /* Zero the output list (do it twice to flush possible junk) */ + mx4200_send(fd, "%s,%03d,,%d,,,,,,", pmvxg, PMVXG_S_PORTCONF, 1); + mx4200_send(fd, "%s,%03d,,%d,,,,,,", pmvxg, PMVXG_S_PORTCONF, 1); + + /* Switch to 2d mode */ + mx4200_send(fd, "%s,%03d,%d,,%.1f,%.1f,,%d,%d,%c,%d", + pmvxg, PMVXG_S_INITMODEB, + 2, /* 2d mode */ + 0.1, /* hor accel fact as per Steve */ + 0.1, /* ver accel fact as per Steve */ + 10, /* hdop limit as per Steve */ + 5, /* elevation limit as per Steve */ + 'U', /* time output mode */ + 0); /* local time offset from gmt */ + + /* Configure time recovery */ + mx4200_send(fd, "%s,%03d,%c,%c,%c,%d,%d,%d,", + pmvxg, PMVXG_S_TRECOVCONF, +#ifdef notdef + 'K', /* known position */ + 'D', /* dynamic position */ +#else + 'S', /* static position */ +#endif + 'U', /* steer clock to gps time */ + 'A', /* always output time pulse */ + 500, /* max time error in ns */ + 0, /* user bias in ns */ + 1); /* output to control port */ +} + + +/* + * mx4200_poll - mx4200 watchdog routine + */ +static void +mx4200_poll(unit, peer) + int unit; + struct peer *peer; +{ + register struct mx4200unit *up; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "mx4200_poll: unit %d invalid", unit); + return; + } + + up = mx4200units[unit]; + if ((current_time - up->lasttime) > 150) { + refclock_report(peer, CEVNT_FAULT); + + /* Request a status message which should trigger a reconfig */ + mx4200_send(up->io.fd, "%s,%03d", "CDGPQ", PMVXG_D_STATUS); + syslog(LOG_DEBUG, "mx4200_poll: request status"); + } +} + +static const char char2hex[] = "0123456789ABCDEF"; + + +/* + * mx4200_receive - receive gps data + */ +static void +mx4200_receive(rbufp) + struct recvbuf *rbufp; +{ + register struct mx4200unit *up; + struct peer *peer; + register char *dpt, *cp; + register u_long tmp_ui; + register u_long tmp_uf; + register u_long gpstime; + struct ppsclockev ev; + register struct calendar *jt; + struct calendar sjt; + register int n; + int valid, leapsec; + register u_char ck; + + up = (struct mx4200unit *)rbufp->recv_srcclock; + peer = up->peer; +#ifdef DEBUG + if (debug > 3) + printf("mx4200_receive: nsamples = %d\n", up->nsamples); +#endif + + /* Record the time of this event */ + up->lasttime = current_time; + + /* Get the pps value */ + if (ioctl(up->io.fd, CIOGETEV, (char *)&ev) < 0) { + /* XXX Actually, if this fails, we're pretty much screwed */ +#ifdef DEBUG + if (debug) { + fprintf(stderr, "mx4200_receive: "); + perror("CIOGETEV"); + } +#endif + refclock_report(peer, CEVNT_FAULT); + mx4200_reset(up); + return; + } + tmp_ui = ev.tv.tv_sec + JAN_1970; + TVUTOTSF(ev.tv.tv_usec, tmp_uf); + + /* Get buffer and length; sock away last timecode */ + n = rbufp->recv_length; + dpt = rbufp->recv_buffer; + if (n <= 1) + return; + up->lencode = n; + memmove(up->lastcode, dpt, n); + + /* + * We expect to see something like: + * + * $PMVXG,830,T,1992,07,09,04:18:34,U,S,-02154,00019,000000,00*1D\n + * + * Reject if any important landmarks are missing. + */ + cp = dpt + n - 4; + if (cp < dpt || *dpt != '$' || cp[0] != '*' || cp[3] != '\n') { +#ifdef DEBUG + if (debug) + printf("mx4200_receive: bad format\n"); +#endif + refclock_report(peer, CEVNT_BADREPLY); + mx4200_reset(up); + return; + } + + /* Check checksum */ + ck = cksum(&dpt[1], n - 5); + if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) { +#ifdef DEBUG + if (debug) + printf("mx4200_receive: bad checksum\n"); +#endif + refclock_report(peer, CEVNT_BADREPLY); + mx4200_reset(up); + return; + } + + /* Truncate checksum (and the buffer for that matter) */ + *cp = '\0'; + + /* Leap second debugging stuff */ + if ((leap_hoursfromleap && !leap_happened) || leap_debug > 0) { + /* generate reports for awhile after leap */ + if (leap_hoursfromleap && !leap_happened) + leap_debug = 3600; + else + --leap_debug; + syslog(LOG_INFO, "mx4200 leap: %s \"%s\"", + umfptoa(tmp_ui, tmp_uf, 6), dpt); + } + + /* Parse time recovery message */ + jt = &sjt; + if ((cp = mx4200_parse(dpt, jt, &valid, &leapsec)) != NULL) { + /* Configure the receiver if necessary */ + if (mx4200_needconf(dpt)) + mx4200_config(up); +#ifdef DEBUG + if (debug) + printf("mx4200_receive: mx4200_parse: %s\n", cp); +#endif + refclock_report(peer, CEVNT_BADREPLY); + mx4200_reset(up); + return; + } + + /* Setup leap second indicator */ + if (leapsec == 0) + up->leap = LEAP_NOWARNING; + else if (leapsec == 1) + up->leap = LEAP_ADDSECOND; + else if (leapsec == -1) + up->leap = LEAP_DELSECOND; + else + up->leap = LEAP_NOTINSYNC; /* shouldn't happen */ + + /* Check parsed time (allow for possible leap seconds) */ + if (jt->second >= 61 || jt->minute >= 60 || jt->hour >= 24) { +#ifdef DEBUG + if (debug) { + printf("mx4200_receive: bad time %d:%02d:%02d", + jt->hour, jt->minute, jt->second); + if (leapsec != 0) + printf(" (leap %+d)", leapsec); + putchar('\n'); + } +#endif + refclock_report(peer, CEVNT_BADTIME); + mx4200_reset(up); + /* Eat the next pulse which the clock claims will be bad */ + up->nsamples = -1; + return; + } + + /* Check parsed date */ + if (jt->monthday > 31 || jt->month > 12 || jt->year < 1900) { +#ifdef DEBUG + if (debug) + printf("mx4200_receive: bad date (%d/%d/%d)\n", + jt->monthday, jt->month, jt->year); +#endif + refclock_report(peer, CEVNT_BADDATE); + mx4200_reset(up); + return; + } + + /* Convert to ntp time */ + gpstime = caltontp(jt); + + /* The gps message describes the *next* pulse; pretend it's this one */ + --gpstime; + + /* Debugging */ +#ifdef DEBUG + checkdfile(); + if (df != NULL) { + l_fp t; + + t.l_ui = gpstime; + t.l_uf = 0; + M_SUB(t.l_ui, t.l_uf, tmp_ui, tmp_uf); + fprintf(df, "%s\t%s", + umfptoa(tmp_ui, tmp_uf, 6), mfptoa(t.l_ui, t.l_uf, 6)); + if (debug > 3) + fprintf(df, "\t(gps: %lu)", gpstime); + if (leapsec != 0) + fprintf(df, "\t(leap sec %+d)", leapsec); + if (!valid) + fprintf(df, "\t(pulse not valid)"); + fputc('\n', df); + } +#endif + + /* Check pps serial number against last one */ + if (up->lastserial + 1 != ev.serial && up->lastserial != 0) { +#ifdef DEBUG + if (debug) { + if (ev.serial == up->lastserial) + printf("mx4200_receive: no new pps event\n"); + else + printf("mx4200_receive: missed %d pps events\n", + ev.serial - up->lastserial - 1); + } +#endif + refclock_report(peer, CEVNT_FAULT); + mx4200_reset(up); + /* fall through and this one collect as first sample */ + } + up->lastserial = ev.serial; + +/* + * XXX + * Since this message is for the next pulse, it's really the next pulse + * that the clock might be telling us will be invalid. + */ + /* Toss if not designated "valid" by the gps */ + if (!valid) { +#ifdef DEBUG + if (debug) + printf("mx4200_receive: pps not valid\n"); +#endif + refclock_report(peer, CEVNT_BADTIME); + mx4200_reset(up); + return; + } + + /* Copy time into mx4200unit struct */ + /* XXX (why?) */ + up->year = jt->year; + up->monthday = jt->monthday; + up->hour = jt->hour; + up->minute = jt->minute; + up->second = jt->second; + + /* Sock away the GPS and UNIX timesamples */ + n = up->nsamples++; + if (n < 0) + return; /* oops, this pulse is bad */ + up->gpssamples[n] = gpstime; + up->unixsamples[n].l_ui = up->lastsampletime.l_ui = tmp_ui; + up->unixsamples[n].l_uf = up->lastsampletime.l_uf = tmp_uf; + if (up->nsamples >= NSTMPS) { + /* + * Here we've managed to complete an entire NSTMPS + * second cycle without major mishap. Process what has + * been received. + */ + mx4200_process(up); + mx4200_reset(up); + } +} + +/* + * Compare two l_fp's, used with qsort() + */ +static int +mx4200_cmpl_fp(p1, p2) + register void *p1, *p2; +{ + + if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2)) + return (-1); + if (L_ISEQU((l_fp *)p1, (l_fp *)p2)) + return (0); + return (1); +} + +/* + * mx4200_process - process a pile of samples from the clock + */ +static void +mx4200_process(up) + struct mx4200unit *up; +{ + struct peer *peer; + struct refclockproc *pp; + register int i, n; + register l_fp *fp, *op; + register u_long *lp; + l_fp off[NSTMPS]; + register u_long tmp_ui, tmp_uf; + register u_long date_ui, date_uf; + u_fp dispersion; + + /* Compute offsets from the raw data. */ + peer = up->peer; + pp = peer->procptr; + fp = up->unixsamples; + op = off; + lp = up->gpssamples; + for (i = 0; i < NSTMPS; ++i, ++lp, ++op, ++fp) { + op->l_ui = *lp; + op->l_uf = 0; + L_SUB(op, fp); + } + + /* Sort offsets into ascending order. */ + qsort((char *)off, NSTMPS, sizeof(l_fp), mx4200_cmpl_fp); + + /* + * Reject the furthest from the median until 8 samples left + */ + i = 0; + n = NSTMPS; + while ((n - i) > 8) { + tmp_ui = off[n-1].l_ui; + tmp_uf = off[n-1].l_uf; + date_ui = off[(n+i)/2].l_ui; + date_uf = off[(n+i)/2].l_uf; + M_SUB(tmp_ui, tmp_uf, date_ui, date_uf); + M_SUB(date_ui, date_uf, off[i].l_ui, off[i].l_uf); + if (M_ISHIS(date_ui, date_uf, tmp_ui, tmp_uf)) { + /* + * reject low end + */ + i++; + } else { + /* + * reject high end + */ + n--; + } + } + + /* + * Compute the dispersion based on the difference between the + * extremes of the remaining offsets. + */ + tmp_ui = off[n-1].l_ui; + tmp_uf = off[n-1].l_uf; + M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf); + dispersion = MFPTOFP(tmp_ui, tmp_uf); + + /* + * Now compute the offset estimate. If the sloppy clock + * flag is set, average the remainder, otherwise pick the + * median. + */ + if (pp->sloppyclockflag) { + tmp_ui = tmp_uf = 0; + while (i < n) { + M_ADD(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf); + i++; + } + M_RSHIFT(tmp_ui, tmp_uf); + M_RSHIFT(tmp_ui, tmp_uf); + M_RSHIFT(tmp_ui, tmp_uf); + i = 0; + off[0].l_ui = tmp_ui; + off[0].l_uf = tmp_uf; + } else { + i = (n + i) / 2; + } + + /* + * Add the default MX4200 QT delay into this. + */ +#ifdef notdef + L_ADDUF(&off[i], MX4200QTFUDGE); +#endif + + /* + * Done. Use lastref as the reference time and lastrec + * as the receive time. ** note this can result in tossing + * out the peer in the protocol module if lastref > lastrec, + * so last rec is used for both values - dlm *** + */ + refclock_receive(up->peer, &off[i], + (s_fp)0, /* delay */ + dispersion, + &up->unixsamples[NSTMPS-1], /* reftime */ + &up->unixsamples[NSTMPS-1], /* rectime */ + up->leap); + + refclock_report(peer, CEVNT_NOMINAL); +} + + +/* + * Returns true if the this is a status message. We use this as + * an indication that the receiver needs to be initialized. + */ +static int +mx4200_needconf(buf) + char *buf; +{ + register long v; + char *cp; + + cp = buf; + + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Record type */ + v = strtol(cp, &cp, 10); + if (v != PMVXG_D_STATUS) + return (0); + /* + * 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. + */ +#ifdef notdef + ++cp; + + /* Receiver status */ + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Number of satellites which should be visible */ + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Number of satellites being tracked */ + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Time since last NAV */ + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Initialization status */ + v = strtol(cp, &cp, 10); + if (v == 0) +#endif + return (1); +} + +/* Parse a mx4200 time recovery message. Returns a string if error */ +static char * +mx4200_parse(buf, jt, validp, leapsecp) + register char *buf; + register struct calendar *jt; + register int *validp, *leapsecp; +{ + register long v; + char *cp; + + cp = buf; + memset((char *)jt, 0, sizeof(*jt)); + + if ((cp = strchr(cp, ',')) == NULL) + return ("no rec-type"); + ++cp; + + /* Record type */ + v = strtol(cp, &cp, 10); + if (v != PMVXG_D_TRECOVOUT) + return ("wrong rec-type"); + + /* Pulse valid indicator */ + if (*cp++ != ',') + return ("no pulse-valid"); + if (*cp == 'T') + *validp = 1; + else if (*cp == 'F') + *validp = 0; + else + return ("bad pulse-valid"); + ++cp; + + /* Year */ + if (*cp++ != ',') + return ("no year"); + jt->year = strtol(cp, &cp, 10); + + /* Month of year */ + if (*cp++ != ',') + return ("no month"); + jt->month = strtol(cp, &cp, 10); + + /* Day of month */ + if (*cp++ != ',') + return ("no month day"); + jt->monthday = strtol(cp, &cp, 10); + + /* Hour */ + if (*cp++ != ',') + return ("no hour"); + jt->hour = strtol(cp, &cp, 10); + + /* Minute */ + if (*cp++ != ':') + return ("no minute"); + jt->minute = strtol(cp, &cp, 10); + + /* Second */ + if (*cp++ != ':') + return ("no second"); + jt->second = strtol(cp, &cp, 10); + + /* Time indicator */ + if (*cp++ != ',' || *cp++ == '\0') + return ("no time indicator"); + + /* Time recovery mode */ + if (*cp++ != ',' || *cp++ == '\0') + return ("no time mode"); + + /* Oscillator offset */ + if ((cp = strchr(cp, ',')) == NULL) + return ("no osc off"); + ++cp; + + /* Time mark error */ + if ((cp = strchr(cp, ',')) == NULL) + return ("no time mark err"); + ++cp; + + /* User time bias */ + if ((cp = strchr(cp, ',')) == NULL) + return ("no user bias"); + ++cp; + + /* Leap second flag */ + if ((cp = strchr(cp, ',')) == NULL) + return ("no leap"); + ++cp; + *leapsecp = strtol(cp, &cp, 10); + + return (NULL); +} + +/* Calculate the checksum */ +static u_char +cksum(cp, n) + register char *cp; + register u_int n; +{ + register u_char ck; + + for (ck = 0; n-- > 0; ++cp) + ck ^= *cp; + return (ck); +} + +static void +#if __STDC__ +mx4200_send(register int fd, const char *fmt, ...) +#else +mx4200_send(fd, fmt, va_alist) + register int fd; + const char *fmt; + va_dcl +#endif +{ + 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 + 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 + ck = 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 + + m = write(fd, buf, n); + if (m < 0) + syslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf); + else if (m != n) + syslog(LOG_ERR, "mx4200_send: write: %d != %d (%s)", m, n, buf); + va_end(ap); +} +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_new/refclock_omega.c b/usr.sbin/xntpd/xntpd/refclock_new/refclock_omega.c new file mode 100644 index 000000000000..e62b668956f3 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_new/refclock_omega.c @@ -0,0 +1,999 @@ +/* + * refclock_omega - clock driver for the Kinemetrics Truetime OM-DC OMEGA + * receiver. + * + * Version 1.0 11-Dec-92 Steve Clift (clift@ml.csiro.au) + * Initial version, mostly lifted from refclock_goes.c. + * + * 1.1 03-May-93 Steve Clift + * Tarted up the sample filtering mechanism to give improved + * one-off measurements. Improved measurement dispersion code + * to account for accumulated drift when the clock loses lock. + * + */ + +#if defined(REFCLOCK) && (defined(OMEGA) || defined(OMEGACLK) || defined(OMEGAPPS)) + +#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" + +#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(OMEGACLK) +#include <sys/clkdefs.h> +#endif /* OMEGACLK */ +#endif /* STREAM */ + +#if defined (OMEGAPPS) +#include <sys/ppsclock.h> +#endif /* OMEGAPPS */ + +#include "ntp_stdlib.h" + +/* + * Support for Kinemetrics Truetime OM-DC OMEGA Receiver + * + * Most of this code is copied from refclock_goes.c with thanks. + * + * the time code looks like follows; Send the clock a R or C and once per + * second a timestamp will appear that looks like this: + * ADDD:HH:MM:SSQCL + * A - control A + * Q Quality indication: indicates possible error of + * > >+- 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. + * C - Carriage return + * L - Line feed + * The carriage return start bit begins on 0 seconds and extends to 1 bit time. + */ + +/* + * Definitions + */ +#define MAXUNITS 4 /* max number of OMEGA units */ +#define OMEGA232 "/dev/omega%d" +#define SPEED232 B9600 /* 9600 baud */ + +/* + * Radio interface parameters + */ +#define OMEGADESCRIPTION "Kinemetrics OM-DC OMEGA Receiver" /* who we are */ +#define OMEGAMAXDISPERSE (FP_SECOND/32) /* max allowed sample dispersion */ +#define OMEGAPRECISION (-10) /* precision assumed (about 1 ms) */ +#define OMEGAREFID "VLF\0" /* reference id */ +#define LENOMEGA 13 /* length of standard response */ +#define GMT 0 /* hour offset from Greenwich */ +#define NSTAMPS 9 /* samples collected when polled */ +#define NSKEEP 5 /* samples to keep after discards */ +#define BMAX 50 /* timecode buffer length */ + +/* + * The OM-DC puts out the start bit of the <CR> on the second, but + * we see the result after the <LF> is received, about 2ms later at + * 9600 baud. Use this as the default fudge time, and let the user + * fiddle it to account for driver latency etc. + */ +#define DEFFUDGETIME 0x00830000 /* default fudge time (~2ms) */ + +/* + * Clock drift errors as u_fp values. + */ +#define U_FP5000MS (5*FP_SECOND) /* 5 seconds */ +#define U_FP500MS (FP_SECOND/2) /* 500 msec */ +#define U_FP50MS (FP_SECOND/20) /* 50 msec */ +#define U_FP5MS (FP_SECOND/200) /* 5 msec */ + +/* + * Station codes + */ +#define STATION_NONE 0 +#define STATION_NORWAY 1 +#define STATION_LIBERIA 2 +#define STATION_HAWAII 3 +#define STATION_N_DAKOTA 4 +#define STATION_LA_REUNION 5 +#define STATION_ARGENTINA 6 +#define STATION_AUSTRALIA 7 +#define STATION_JAPAN 8 + +/* + * Hack to avoid excercising the multiplier. I have no pride. + */ +#define MULBY10(x) (((x)<<3) + ((x)<<1)) + +/* + * Imported from the timer module + */ +extern u_long current_time; +extern struct event timerqueue[]; + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * OMEGA unit control structure + */ +struct omegaunit { + struct peer *peer; /* associated peer structure */ + struct refclockio io; /* given to the I/O handler */ + l_fp lastrec; /* last receive time */ + l_fp lastref; /* last timecode time */ + l_fp offset[NSTAMPS]; /* recent sample offsets */ + char lastcode[BMAX]; /* last timecode received */ + u_short station; /* which station we're locked to */ + u_short polled; /* Hand in a time sample? */ + u_long coderecv; /* timecodes received */ + u_char lencode; /* length of last timecode */ + u_long lasttime; /* last time clock heard from */ + 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 failure */ + u_char year; /* year of eternity */ + u_short day; /* day of year */ + u_char hour; /* hour of day */ + u_char minute; /* minute of hour */ + u_char second; /* seconds of minute */ + u_char leap; /* leap indicators */ + u_short msec; /* millisecond of second */ + u_char quality; /* quality char from last timecode */ + u_long yearstart; /* start of current year */ + /* + * Status tallies + */ + u_long polls; /* polls sent */ + u_long noreply; /* no replies to polls */ + 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 omegaunit *omegaunits[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 fudgefactor1[MAXUNITS]; +static l_fp fudgefactor2[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static u_char readonlyclockflag[MAXUNITS]; +static u_long refid[MAXUNITS]; + +/* + * Function prototypes + */ +static void omega_init P((void)); +static int omega_start P((u_int, struct peer *)); +static void omega_shutdown P((int)); +static void omega_report_event P((struct omegaunit *, int)); +static void omega_receive P((struct recvbuf *)); +static char omega_process P((struct omegaunit *, l_fp *, u_fp *)); +static void omega_poll P((int, struct peer *)); +static void omega_control P((u_int, struct refclockstat *, struct refclockstat *)); +static void omega_buginfo P((int, struct refclockbug *)); +static void omega_send P((struct omegaunit *, char *)); + +/* + * Transfer vector + */ +struct refclock refclock_omega = { + omega_start, omega_shutdown, omega_poll, + omega_control, omega_init, omega_buginfo, NOFLAGS +}; + +/* + * omega_init - initialize internal omega driver data + */ +static void +omega_init() +{ + register int i; + /* + * Just zero the data arrays + */ + memset((char *)omegaunits, 0, sizeof omegaunits); + memset((char *)unitinuse, 0, sizeof unitinuse); + + /* + * Initialize fudge factors to default. + */ + for (i = 0; i < MAXUNITS; i++) { + fudgefactor1[i].l_ui = 0; + fudgefactor1[i].l_uf = DEFFUDGETIME; + fudgefactor2[i].l_ui = 0; + fudgefactor2[i].l_uf = 0; + stratumtouse[i] = 0; + readonlyclockflag[i] = 0; + memcpy((char *)&refid[i], OMEGAREFID, 4); + } +} + + +/* + * omega_start - open the OMEGA devices and initialize data for processing + */ +static int +omega_start(unit, peer) + u_int unit; + struct peer *peer; +{ + register struct omegaunit *omega; + register int i; + int fd232; + char omegadev[20]; + + /* + * Check configuration info + */ + if (unit >= MAXUNITS) { + syslog(LOG_ERR,"omega_start: unit %d invalid", unit); + return 0; + } + if (unitinuse[unit]) { + syslog(LOG_ERR, "omega_start: unit %d in use", unit); + return 0; + } + + /* + * Open serial port + */ + (void) sprintf(omegadev, OMEGA232, unit); + fd232 = open(omegadev, O_RDWR, 0777); + if (fd232 == -1) { + syslog(LOG_ERR, "omega_start: open of %s: %m", omegadev); + return 0; + } + +#if defined(HAVE_SYSV_TTYS) + /* + * System V serial line parameters (termio interface) + * + */ + { struct termio ttyb; + if (ioctl(fd232, TCGETA, &ttyb) < 0) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TCGETA): %m", omegadev); + 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) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TCSETA): %m", omegadev); + goto screwed; + } + } +#endif /* HAVE_SYSV_TTYS */ +#if defined(HAVE_TERMIOS) + /* + * POSIX serial line parameters (termios interface) + * + * The OMEGACLK option provides timestamping at the driver level. + * It requires the tty_clk streams module. + * + * The OMEGAPPS 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 SunOS 4.1.1 or + * later. + */ + { struct termios ttyb, *ttyp; + + ttyp = &ttyb; + if (tcgetattr(fd232, ttyp) < 0) { + syslog(LOG_ERR, + "omega_start: tcgetattr(%s): %m", omegadev); + 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) { + syslog(LOG_ERR, + "omega_start: tcsetattr(%s): %m", omegadev); + goto screwed; + } + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(LOG_ERR, + "omega_start: tcflush(%s): %m", omegadev); + goto screwed; + } + } +#endif /* HAVE_TERMIOS */ +#ifdef STREAM +#if defined(OMEGACLK) + if (ioctl(fd232, I_PUSH, "clk") < 0) + syslog(LOG_ERR, + "omega_start: ioctl(%s, I_PUSH, clk): %m", omegadev); + if (ioctl(fd232, CLK_SETSTR, "\n") < 0) + syslog(LOG_ERR, + "omega_start: ioctl(%s, CLK_SETSTR): %m", omegadev); +#endif /* OMEGACLK */ +#if defined(OMEGAPPS) + if (ioctl(fd232, I_PUSH, "ppsclock") < 0) + syslog(LOG_ERR, + "omega_start: ioctl(%s, I_PUSH, ppsclock): %m", omegadev); + else + fdpps = fd232; +#endif /* OMEGAPPS */ +#endif /* STREAM */ +#if defined(HAVE_BSD_TTYS) + /* + * 4.3bsd serial line parameters (sgttyb interface) + * + * The OMEGACLK option provides timestamping at the driver level. + * It requires the tty_clk line discipline and 4.3bsd or later. + */ + { struct sgttyb ttyb; +#if defined(OMEGACLK) + int ldisc = CLKLDISC; +#endif /* OMEGACLK */ + + if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TIOCGETP): %m", omegadev); + goto screwed; + } + ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; +#if defined(OMEGACLK) + 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 /* OMEGACLK */ + if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TIOCSETP): %m", omegadev); + goto screwed; + } +#if defined(OMEGACLK) + if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TIOCSETD): %m",omegadev); + goto screwed; + } +#endif /* OMEGACLK */ + } +#endif /* HAVE_BSD_TTYS */ + + /* + * Allocate unit structure + */ + if (omegaunits[unit] != 0) { + omega = omegaunits[unit]; /* The one we want is okay */ + } else { + for (i = 0; i < MAXUNITS; i++) { + if (!unitinuse[i] && omegaunits[i] != 0) + break; + } + if (i < MAXUNITS) { + /* + * Reclaim this one + */ + omega = omegaunits[i]; + omegaunits[i] = 0; + } else { + omega = (struct omegaunit *) + emalloc(sizeof(struct omegaunit)); + } + } + memset((char *)omega, 0, sizeof(struct omegaunit)); + omegaunits[unit] = omega; + + /* + * Set up the structures + */ + omega->peer = peer; + omega->unit = (u_char)unit; + omega->timestarted = current_time; + omega->station = STATION_NONE; + + omega->io.clock_recv = omega_receive; + omega->io.srcclock = (caddr_t)omega; + omega->io.datalen = 0; + omega->io.fd = fd232; + if (!io_addclock(&omega->io)) { + goto screwed; + } + + /* + * All done. Initialize a few random peer variables, then + * return success. + */ + peer->precision = OMEGAPRECISION; + peer->rootdelay = 0; + peer->rootdispersion = 0; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + unitinuse[unit] = 1; + return 1; + + /* + * Something broke; abandon ship + */ +screwed: + (void) close(fd232); + return 0; +} + + +/* + * omega_shutdown - shut down a OMEGA clock + */ +static void +omega_shutdown(unit) + int unit; +{ + register struct omegaunit *omega; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "omega_shutdown: unit %d invalid", + unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "omega_shutdown: unit %d not in use", unit); + return; + } + + /* + * Tell the I/O module to turn us off. We're history. + */ + omega = omegaunits[unit]; + io_closeclock(&omega->io); + unitinuse[unit] = 0; +} + + +/* + * omega_report_event - note the occurance of an event + */ +static void +omega_report_event(omega, code) + struct omegaunit *omega; + int code; +{ + struct peer *peer; + + peer = omega->peer; + if (omega->status != (u_char)code) { + omega->status = (u_char)code; + if (code != CEVNT_NOMINAL) + omega->lastevent = (u_char)code; + syslog(LOG_INFO, + "omega clock %s event %x\n", ntoa(&peer->srcadr), code); + } +} + + +/* + * omega_receive - receive data from the serial interface on a + * Kinemetrics OM-DC OMEGA clock. + */ +static void +omega_receive(rbufp) + struct recvbuf *rbufp; +{ + register int i; + register struct omegaunit *omega; + register u_char *dpt; + register char *cp, *cpend; + register u_char *dpend; + l_fp tstmp; + u_fp dispersion, drift; + + /* + * Get the clock this applies to and a pointers to the data + */ + omega = (struct omegaunit *)rbufp->recv_srcclock; + dpt = (u_char *)&rbufp->recv_space; + +#ifndef PEDANTIC + /* + * The OM-DC outputs a timecode every second, but we only want + * a set of NSTAMPS timecodes when polled (every 64 seconds). + * Setting PEDANTIC causes a sanity check on every timecode. + */ + if (!omega->polled) + return; +#endif + + /* + * Edit timecode to remove control chars + */ + dpend = dpt + rbufp->recv_length; + cp = omega->lastcode; + cpend = omega->lastcode + BMAX - 1; + while (dpt < dpend && cp < cpend) { + if ((*cp = 0x7f & *dpt++) >= ' ') cp++; +#ifdef OMEGACLK + else if (*cp == '\r') { + if (dpend - dpt < 8) { + /* short timestamp */ + return; + } + if (!buftvtots(dpt,&omega->lastrec)) { + /* screwy timestamp */ + return; + } + dpt += 8; + } +#endif + } + *cp = '\0'; + omega->lencode = cp - omega->lastcode; + + if (omega->lencode == 0) + return; + else if (omega->lencode != LENOMEGA) { + omega->badformat++; + /* Sometimes get a lot of these, filling the log with noise */ + /* omega_report_event(omega, CEVNT_BADREPLY); */ + return; + } + +#ifndef OMEGACLK + omega->lastrec = rbufp->recv_time; +#endif + +#ifdef DEBUG + if (debug) + printf("omega: timecode %d %s\n", + omega->lencode, omega->lastcode); +#endif + + /* + * We get down to business, check the timecode format + * and decode its contents. + */ + cp = omega->lastcode; + omega->leap = 0; + /* + * Check timecode format. + */ + if (!isdigit(cp[0]) || /* day of year */ + !isdigit(cp[1]) || + !isdigit(cp[2]) || + cp[3] != ':' || /* <sp> */ + !isdigit(cp[4]) || /* hours */ + !isdigit(cp[5]) || + cp[6] != ':' || /* : separator */ + !isdigit(cp[7]) || /* minutes */ + !isdigit(cp[8]) || + cp[9] != ':' || /* : separator */ + !isdigit(cp[10]) || /* seconds */ + !isdigit(cp[11])) { + omega->badformat++; + omega_report_event(omega, CEVNT_BADREPLY); + return; + } + + /* + * Convert and check values. + */ + omega->year = 0; /* fake */ + omega->day = cp[0] - '0'; + omega->day = MULBY10(omega->day) + cp[1] - '0'; + omega->day = MULBY10(omega->day) + cp[2] - '0'; + omega->hour = MULBY10(cp[4] - '0') + cp[5] - '0'; + omega->minute = MULBY10(cp[7] - '0') + cp[8] - '0'; + omega->second = MULBY10(cp[10] - '0') + cp[11] - '0'; + omega->msec = 0; + + if (omega->day < 1 || omega->day > 366) { + omega->baddata++; + omega_report_event(omega, CEVNT_BADDATE); + return; + } + if (omega->hour > 23 || omega->minute > 59 || omega->second > 59) { + omega->baddata++; + omega_report_event(omega, CEVNT_BADTIME); + return; + } + + /* + * Check quality/station-id flag. The OM-DC should normally stay + * permanently locked to a station, and its time error should be less + * than 1 msec. If it loses lock for any reason, it makes a worst + * case drift estimate based on the internally stored stability figure + * for its reference oscillator. The stability figure can be adjusted + * by the user based on experience. The default value is 1E05, which + * is pretty bad - 2E07 is about right for the unit I have. + * + * The following is arbitrary, change it if you're offended: + * For errors less than 50 msec, just clear the station indicator. + * For errors greater than 50 msec, flag loss of sync and report a + * propagation problem. If the error is greater than 500 msec, + * something is dreadfully wrong - report a clock fault. + * + * In each case, we set a drift estimate which is used below as an + * estimate of measurement accuracy. + */ + omega->quality = cp[12]; + if (cp[12] == '>' || cp[12] == '?') { + /* Error 500 to 5000 msec */ + omega_report_event(omega, CEVNT_FAULT); + omega->leap = LEAP_NOTINSYNC; + omega->station = STATION_NONE; + drift = U_FP5000MS; + } else if (cp[12] == '#') { + /* Error 50 to 500 msec */ + omega_report_event(omega, CEVNT_PROP); + omega->leap = LEAP_NOTINSYNC; + omega->station = STATION_NONE; + drift = U_FP500MS; + } else if (cp[12] == '*') { + /* Error 5 to 50 msec */ + omega->lasttime = current_time; + omega->station = STATION_NONE; + drift = U_FP50MS; + } else if (cp[12] == '.') { + /* Error 1 to 5 msec */ + omega->lasttime = current_time; + omega->station = STATION_NONE; + drift = U_FP5MS; + } else if ('A' <= cp[12] && cp[12] <= 'H') { + /* Error less than 1 msec */ + omega->lasttime = current_time; + omega->station = cp[12] - 'A' + 1; + drift = 0; + } else { + omega->badformat++; + omega_report_event(omega, CEVNT_BADREPLY); + return; + } + +#ifdef PEDANTIC + /* If we haven't been polled, bail out. */ + if (!omega->polled) + return; +#endif + + /* + * Now, compute the reference time value. Use the heavy + * machinery for the seconds and the millisecond field for the + * fraction when present. + * + * this code does not yet know how to do the years + */ + tstmp = omega->lastrec; + if (!clocktime(omega->day, omega->hour, omega->minute, + omega->second, GMT, tstmp.l_ui, + &omega->yearstart, &omega->lastref.l_ui)) { + omega->baddata++; + omega_report_event(omega, CEVNT_BADTIME); + return; + } + MSUTOTSF(omega->msec, omega->lastref.l_uf); + + /* + * Adjust the read value by fudgefactor1 to correct RS232 delays. + */ + L_ADD(&omega->lastref, &fudgefactor1[omega->unit]); + + /* Carousel of NSTAMPS offsets. */ + i = omega->coderecv % NSTAMPS; + omega->offset[i] = omega->lastref; + L_SUB(&omega->offset[i], &tstmp); + omega->coderecv++; + + /* If we don't yet have a full set, return. */ + if (omega->coderecv < NSTAMPS) + return; + + /* + * Filter the samples, add the fudge factor and pass the + * offset and dispersion along. 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 the sample filter chokes because of excessive + * dispersion or whatever, get a new sample (omega->coderecv + * is still >= NSTAMPS) and try again. + */ + if (!omega_process(omega, &tstmp, &dispersion)) { + omega->baddata++; + omega_report_event(omega, CEVNT_BADTIME); + return; + } + + /* + * Add accumulated clock drift to the dispersion to get + * a (hopefully) meaningful measurement accuracy estimate. + */ + dispersion += drift; + refclock_receive(omega->peer, &tstmp, GMT, dispersion, + &omega->lastrec, &omega->lastrec, omega->leap); + + /* + * We have succeeded in answering the poll. If the clock + * is locked, we're nominal. + */ + omega->polled = 0; + omega->coderecv = 0; + if (omega->leap != LEAP_NOTINSYNC) + omega_report_event(omega, CEVNT_NOMINAL); +} + + +/* + * omega_send - time to send the clock a signal to cough up a time sample + */ +static void +omega_send(omega,cmd) + struct omegaunit *omega; + char *cmd; +{ + if (!readonlyclockflag[omega->unit]) { + /* + * Send a command to the clock. + */ + if (write(omega->io.fd, cmd, 1) != 1) { + syslog(LOG_ERR, "omega_send: unit %d: %m", omega->unit); + omega_report_event(omega, CEVNT_FAULT); + } + } +} + + +/* + * Compare two l_fp's, used with qsort() + */ +static int +omega_cmpl_fp(p1, p2) + register void *p1, *p2; +{ + + if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2)) + return (-1); + if (L_ISEQU((l_fp *)p1, (l_fp *)p2)) + return (0); + return (1); +} + + +/* + * omega_process - process a pile of samples from the clock + */ +static char +omega_process(omega, offset, dispersion) + struct omegaunit *omega; + l_fp *offset; + u_fp *dispersion; +{ + register int i, n; + register u_long med_ui, med_uf, tmp_ui, tmp_uf; + l_fp off[NSTAMPS]; + u_fp disp; + + /* Copy in offsets and sort into ascending order */ + for (i = 0; i < NSTAMPS; i++) + off[i] = omega->offset[i]; + qsort((char *)off, NSTAMPS, sizeof(l_fp), omega_cmpl_fp); + /* + * Reject the furthest from the median until NSKEEP samples remain + */ + i = 0; + n = NSTAMPS; + while ((n - i) > NSKEEP) { + tmp_ui = off[n-1].l_ui; + tmp_uf = off[n-1].l_uf; + med_ui = off[(n+i)/2].l_ui; + med_uf = off[(n+i)/2].l_uf; + M_SUB(tmp_ui, tmp_uf, med_ui, med_uf); + M_SUB(med_ui, med_uf, off[i].l_ui, off[i].l_uf); + if (M_ISHIS(med_ui, med_uf, tmp_ui, tmp_uf)) { + /* reject low end */ + i++; + } else { + /* reject high end */ + n--; + } + } + + /* + * Compute the dispersion based on the difference between the + * extremes of the remaining offsets. If this is greater than + * the allowed sample set dispersion, bail out. Otherwise, + * return the median offset and the dispersion. + */ + tmp_ui = off[n-1].l_ui; + tmp_uf = off[n-1].l_uf; + M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf); + disp = MFPTOFP(tmp_ui, tmp_uf); + if (disp > OMEGAMAXDISPERSE) + return 0; + *offset = off[(n+1)/2]; + *dispersion = disp; + return 1; +} + + +/* + * omega_poll - called by the transmit procedure + */ +static void +omega_poll(unit, peer) + int unit; + struct peer *peer; +{ + struct omegaunit *omega; + + /* + * 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 (unit >= MAXUNITS) { + syslog(LOG_ERR, "omega_poll: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "omega_poll: unit %d not in use", unit); + return; + } + omega = omegaunits[unit]; + if ((current_time - omega->lasttime) > 150) { + omega->noreply++; + omega_report_event(omegaunits[unit], CEVNT_TIMEOUT); + } + + /* + * polled every 64 seconds. Ask OMEGA_RECEIVE to hand in a timestamp. + */ + omega->polled = 1; + omega->polls++; + /* + * Ensure the clock is running in the correct mode - on-second + * timestamps. + */ + omega_send(omega,"C"); +} + + +/* + * omega_control - set fudge factors, return statistics + */ +static void +omega_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct omegaunit *omega; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "omega_control: unit %d invalid", unit); + return; + } + + if (in != 0) { + if (in->haveflags & CLK_HAVETIME1) + fudgefactor1[unit] = in->fudgetime1; + if (in->haveflags & CLK_HAVETIME2) + fudgefactor2[unit] = in->fudgetime2; + if (in->haveflags & CLK_HAVEVAL1) + stratumtouse[unit] = (u_char)(in->fudgeval1); + if (in->haveflags & CLK_HAVEVAL2) + refid[unit] = in->fudgeval2; + if (in->haveflags & CLK_HAVEFLAG1) + readonlyclockflag[unit] = in->flags & CLK_FLAG1; + if (unitinuse[unit]) { + struct peer *peer; + + peer = omegaunits[unit]->peer; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + } + } + + if (out != 0) { + out->type = REFCLK_OMEGA_TRUETIME; + out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 | + CLK_HAVEVAL2| CLK_HAVEFLAG1; + out->clockdesc = OMEGADESCRIPTION; + out->fudgetime1 = fudgefactor1[unit]; + out->fudgetime2 = fudgefactor2[unit]; + out->fudgeval1 = stratumtouse[unit]; + out->fudgeval2 = refid[unit]; + out->flags = readonlyclockflag[unit]; + if (unitinuse[unit]) { + omega = omegaunits[unit]; + out->flags |= omega->station << 1; + out->lencode = omega->lencode; + out->lastcode = omega->lastcode; + out->timereset = current_time - omega->timestarted; + out->polls = omega->polls; + out->noresponse = omega->noreply; + out->badformat = omega->badformat; + out->baddata = omega->baddata; + out->lastevent = omega->lastevent; + out->currentstatus = omega->status; + } + } +} + + +/* + * omega_buginfo - return clock dependent debugging info + */ +static void +omega_buginfo(unit, bug) + int unit; + register struct refclockbug *bug; +{ + register struct omegaunit *omega; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "omega_buginfo: unit %d invalid", unit); + return; + } + + if (!unitinuse[unit]) + return; + omega = omegaunits[unit]; + + bug->nvalues = 11; + bug->ntimes = 5; + if (omega->lasttime != 0) + bug->values[0] = current_time - omega->lasttime; + else + bug->values[0] = 0; + bug->values[1] = omega->reason; + bug->values[2] = omega->year; + bug->values[3] = omega->day; + bug->values[4] = omega->hour; + bug->values[5] = omega->minute; + bug->values[6] = omega->second; + bug->values[7] = omega->msec; + bug->values[8] = omega->noreply; + bug->values[9] = omega->yearstart; + bug->values[10] = omega->quality; + bug->stimes = 0x1c; + bug->times[0] = omega->lastref; + bug->times[1] = omega->lastrec; + bug->times[2] = omega->offset[0]; + bug->times[3] = omega->offset[1]; + bug->times[4] = omega->offset[2]; +} +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_new/refclock_parse.c b/usr.sbin/xntpd/xntpd/refclock_new/refclock_parse.c new file mode 100644 index 000000000000..1b950a9b8e6f --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_new/refclock_parse.c @@ -0,0 +1,3617 @@ +#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) +/* + * /src/NTP/REPOSITORY/v3/xntpd/refclock_parse.c,v 3.59 1994/05/23 16:29:27 kardel Exp + * + * refclock_parse.c,v 3.59 1994/05/23 16:29:27 kardel Exp + * + * generic reference clock driver for receivers + * + * 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 (new - careful!) + * + * Copyright (c) 1989,1990,1991,1992,1993,1994 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * 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. + * + */ + +/* + * Defines: + * REFCLOCK && (PARSE||PARSEPPS) + * - enable this mess + * STREAM - allow for STREAMS modules + * ("parse", "ppsclocd", "ppsclock") + * PARSEPPS - provide PPS information to loopfilter (for + * backward compatibilty only) + * PPS - supply loopfilter with PPS samples (if configured) + * PPSPPS - notify loopfilter of PPS file descriptor + * + * TTY defines: + * HAVE_BSD_TTYS - currently unsupported + * HAVE_SYSV_TTYS - will use termio.h + * HAVE_TERMIOS - will use termios.h + * STREAM - will use streams and implies HAVE_TERMIOS + */ + +/* + * This driver currently provides the support for + * - Meinberg DCF77 receiver DCF77 PZF 535 (TCXO version) (DCF) + * - Meinberg DCF77 receiver DCF77 PZF 535 (OCXO version) (DCF) + * - Meinberg DCF77 receiver U/A 31 (DCF) + * - IGEL CLOCK (DCF) + * - ELV DCF7000 (DCF) + * - Schmid clock (DCF) + * - Conrad DCF77 receiver module (DCF) + * - FAU DCF77 NTP receiver (TimeBrick) (DCF) + * - Meinberg GPS166 (GPS) + * - Trimble SV6 (GPS) + * + */ + +/* + * Meinberg receivers are connected via a 9600 baud serial line + * + * Receivers that do NOT support: + * - leap second indication + * DCF U/A 31 + * DCF PZF535 (stock version) + * + * so... + * - for PZF535 please ask for revision PZFUERL4.6 or higher + * (support for leap second and alternate antenna) + * + * The Meinberg GPS receiver also has a special NTP time stamp + * format. The firmware release is Uni-Erlangen. Only this + * firmware release is supported by xntp3. + * + * Meinberg generic receiver setup: + * output time code every second + * Baud rate 9600 7E2S + */ + +#include "ntpd.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_control.h" + +#include <stdio.h> +#include <ctype.h> +#include <time.h> + +#include <sys/errno.h> +extern int errno; + +#if !defined(STREAM) && !defined(HAVE_SYSV_TTYS) && !defined(HAVE_BSD_TTYS) && !defined(HAVE_TERMIOS) +/* #error NEED TO DEFINE ONE OF "STREAM" or "HAVE_SYSV_TTYS" */ +NEED TO DEFINE ONE OF "STREAM", "HAVE_SYSV_TTYS" or "HAVE_TERMIOS" +#endif + +#ifdef STREAM +#include <sys/stream.h> +#include <sys/stropts.h> +#ifndef HAVE_TERMIOS +#define HAVE_TERMIOS +#endif +#endif + +#ifdef HAVE_TERMIOS +#include <termios.h> +#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 +#include <termio.h> +#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 */ +CURRENTLY NO BSD TTY SUPPORT +#endif + +#if !defined(O_RDWR) /* XXX SOLARIS */ +#include <fcntl.h> +#endif /* !def(O_RDWR) */ + +#ifdef PPSPPS +#include <sys/ppsclock.h> +#endif + +#include "ntp_select.h" +#include "ntp_stdlib.h" + +#include "parse.h" + +#if !defined(NO_SCCSID) && !defined(lint) && !defined(__GNUC__) +static char rcsid[]="refclock_parse.c,v 3.59 1994/05/23 16:29:27 kardel Exp"; +#endif + +/**=========================================================================== + ** external interface to xntp mechanism + **/ + +static void parse_init P((void)); +static int parse_start P((u_int, struct peer *)); +static void parse_shutdown P((int)); +static void parse_poll P((int, struct peer *)); +static void parse_control P((u_int, struct refclockstat *, struct refclockstat *)); + +#define parse_buginfo noentry + +struct refclock refclock_parse = { + parse_start, + parse_shutdown, + parse_poll, + parse_control, + parse_init, + parse_buginfo, + NOFLAGS +}; + +/* + * the unit field selects for one the prototype to be used (lower 4 bits) + * and for the other the clock type in case of different but similar + * receivers (bits 4-6) + * the most significant bit encodes PPS support + * when the most significant bit is set the pps telegrams will be used + * for controlling the local clock (ntp_loopfilter.c) + * receiver specific configration data is kept in the clockinfo field. + */ + +/* + * Definitions + */ +#define MAXUNITS 4 /* maximum number of "PARSE" units permitted */ +#define PARSEDEVICE "/dev/refclock-%d" /* device to open %d is unit number */ + +/**=========================================================================== + ** function vector for dynamically binding io handling mechanism + **/ + +typedef struct bind +{ + char *bd_description; /* name of type of binding */ + int (*bd_init)(); /* initialize */ + void (*bd_end)(); /* end */ + int (*bd_setcs)(); /* set character size */ + int (*bd_disable)(); /* disable */ + int (*bd_enable)(); /* enable */ + int (*bd_getfmt)(); /* get format */ + int (*bd_setfmt)(); /* setfmt */ + int (*bd_getstat)(); /* getstat */ + int (*bd_setstat)(); /* setstat */ + int (*bd_timecode)(); /* get time code */ + void (*bd_receive)(); /* receive operation */ + void (*bd_poll)(); /* poll 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_GETSTAT(_X_, _DCT_) (*(_X_)->binding->bd_getstat)(_X_, _DCT_) +#define PARSE_SETSTAT(_X_, _DCT_) (*(_X_)->binding->bd_setstat)(_X_, _DCT_) +#define PARSE_GETTIMECODE(_X_, _DCT_) (*(_X_)->binding->bd_timecode)(_X_, _DCT_) +#define PARSE_POLL(_X_) (*(_X_)->binding->bd_poll)(_X_) + +/* + * io modes + */ +#define PARSE_F_NOPOLLONLY 0x0001 /* always do async io (possible PPS support via PARSE) */ +#define PARSE_F_POLLONLY 0x0002 /* never do async io (no PPS support via PARSE) */ +#define PARSE_F_PPSPPS 0x0004 /* use loopfilter PPS code (CIOGETEV) */ +#define PARSE_F_PPSONSECOND 0x0008 /* PPS pulses are on second */ + +/**=========================================================================== + ** refclock instance data + **/ + +struct parseunit +{ + /* + * XNTP management + */ + struct peer *peer; /* backlink to peer structure - refclock inactive if 0 */ + int fd; /* device file descriptor */ + u_char unit; /* encoded unit/type/PPS */ + + /* + * XNTP io + */ + struct refclockio io; /* io system structure (used in PPS mode) */ + bind_t *binding; /* io handling binding */ + + /* + * parse state + */ + parse_t parseio; /* io handling structure (user level parsing) */ + + /* + * type specific parameters + */ + struct clockinfo *parse_type; /* link to clock description */ + + /* + * clock specific configuration + */ + l_fp basedelay; /* clock local phase offset */ + l_fp ppsdelay; /* clock local pps phase offset */ + + /* + * clock state handling/reporting + */ + u_char flags; /* flags (leap_control) */ + u_char status; /* current status */ + u_char lastevent; /* last not NORMAL status */ + u_long lastchange; /* time (xntp) when last state change accured */ + u_long statetime[CEVNT_MAX+1]; /* accumulated time of clock states */ + struct event stattimer; /* statistics timer */ + u_long polls; /* polls from NTP protocol machine */ + u_long noresponse; /* number of expected but not seen datagrams */ + u_long badformat; /* bad format (failed format conversions) */ + u_long baddata; /* usually bad receive length, bad format */ + + u_char pollonly; /* 1 for polling only (no PPS mode) */ + u_char pollneeddata; /* 1 for receive sample expected in PPS mode */ + u_long laststatus; /* last packet status (error indication) */ + u_short lastformat; /* last format used */ + u_long lastsync; /* time (xntp) when clock was last seen fully synchronized */ + u_long timestarted; /* time (xntp) when peer clock was instantiated */ + u_long nosynctime; /* time (xntp) when last nosync message was posted */ + u_long lastmissed; /* time (xntp) 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 data */ +}; + + +/**=========================================================================== + ** Clockinfo section all parameter for specific clock types + ** includes NTP paramaters, TTY parameters and IO handling parameters + **/ + +static void poll_dpoll P((struct parseunit *)); +static void poll_poll P((struct parseunit *)); +static int poll_init P((struct parseunit *)); +static void poll_end P((struct parseunit *)); + +typedef struct poll_info +{ + u_long rate; /* poll rate - once every "rate" seconds - 0 off */ + char * string; /* string to send for polling */ + u_long count; /* number of charcters in string */ +} poll_info_t; + +#define NO_FLAGS 0 +#define NO_POLL (void (*)())0 +#define NO_INIT (int (*)())0 +#define NO_END (void (*)())0 +#define NO_DATA (void *)0 +#define NO_FORMAT "" +#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 0x00000000 +#define NOCLOCK_BASEDELAY 0x00000000 +#define NOCLOCK_DESCRIPTION ((char *)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 NO_FORMAT +#define NOCLOCK_TYPE CTL_SST_TS_UNSPEC + +#define DCF_TYPE CTL_SST_TS_LF +#define GPS_TYPE CTL_SST_TS_UHF + +/* + * receiver specific constants + */ +#define MBG_CFLAG19200 (B19200|CS7|PARENB|CREAD|HUPCL) +#define MBG_CFLAG (B9600|CS7|PARENB|CREAD|HUPCL) +#define MBG_IFLAG (IGNBRK|IGNPAR|ISTRIP) +#define MBG_OFLAG 0 +#define MBG_LFLAG 0 +/* + * Meinberg DCF U/A 31 (AM) receiver + */ +#define DCFUA31_ROOTDELAY 0x00000D00 /* 50.78125ms */ +#define DCFUA31_BASEDELAY 0x02C00000 /* 10.7421875ms: 10 ms (+/- 3 ms) */ +#define DCFUA31_DESCRIPTION "Meinberg DCF U/A 31" +#define DCFUA31_MAXUNSYNC 60*30 /* only trust clock for 1/2 hour */ +#define DCFUA31_CFLAG MBG_CFLAG +#define DCFUA31_IFLAG MBG_IFLAG +#define DCFUA31_OFLAG MBG_OFLAG +#define DCFUA31_LFLAG MBG_LFLAG + +/* + * Meinberg DCF PZF535/TCXO (FM/PZF) receiver + */ +#define DCFPZF535_ROOTDELAY 0x00000034 /* 800us */ +#define DCFPZF535_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */ +#define DCFPZF535_DESCRIPTION "Meinberg DCF PZF 535/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_CFLAG MBG_CFLAG +#define DCFPZF535_IFLAG MBG_IFLAG +#define DCFPZF535_OFLAG MBG_OFLAG +#define DCFPZF535_LFLAG MBG_LFLAG + + +/* + * Meinberg DCF PZF535/OCXO receiver + */ +#define DCFPZF535OCXO_ROOTDELAY 0x00000034 /* 800us (max error * 10) */ +#define DCFPZF535OCXO_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */ +#define DCFPZF535OCXO_DESCRIPTION "Meinberg DCF PZF 535/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_CFLAG MBG_CFLAG +#define DCFPZF535OCXO_IFLAG MBG_IFLAG +#define DCFPZF535OCXO_OFLAG MBG_OFLAG +#define DCFPZF535OCXO_LFLAG MBG_LFLAG + +/* + * Meinberg GPS166 receiver + */ +#define GPS166_ROOTDELAY 0x00000000 /* nothing here */ +#define GPS166_BASEDELAY 0x00800000 /* XXX to be fixed ! 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */ +#define GPS166_DESCRIPTION "Meinberg GPS166 receiver" +#define GPS166_MAXUNSYNC 0 /* this clock is immediately lost */ +#define GPS166_CFLAG MBG_CFLAG +#define GPS166_IFLAG MBG_IFLAG +#define GPS166_OFLAG MBG_OFLAG +#define GPS166_LFLAG MBG_LFLAG +#define GPS166_POLL NO_POLL +#define GPS166_INIT NO_INIT +#define GPS166_END NO_END +#define GPS166_DATA NO_DATA +#define GPS166_ID GPS_ID +#define GPS166_FORMAT NO_FORMAT + +/* + * ELV DCF7000 Wallclock-Receiver/Switching Clock (Kit) + * + * This is really not the hottest clock - but before you have nothing ... + */ +#define DCF7000_ROOTDELAY 0x00000364 /* 13 ms */ +#define DCF7000_BASEDELAY 0x67AE0000 /* 405 ms - slow blow */ +#define DCF7000_DESCRIPTION "ELV DCF7000" +#define DCF7000_MAXUNSYNC (60*5) /* sorry - but it just was not build as a clock */ +#define DCF7000_CFLAG (B9600|CS8|CREAD|PARENB|PARODD|CLOCAL|HUPCL) +#define DCF7000_IFLAG (IGNBRK) +#define DCF7000_OFLAG 0 +#define DCF7000_LFLAG 0 + +/* + * 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 poll_end +#define WSDCF_DATA ((void *)(&wsdcf_pollinfo)) +#define WSDCF_ROOTDELAY 0X00004A3D /* ~ 290ms */ +#define WSDCF_BASEDELAY 0x028F5C29 /* ~ 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_CFLAG (B1200|CS8|CREAD|CLOCAL) +#define WSDCF_IFLAG 0 +#define WSDCF_OFLAG 0 +#define WSDCF_LFLAG 0 + +/* + * RAW DCF77 - input of DCF marks via RS232 - many variants + */ +#define RAWDCF_FLAGS PARSE_F_NOPOLLONLY +#define RAWDCF_ROOTDELAY 0x00000364 /* 13 ms */ +#define RAWDCF_FORMAT "RAW DCF77 Timecode" +#define RAWDCF_MAXUNSYNC (0) /* sorry - its a true receiver - no signal - no time */ +#define RAWDCF_CFLAG (B50|CS8|CREAD|CLOCAL|PARENB) +#define RAWDCF_IFLAG (IGNPAR) +#define RAWDCF_OFLAG 0 +#define RAWDCF_LFLAG 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 0x420C49B0 /* ~258 ms - Conrad receiver @ 50 Baud on a Sun */ +#define CONRAD_DESCRIPTION "RAW DCF77 CODE (Conrad DCF77 receiver module)" + +/* + * TimeBrick receiver + */ +#define TIMEBRICK_BASEDELAY 0x35C29000 /* ~210 ms - TimeBrick @ 50 Baud on a Sun */ +#define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)" + +/* + * IGEL:clock receiver + */ +#define IGELCLOCK_BASEDELAY 0x420C49B0 /* ~258 ms - IGEL:clock receiver */ +#define IGELCLOCK_DESCRIPTION "RAW DCF77 CODE (IGEL:clock)" +#define IGELCLOCK_CFLAG (B1200|CS8|CREAD|HUPCL|CLOCAL) + +/* + * Trimble SV6 GPS receiver + */ +#ifndef TRIM_POLLRATE +#define TRIM_POLLRATE 0 /* only true direct polling */ +#endif +#define TRIM_POLLCMD ">SRM;FR_FLAG=F<>QTM<" +#define TRIM_CMDSIZE 20 + +static poll_info_t trimble_pollinfo = { TRIM_POLLRATE, TRIM_POLLCMD, TRIM_CMDSIZE }; +static int trimble_init P((struct parseunit *)); + +#define TRIMBLESV6_CFLAG (B4800|CS8|CREAD) +#define TRIMBLESV6_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON) +#define TRIMBLESV6_OFLAG (OPOST|ONLCR) +#define TRIMBLESV6_LFLAG (ICANON|ECHOK) +#define TRIMBLESV6_FLAGS (PARSE_F_PPSPPS|PARSE_F_PPSONSECOND) +#define TRIMBLESV6_POLL poll_dpoll +#define TRIMBLESV6_INIT trimble_init +#define TRIMBLESV6_END poll_end +#define TRIMBLESV6_DATA ((void *)(&trimble_pollinfo)) +#define TRIMBLESV6_ID GPS_ID +#define TRIMBLESV6_FORMAT NO_FORMAT +#define TRIMBLESV6_ROOTDELAY 0x0 +#define TRIMBLESV6_BASEDELAY 0x0 +#define TRIMBLESV6_DESCRIPTION "Trimble SV6 GPS receiver" +#define TRIMBLESV6_MAXUNSYNC 0 +#define TRIMBLESV6_EOL '<' + +static struct clockinfo +{ + u_long cl_flags; /* operation flags (io modes) */ + void (*cl_poll)(); /* active poll routine */ + int (*cl_init)(); /* active poll init routine */ + void (*cl_end)(); /* active poll end routine */ + void *cl_data; /* local data area for "poll" mechanism */ + u_fp cl_rootdelay; /* rootdelay */ + u_long cl_basedelay; /* current offset - unsigned l_fp fractional part */ + u_long cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional part */ + char *cl_id; /* ID code (usually "DCF") */ + char *cl_description; /* device name */ + 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_cflag; /* terminal io flags */ + u_long cl_iflag; /* terminal io flags */ + u_long cl_oflag; /* terminal io flags */ + u_long cl_lflag; /* terminal io flags */ +} clockinfo[] = +{ /* 0. 0.0.128 - base offset for PPS support */ + { /* 127.127.8.<device> */ + NO_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + DCFPZF535_ROOTDELAY, + DCFPZF535_BASEDELAY, + NO_PPSDELAY, + DCF_P_ID, + DCFPZF535_DESCRIPTION, + NO_FORMAT, + DCF_TYPE, + DCFPZF535_MAXUNSYNC, + DCFPZF535_CFLAG, + DCFPZF535_IFLAG, + DCFPZF535_OFLAG, + DCFPZF535_LFLAG + }, + { /* 127.127.8.4+<device> */ + NO_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + DCFPZF535OCXO_ROOTDELAY, + DCFPZF535OCXO_BASEDELAY, + NO_PPSDELAY, + DCF_P_ID, + DCFPZF535OCXO_DESCRIPTION, + NO_FORMAT, + DCF_TYPE, + DCFPZF535OCXO_MAXUNSYNC, + DCFPZF535OCXO_CFLAG, + DCFPZF535OCXO_IFLAG, + DCFPZF535OCXO_OFLAG, + DCFPZF535OCXO_LFLAG + }, + { /* 127.127.8.8+<device> */ + NO_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + DCFUA31_ROOTDELAY, + DCFUA31_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + DCFUA31_DESCRIPTION, + NO_FORMAT, + DCF_TYPE, + DCFUA31_MAXUNSYNC, + DCFUA31_CFLAG, + DCFUA31_IFLAG, + DCFUA31_OFLAG, + DCFUA31_LFLAG + }, + { /* 127.127.8.12+<device> */ + NO_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + DCF7000_ROOTDELAY, + DCF7000_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + DCF7000_DESCRIPTION, + NO_FORMAT, + DCF_TYPE, + DCF7000_MAXUNSYNC, + DCF7000_CFLAG, + DCF7000_IFLAG, + DCF7000_OFLAG, + DCF7000_LFLAG + }, + { /* 127.127.8.16+<device> */ + NO_FLAGS, + WSDCF_POLL, + WSDCF_INIT, + WSDCF_END, + WSDCF_DATA, + WSDCF_ROOTDELAY, + WSDCF_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + WSDCF_DESCRIPTION, + WSDCF_FORMAT, + DCF_TYPE, + WSDCF_MAXUNSYNC, + WSDCF_CFLAG, + WSDCF_IFLAG, + WSDCF_OFLAG, + WSDCF_LFLAG + }, + { /* 127.127.8.20+<device> */ + RAWDCF_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + RAWDCF_ROOTDELAY, + CONRAD_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + CONRAD_DESCRIPTION, + RAWDCF_FORMAT, + DCF_TYPE, + RAWDCF_MAXUNSYNC, + RAWDCF_CFLAG, + RAWDCF_IFLAG, + RAWDCF_OFLAG, + RAWDCF_LFLAG + }, + { /* 127.127.8.24+<device> */ + RAWDCF_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + RAWDCF_ROOTDELAY, + TIMEBRICK_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + TIMEBRICK_DESCRIPTION, + RAWDCF_FORMAT, + DCF_TYPE, + RAWDCF_MAXUNSYNC, + RAWDCF_CFLAG, + RAWDCF_IFLAG, + RAWDCF_OFLAG, + RAWDCF_LFLAG + }, + { /* 127.127.8.28+<device> */ + NO_FLAGS, + GPS166_POLL, + GPS166_INIT, + GPS166_END, + GPS166_DATA, + GPS166_ROOTDELAY, + GPS166_BASEDELAY, + NO_PPSDELAY, + GPS166_ID, + GPS166_DESCRIPTION, + GPS166_FORMAT, + GPS_TYPE, + GPS166_MAXUNSYNC, + GPS166_CFLAG, + GPS166_IFLAG, + GPS166_OFLAG, + GPS166_LFLAG + }, + { /* 127.127.8.32+<device> */ + TRIMBLESV6_FLAGS, +#if TRIM_POLLRATE /* DHD940515: Allow user config */ + NO_POLL, +#else + TRIMBLESV6_POLL, +#endif + TRIMBLESV6_INIT, + TRIMBLESV6_END, + TRIMBLESV6_DATA, + TRIMBLESV6_ROOTDELAY, + TRIMBLESV6_BASEDELAY, + NO_PPSDELAY, + TRIMBLESV6_ID, + TRIMBLESV6_DESCRIPTION, + TRIMBLESV6_FORMAT, + GPS_TYPE, + TRIMBLESV6_MAXUNSYNC, + TRIMBLESV6_CFLAG, + TRIMBLESV6_IFLAG, + TRIMBLESV6_OFLAG, + TRIMBLESV6_LFLAG + }, + { /* 127.127.8.36+<device> */ + RAWDCF_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + RAWDCF_ROOTDELAY, + IGELCLOCK_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + IGELCLOCK_DESCRIPTION, + RAWDCF_FORMAT, + DCF_TYPE, + RAWDCF_MAXUNSYNC, + IGELCLOCK_CFLAG, + RAWDCF_IFLAG, + RAWDCF_OFLAG, + RAWDCF_LFLAG + } +}; + +static int ncltypes = sizeof(clockinfo) / sizeof(struct clockinfo); + +#define CL_REALTYPE(x) (((x) >> 2) & 0x1F) +#define CL_TYPE(x) ((CL_REALTYPE(x) >= ncltypes) ? ~0 : CL_REALTYPE(x)) +#define CL_PPS(x) ((x) & 0x80) +#define CL_UNIT(x) ((x) & 0x3) + +/* + * Other constant stuff + */ +#define PARSEHSREFID 0x7f7f08ff /* 127.127.8.255 refid for hi strata */ + +#define PARSENOSYNCREPEAT (10*60) /* mention uninitialized clocks all 10 minutes */ +#define PARSESTATISTICS (60*60) /* output state statistics every hour */ + +static struct parseunit *parseunits[MAXUNITS]; + +extern u_long current_time; +extern s_char sys_precision; +extern struct event timerqueue[]; +#ifdef PPSPPS +extern int fdpps; +#endif + +static int notice = 0; + +#define PARSE_STATETIME(parse, i) ((parse->status == 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 *)); + +/**=========================================================================== + ** 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_nop 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_getstat P((struct parseunit *, parsectl_t *)); +static int stream_setstat P((struct parseunit *, parsectl_t *)); +static int stream_timecode P((struct parseunit *, parsectl_t *)); +static void stream_receive P((struct recvbuf *)); +static void stream_poll P((struct parseunit *)); +#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_getstat P((struct parseunit *, parsectl_t *)); +static int local_setstat P((struct parseunit *, parsectl_t *)); +static int local_timecode P((struct parseunit *, parsectl_t *)); +static void local_receive P((struct recvbuf *)); +static void local_poll P((struct parseunit *)); + +static bind_t io_bindings[] = +{ +#ifdef STREAM + { + "parse STREAM", + stream_init, + stream_nop, + stream_setcs, + stream_disable, + stream_enable, + stream_getfmt, + stream_setfmt, + stream_getstat, + stream_setstat, + stream_timecode, + stream_receive, + stream_poll + }, + { + "ppsclock STREAM", + ppsclock_init, + local_end, + local_setcs, + local_nop, + local_nop, + local_getfmt, + local_setfmt, + local_getstat, + local_setstat, + local_timecode, + local_receive, + local_poll + }, +#endif + { + "normal", + local_init, + local_end, + local_setcs, + local_nop, + local_nop, + local_getfmt, + local_setfmt, + local_getstat, + local_setstat, + local_timecode, + local_receive, + local_poll + }, + { + (char *)0, + } +}; + +#ifdef STREAM +/*-------------------------------------------------- + * ppsclock STREAM init + */ +static int +ppsclock_init(parse) + struct parseunit *parse; +{ + /* + * now push the parse streams module + * it will ensure exclusive access to the device + */ + if (ioctl(parse->fd, I_PUSH, (caddr_t)"ppsclocd") == -1 && + ioctl(parse->fd, I_PUSH, (caddr_t)"ppsclock") == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: ppsclock_init: ioctl(fd, I_PUSH, \"ppsclock\"): %m", + CL_UNIT(parse->unit)); + return 0; + } + if (!local_init(parse)) + { + (void)ioctl(parse->fd, I_POP, (caddr_t)0); + return 0; + } + + parse->flags |= PARSE_PPSCLOCK; + return 1; +} + +/*-------------------------------------------------- + * parse STREAM init + */ +static int +stream_init(parse) + struct parseunit *parse; +{ + /* + * now push the parse streams module + * to test whether it is there (Oh boy - neat kernel interface) + */ + if (ioctl(parse->fd, I_PUSH, (caddr_t)"parse") == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CL_UNIT(parse->unit)); + return 0; + } + else + { + while(ioctl(parse->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->fd, I_PUSH, (caddr_t)"parse") == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CL_UNIT(parse->unit)); + return 0; + } + else + { + return 1; + } + } +} + + /*-------------------------------------------------- + * STREAM setcs + */ +static int +stream_setcs(parse, tcl) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_setcs: ioctl(fd, I_STR, PARSEIOC_SETCS): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM nop + */ +static void +stream_nop(parse) + struct parseunit *parse; +{ +} + +/*-------------------------------------------------- + * STREAM enable + */ +static int +stream_enable(parse) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_enable: ioctl(fd, I_STR, PARSEIOC_ENABLE): %m", CL_UNIT(parse->unit)); + return 0; + } + parse->io.clock_recv = stream_receive; /* ok - parse input in kernel */ + return 1; +} + +/*-------------------------------------------------- + * STREAM disable + */ +static int +stream_disable(parse) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_disable: ioctl(fd, I_STR, PARSEIOC_DISABLE): %m", CL_UNIT(parse->unit)); + return 0; + } + parse->io.clock_recv = local_receive; /* ok - parse input in daemon */ + return 1; +} + +/*-------------------------------------------------- + * STREAM getfmt + */ +static int +stream_getfmt(parse, tcl) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: ioctl(fd, I_STR, PARSEIOC_GETFMT): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM setfmt + */ +static int +stream_setfmt(parse, tcl) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_setfmt: ioctl(fd, I_STR, PARSEIOC_SETFMT): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM getstat + */ +static int +stream_getstat(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + struct strioctl strioc; + + strioc.ic_cmd = PARSEIOC_GETSTAT; + strioc.ic_timout = 0; + strioc.ic_dp = (char *)tcl; + strioc.ic_len = sizeof (*tcl); + + if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_getstat: ioctl(fd, I_STR, PARSEIOC_GETSTAT): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM setstat + */ +static int +stream_setstat(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + struct strioctl strioc; + + strioc.ic_cmd = PARSEIOC_SETSTAT; + strioc.ic_timout = 0; + strioc.ic_dp = (char *)tcl; + strioc.ic_len = sizeof (*tcl); + + if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_setstat: ioctl(fd, I_STR, PARSEIOC_SETSTAT): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM timecode + */ +static int +stream_timecode(parse, tcl) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_process: ioctl(fd, I_STR, PARSEIOC_TIMECODE): %m", CL_UNIT(parse->unit), parse->fd); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM receive + */ +static void +stream_receive(rbufp) + struct recvbuf *rbufp; +{ + struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock; + parsetime_t parsetime; + + if (rbufp->recv_length != sizeof(parsetime_t)) + { + syslog(LOG_ERR,"PARSE receiver #%d: parse_receive: bad size (got %d expected %d)", + CL_UNIT(parse->unit), rbufp->recv_length, sizeof(parsetime_t)); + parse->baddata++; + parse_event(parse, CEVNT_BADREPLY); + return; + } + memmove((caddr_t)&parsetime, + (caddr_t)&rbufp->recv_space, + sizeof(parsetime_t)); + + /* + * switch time stamp world - be sure to normalize small usec field + * errors. + */ + +#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)) \ + { \ + syslog(LOG_ERR,"parse: stream_receive: timestamp conversion error (buftvtots) (%s) (%d.%06d) ", (_Y_), (&(_X_))->tv.tv_sec, (&(_X_))->tv.tv_usec);\ + return; \ + } \ + else \ + { \ + (&(_X_))->fp = ts; \ + } \ + } + + if (PARSE_TIMECODE(parsetime.parse_state)) + { + cvt_ts(parsetime.parse_time, "parse_time"); + cvt_ts(parsetime.parse_stime, "parse_stime"); + } + + if (PARSE_PPS(parsetime.parse_state)) + cvt_ts(parsetime.parse_ptime, "parse_ptime"); + + parse_process(parse, &parsetime); +} + +/*-------------------------------------------------- + * STREAM poll + */ +static void +stream_poll(parse) + struct parseunit *parse; +{ + register int fd, i, rtc; + fd_set fdmask; + struct timeval timeout, starttime, curtime, selecttime; + parsetime_t parsetime; + + /* + * now we do the following: + * - read the first packet from the parse module (OLD !!!) + * - read the second packet from the parse module (fresh) + * - compute values for xntp + */ + + FD_ZERO(&fdmask); + fd = parse->fd; + FD_SET(fd, &fdmask); + timeout.tv_sec = 0; + timeout.tv_usec = 500000; /* 0.5 sec */ + + if (parse->parse_type->cl_poll) + { + parse->parse_type->cl_poll(parse); + } + + if (GETTIMEOFDAY(&starttime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + + selecttime = timeout; + + while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1) + { + /* no data from the radio clock */ + + if (rtc == -1) + { + if (errno == EINTR) + { + if (GETTIMEOFDAY(&curtime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec; + if (curtime.tv_usec < starttime.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec; + } + else + { + selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec; + } + + + if (timercmp(&selecttime, &timeout, >)) + { + /* + * elapsed real time passed timeout value - consider it timed out + */ + break; + } + + /* + * calculate residual timeout value + */ + selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec; + + if (selecttime.tv_usec > timeout.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec; + } + else + { + selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec; + } + + FD_SET(fd, &fdmask); + continue; + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device (select() error: %m)", CL_UNIT(parse->unit)); + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device", CL_UNIT(parse->unit)); + } + parse->noresponse++; + parse->lastmissed = current_time; + parse_event(parse, CEVNT_TIMEOUT); + + return; + } + + while (((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime))) + { + /* bad packet */ + if ( i == -1) + { + if (errno == EINTR) + { + continue; + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (read() error: %m)", CL_UNIT(parse->unit), i, sizeof(parsetime)); + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (got %d bytes - expected %d bytes)", CL_UNIT(parse->unit), i, sizeof(parsetime)); + } + parse->baddata++; + parse_event(parse, CEVNT_BADREPLY); + + return; + } + + if (parse->parse_type->cl_poll) + { + parse->parse_type->cl_poll(parse); + } + + timeout.tv_sec = 1; + timeout.tv_usec = 500000; /* 1.500 sec */ + FD_ZERO(&fdmask); + FD_SET(fd, &fdmask); + + if (GETTIMEOFDAY(&starttime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + + selecttime = timeout; + + while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1) + { + /* no data from the radio clock */ + + if (rtc == -1) + { + if (errno == EINTR) + { + if (GETTIMEOFDAY(&curtime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec; + if (curtime.tv_usec < starttime.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec; + } + else + { + selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec; + } + + + if (timercmp(&selecttime, &timeout, >)) + { + /* + * elapsed real time passed timeout value - consider it timed out + */ + break; + } + + /* + * calculate residual timeout value + */ + selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec; + + if (selecttime.tv_usec > timeout.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec; + } + else + { + selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec; + } + + FD_SET(fd, &fdmask); + continue; + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device (select() error: %m)", CL_UNIT(parse->unit)); + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device", CL_UNIT(parse->unit)); + } + + /* + * we will return here iff we got a good old sample as this would + * be misinterpreted. bad samples are passed on to be logged into the + * state statistics + */ + if ((parsetime.parse_status & CVT_MASK) == CVT_OK) + { + parse->noresponse++; + parse->lastmissed = current_time; + parse_event(parse, CEVNT_TIMEOUT); + return; + } + } + + /* + * we get here either by a possible read() (rtc == 1 - while assertion) + * or by a timeout or a system call error. when a read() is possible we + * get the new data, otherwise we stick with the old + */ + if ((rtc == 1) && ((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime))) + { + /* bad packet */ + if ( i== -1) + { + syslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (read() error: %m)", CL_UNIT(parse->unit), i, sizeof(parsetime)); + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (got %d bytes - expected %d bytes)", CL_UNIT(parse->unit), i, sizeof(parsetime)); + } + parse->baddata++; + parse_event(parse, CEVNT_BADREPLY); + + return; + } + + /* + * process what we got + */ + parse_process(parse, &parsetime); +} +#endif + +/*-------------------------------------------------- + * local init + */ +static int +local_init(parse) + struct parseunit *parse; +{ + return parse_ioinit(&parse->parseio); +} + +/*-------------------------------------------------- + * local end + */ +static void +local_end(parse) + struct parseunit *parse; +{ + parse_ioend(&parse->parseio); +} + + +/*-------------------------------------------------- + * local nop + */ +static int +local_nop(parse) + struct parseunit *parse; +{ + return 1; +} + +/*-------------------------------------------------- + * local setcs + */ +static int +local_setcs(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_setcs(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local getfmt + */ +static int +local_getfmt(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_getfmt(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local setfmt + */ +static int +local_setfmt(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_setfmt(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local getstat + */ +static int +local_getstat(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_getstat(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local setstat + */ +static int +local_setstat(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_setstat(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local timecode + */ +static int +local_timecode(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_timecode(tcl, &parse->parseio); +} + + +/*-------------------------------------------------- + * local receive + */ +static void +local_receive(rbufp) + struct recvbuf *rbufp; +{ + struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock; + register int count; + register char *s; + /* + * eat all characters, parsing then and feeding complete samples + */ + count = rbufp->recv_length; + s = rbufp->recv_buffer; + + while (count--) + { + if (parse_ioread(&parse->parseio, *s++, &rbufp->recv_time)) + { + /* + * got something good to eat + */ +#ifdef PPSPPS + if (!PARSE_PPS(parse->parseio.parse_dtime.parse_state) && + (parse->flags & PARSE_PPSCLOCK)) + { + l_fp ts; + struct ppsclockev ev; + + if (ioctl(parse->fd, CIOGETEV, (caddr_t)&ev) == 0) + { + if (ev.serial != parse->ppsserial) + { + /* + * add PPS time stamp if available via ppsclock module + * and not supplied already. + */ + if (!buftvtots((const char *)&ev.tv, &ts)) + { + syslog(LOG_ERR,"parse: local_receive: timestamp conversion error (buftvtots) (ppsclockev.tv)"); + } + else + { + parse->parseio.parse_dtime.parse_ptime.fp = ts; + parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS; + } + } + parse->ppsserial = ev.serial; + } + } +#endif + parse_process(parse, &parse->parseio.parse_dtime); + parse_iodone(&parse->parseio); + } + } +} + +/*-------------------------------------------------- + * local poll + */ +static void +local_poll(parse) + struct parseunit *parse; +{ + register int fd, i, rtc; + fd_set fdmask; + struct timeval timeout, starttime, curtime, selecttime; + static struct timeval null_time = { 0, 0}; + timestamp_t ts; + + FD_ZERO(&fdmask); + fd = parse->fd; + FD_SET(fd, &fdmask); + timeout.tv_sec = 1; + timeout.tv_usec = 500000; /* 1.5 sec */ + + if (parse->parse_type->cl_poll) + { + parse->parse_type->cl_poll(parse); + } + + if (GETTIMEOFDAY(&starttime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + + selecttime = timeout; + + do + { + while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1) + { + /* no data from the radio clock */ + + if (rtc == -1) + { + if (errno == EINTR) + { + if (GETTIMEOFDAY(&curtime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec; + if (curtime.tv_usec < starttime.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec; + } + else + { + selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec; + } + + + if (!timercmp(&selecttime, &timeout, >)) + { + /* + * calculate residual timeout value + */ + selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec; + + if (selecttime.tv_usec > timeout.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec; + } + else + { + selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec; + } + + FD_SET(fd, &fdmask); + continue; + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data from device (select() error: %m)", CL_UNIT(parse->unit)); + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data from device", CL_UNIT(parse->unit)); + } + + parse->noresponse++; + parse->lastmissed = current_time; + parse_event(parse, CEVNT_TIMEOUT); + + return; + } + + /* + * at least 1 character is available - gobble everthing up that is available + */ + do + { + char inbuf[256]; + + register char *s = inbuf; + + rtc = i = read(fd, inbuf, sizeof(inbuf)); + + get_systime(&ts.fp); + + while (i-- > 0) + { + if (parse_ioread(&parse->parseio, *s++, &ts)) + { + /* + * got something good to eat + */ + parse_process(parse, &parse->parseio.parse_dtime); + parse_iodone(&parse->parseio); + /* + * done if no more characters are available + */ + FD_SET(fd, &fdmask); + if ((i == 0) && + (select(fd + 1, &fdmask, 0, 0, &null_time) == 0)) + return; + } + } + FD_SET(fd, &fdmask); + } while ((rtc = select(fd + 1, &fdmask, 0, 0, &null_time)) == 1); + FD_SET(fd, &fdmask); + } while (1); +} + +/*-------------------------------------------------- + * init_iobinding - find and initialize lower layers + */ +static bind_t * +init_iobinding(parse) + struct parseunit *parse; +{ + register 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(state, buffer) + u_long state; + char *buffer; +{ + static struct bits + { + u_long bit; + 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; + 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 & state) + { + if (buffer[0]) + strcat(buffer, "; "); + strcat(buffer, flagstrings[i].name); + } + i++; + } + + if (state & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION)) + { + register 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 & state) + { + 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(state, buffer) + u_long state; + char *buffer; +{ + static struct bits + { + u_long bit; + 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" }, + { 0 } + }; + int i; + + *buffer = '\0'; + + i = 0; + while (flagstrings[i].bit) + { + if (flagstrings[i].bit & state) + { + if (buffer[0]) + strcat(buffer, "; "); + strcat(buffer, flagstrings[i].name); + } + i++; + } + + return buffer; +} + +/*-------------------------------------------------- + * convert a clock status flag field to a string + */ +static char * +clockstatus(state) + u_long state; +{ + static char buffer[20]; + static struct status + { + u_long value; + 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" }, + { ~0 } + }; + int i; + + i = 0; + while (flagstrings[i].value != ~0) + { + if (flagstrings[i].value == state) + { + return flagstrings[i].name; + } + i++; + } + + sprintf(buffer, "unknown #%ld", (u_long)state); + + return buffer; +} + +/*-------------------------------------------------- + * mkascii - make a printable ascii string + * assumes (unless defined better) 7-bit ASCII + */ +#ifndef isprint +#define isprint(_X_) (((_X_) > 0x1F) && ((_X_) < 0x7F)) +#endif + +static char * +mkascii(buffer, blen, src, srclen) + register char *buffer; + register long blen; + register char *src; + register long srclen; +{ + register char *b = buffer; + register 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 ((*src != '\\') && isprint(*src)) + { /* printables 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; + } + 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; +} + + +/*-------------------------------------------------- + * l_mktime - make representation of a relative time + */ +static char * +l_mktime(delta) + 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(parse) + register struct parseunit *parse; +{ + register int i; + + syslog(LOG_INFO, "PARSE receiver #%d: running time: %s", + CL_UNIT(parse->unit), + l_mktime(current_time - parse->timestarted)); + + syslog(LOG_INFO, "PARSE receiver #%d: current status: %s", + CL_UNIT(parse->unit), + clockstatus(parse->status)); + + for (i = 0; i <= CEVNT_MAX; i++) + { + register u_long stime; + register u_long percent, div = current_time - parse->timestarted; + + percent = stime = PARSE_STATETIME(parse, i); + + while (((u_long)(~0) / 10000) < percent) + { + percent /= 10; + div /= 10; + } + + if (div) + percent = (percent * 10000) / div; + else + percent = 10000; + + if (stime) + syslog(LOG_INFO, "PARSE receiver #%d: state %18s: %13s (%3d.%02d%%)", + CL_UNIT(parse->unit), + clockstatus(i), + l_mktime(stime), + percent / 100, percent % 100); + } +} + +/*-------------------------------------------------- + * cparse_statistics - wrapper for statistics call + */ +static void +cparse_statistics(peer) + register struct peer *peer; +{ + register struct parseunit *parse = (struct parseunit *)peer; + + parse_statistics(parse); + parse->stattimer.event_time = current_time + PARSESTATISTICS; + TIMER_ENQUEUE(timerqueue, &parse->stattimer); +} + +/**=========================================================================== + ** xntp interface routines + **/ + +/*-------------------------------------------------- + * parse_init - initialize internal parse driver data + */ +static void +parse_init() +{ + memset((caddr_t)parseunits, 0, sizeof parseunits); +} + + +/*-------------------------------------------------- + * parse_shutdown - shut down a PARSE clock + */ +static void +parse_shutdown(unit) + int unit; +{ + register struct parseunit *parse; + + unit = CL_UNIT(unit); + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, + "PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit invalid (max %d)", + unit,MAXUNITS); + return; + } + + parse = parseunits[unit]; + + if (parse && !parse->peer) { + syslog(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); + TIMER_DEQUEUE(&parse->stattimer); + +#if PPSPPS + { + /* + * kill possible PPS association + */ + if (fdpps == parse->fd) + fdpps = -1; + } +#endif + + 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. + */ + if (!parse->pollonly) + io_closeclock(&parse->io); + else + (void) close(parse->fd); + + syslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" removed", + CL_UNIT(parse->unit), parse->parse_type->cl_description); + + parse->peer = (struct peer *)0; /* unused now */ +} + +/*-------------------------------------------------- + * parse_start - open the PARSE devices and initialize data for processing + */ +static int +parse_start(sysunit, peer) + u_int sysunit; + struct peer *peer; +{ + u_int unit; + int fd232, i; +#ifdef HAVE_TERMIOS + struct termios tm; /* NEEDED FOR A LONG TIME ! */ +#endif +#ifdef HAVE_SYSV_TTYS + struct termio tm; /* NEEDED FOR A LONG TIME ! */ +#endif + struct parseunit * parse; + char parsedev[sizeof(PARSEDEVICE)+20]; + parsectl_t tmp_ctl; + u_int type; + + type = CL_TYPE(sysunit); + unit = CL_UNIT(sysunit); + + if (unit >= MAXUNITS) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit number invalid (max %d)", + unit, MAXUNITS-1); + return 0; + } + + if ((type == ~0) || (clockinfo[type].cl_description == (char *)0)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unsupported clock type %d (max %d)", + unit, CL_REALTYPE(sysunit), ncltypes-1); + return 0; + } + + if (parseunits[unit] && parseunits[unit]->peer) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit in use", unit); + 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, 0777); + if (fd232 == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: open of %s failed: %m", unit, parsedev); + return 0; + } + + /* + * 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 (parseunits[unit]) + { + parse = parseunits[unit]; /* The one we want is okay - and free */ + } + else + { + for (i = 0; i < MAXUNITS; i++) + { + if (parseunits[i] && !parseunits[i]->peer) + break; + } + if (i < MAXUNITS) + { + /* + * Reclaim this one + */ + parse = parseunits[i]; + parseunits[i] = (struct parseunit *)0; + } + else + { + parse = (struct parseunit *) + emalloc(sizeof(struct parseunit)); + } + } + + memset((char *)parse, 0, sizeof(struct parseunit)); + parseunits[unit] = parse; + + /* + * Set up the structures + */ + parse->unit = (u_char)sysunit; + parse->timestarted = current_time; + parse->lastchange = current_time; + /* + * we want to filter input for the sake of + * getting an impression on dispersion + * also we like to average the median range + */ + parse->flags = PARSE_STAT_FILTER|PARSE_STAT_AVG; + parse->pollneeddata = 0; + parse->pollonly = 1; /* go for default polling mode */ + parse->lastformat = ~0; /* assume no format known */ + parse->status = CEVNT_TIMEOUT; /* expect the worst */ + parse->laststatus = ~0; /* be sure to mark initial status change */ + parse->nosynctime = 0; /* assume clock reasonable */ + parse->lastmissed = 0; /* assume got everything */ + parse->ppsserial = 0; + parse->localdata = (void *)0; + + parse->parse_type = &clockinfo[type]; + + parse->basedelay.l_ui = 0; /* we can only pre-configure delays less than 1 second */ + parse->basedelay.l_uf = parse->parse_type->cl_basedelay; + + parse->ppsdelay.l_ui = 0; /* we can only pre-configure delays less than 1 second */ + parse->ppsdelay.l_uf = parse->parse_type->cl_ppsdelay; + + peer->rootdelay = parse->parse_type->cl_rootdelay; + peer->sstclktype = parse->parse_type->cl_type; + peer->precision = sys_precision; + peer->stratum = STRATUM_REFCLOCK; + if (peer->stratum <= 1) + memmove((char *)&peer->refid, parse->parse_type->cl_id, 4); + else + peer->refid = htonl(PARSEHSREFID); + + parse->fd = fd232; + + parse->peer = peer; /* marks it also as busy */ + + parse->binding = init_iobinding(parse); + + if (parse->binding == (bind_t *)0) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: io sub system initialisation failed."); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; /* well, ok - special initialisation broke */ + } + + /* + * configure terminal line + */ + if (TTY_GETATTR(fd232, &tm) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcgetattr(%d, &tm): %m", unit, fd232); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; + } + else + { +#ifndef _PC_VDISABLE + memset((char *)tm.c_cc, 0, sizeof(tm.c_cc)); +#else + int disablec; + errno = 0; /* pathconf can deliver -1 without changing errno ! */ + + disablec = fpathconf(parse->fd, _PC_VDISABLE); + if (disablec == -1 && errno) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: fpathconf(fd, _PC_VDISABLE): %m", CL_UNIT(parse->unit)); + memset((char *)tm.c_cc, 0, sizeof(tm.c_cc)); /* best guess */ + } + else + if (disablec != -1) + memset((char *)tm.c_cc, disablec, sizeof(tm.c_cc)); +#endif + + tm.c_cflag = clockinfo[type].cl_cflag; + tm.c_iflag = clockinfo[type].cl_iflag; + tm.c_oflag = clockinfo[type].cl_oflag; + tm.c_lflag = clockinfo[type].cl_lflag; + + if (TTY_SETATTR(fd232, &tm) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcsetattr(%d, &tm): %m", unit, fd232); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; + } + } + + /* + * 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 (tm.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)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setcs() FAILED.", unit); + parse_shutdown(parse->unit); /* 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)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setfmt() FAILED.", unit); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; /* well, ok - special initialisation broke */ + } + +#ifdef TCFLSH + /* + * get rid of all IO accumulated so far + */ + { +#ifndef TCIOFLUSH +#define TCIOFLUSH 2 +#endif + int flshcmd = TCIOFLUSH; + + (void) ioctl(parse->fd, TCFLSH, (caddr_t)&flshcmd); + } +#endif + + tmp_ctl.parsestatus.flags = parse->flags & PARSE_STAT_FLAGS; + + if (!PARSE_SETSTAT(parse, &tmp_ctl)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setstat() FAILED.", unit); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; /* well, ok - special initialisation broke */ + } + + /* + * try to do any special initializations + */ + if (parse->parse_type->cl_init) + { + if (parse->parse_type->cl_init(parse)) + { + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; /* well, ok - special initialisation broke */ + } + } + + if (!(parse->parse_type->cl_flags & PARSE_F_POLLONLY) && + (CL_PPS(parse->unit) || (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY))) + { + /* + * Insert in async io device list. + */ + parse->io.clock_recv = parse->binding->bd_receive; /* pick correct receive routine */ + parse->io.srcclock = (caddr_t)parse; + parse->io.datalen = 0; + parse->io.fd = parse->fd; /* replicated, but what the heck */ + if (!io_addclock(&parse->io)) + { + if (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY) + { + syslog(LOG_ERR, + "PARSE receiver #%d: parse_start: addclock %s fails (ABORT - clock type requires async io)", CL_UNIT(parse->unit), parsedev); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; + } + else + { + syslog(LOG_ERR, + "PARSE receiver #%d: parse_start: addclock %s fails (switching to polling mode)", CL_UNIT(parse->unit), parsedev); + } + } + else + { + parse->pollonly = 0; /* + * update at receipt of time_stamp - also + * supports PPS processing + */ + } + } + +#ifdef PPSPPS + if (parse->pollonly && (parse->parse_type->cl_flags & PARSE_F_PPSPPS)) + { + if (fdpps == -1) + { + fdpps = parse->fd; + if (!PARSE_DISABLE(parse)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_disable() FAILED", CL_UNIT(parse->unit)); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; + } + } + else + { + syslog(LOG_NOTICE, "PARSE receiver #%d: parse_start: loopfilter PPS already active - no PPS via CIOGETEV", CL_UNIT(parse->unit)); + } + } +#endif + + /* + * wind up statistics timer + */ + parse->stattimer.peer = (struct peer *)parse; /* we know better, but what the heck */ + parse->stattimer.event_handler = cparse_statistics; + parse->stattimer.event_time = current_time + PARSESTATISTICS; + TIMER_ENQUEUE(timerqueue, &parse->stattimer); + + /* + * get out Copyright information once + */ + if (!notice) + { + syslog(LOG_INFO, "NTP PARSE support: Copyright (c) 1989-1993, Frank Kardel"); + notice = 1; + } + + /* + * print out configuration + */ + syslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" (device %s) added", + CL_UNIT(parse->unit), + parse->parse_type->cl_description, parsedev); + + syslog(LOG_INFO, "PARSE receiver #%d: Stratum %d, %sPPS support, trust time %s, precision %d", + CL_UNIT(parse->unit), + parse->peer->stratum, (parse->pollonly || !CL_PPS(parse->unit)) ? "no " : "", + l_mktime(parse->parse_type->cl_maxunsync), parse->peer->precision); + + syslog(LOG_INFO, "PARSE receiver #%d: rootdelay %s s, phaseadjust %s s, %s IO handling", + CL_UNIT(parse->unit), + ufptoa(parse->parse_type->cl_rootdelay, 6), + lfptoa(&parse->basedelay, 8), + parse->binding->bd_description); + + syslog(LOG_INFO, "PARSE receiver #%d: Format recognition: %s", CL_UNIT(parse->unit), + !(*parse->parse_type->cl_format) ? "<AUTOMATIC>" : parse->parse_type->cl_format); + +#ifdef PPSPPS + syslog(LOG_INFO, "PARSE receiver #%d: %sCD PPS support", + CL_UNIT(parse->unit), + (fdpps == parse->fd) ? "" : "NO "); +#endif + + return 1; +} + +/*-------------------------------------------------- + * parse_poll - called by the transmit procedure + */ +static void +parse_poll(unit, peer) + int unit; + struct peer *peer; +{ + register struct parseunit *parse; + + unit = CL_UNIT(unit); + + if (unit >= MAXUNITS) + { + syslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit invalid", + unit); + return; + } + + parse = parseunits[unit]; + + if (!parse->peer) + { + syslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit unused", + unit); + return; + } + + if (peer != parse->peer) + { + syslog(LOG_ERR, + "PARSE receiver #%d: poll: INTERNAL: peer incorrect", + unit); + return; + } + + /* + * Update clock stat counters + */ + parse->polls++; + + /* + * in PPS mode we just mark that we want the next sample + * for the clock filter + */ + if (!parse->pollonly) + { + if (parse->pollneeddata) + { + /* + * bad news - didn't get a response last time + */ + parse->noresponse++; + parse->lastmissed = current_time; + parse_event(parse, CEVNT_TIMEOUT); + + syslog(LOG_WARNING, "PARSE receiver #%d: no data from device within poll interval", CL_UNIT(parse->unit)); + } + parse->pollneeddata = 1; + if (parse->parse_type->cl_poll) + { + parse->parse_type->cl_poll(parse); + } + return; + } + + /* + * the following code is only executed only when polling is used + */ + + PARSE_POLL(parse); +} + +/*-------------------------------------------------- + * parse_leap - called when a leap second occurs + */ + +static void +parse_leap() +{ + /* + * PARSE encodes the LEAP correction direction. + * For timecodes that do not pass on the leap correction direction + * the default PARSEB_LEAPADD must be used. It may then be modified + * with a fudge flag (flag2). + */ +} + + +/*-------------------------------------------------- + * parse_control - set fudge factors, return statistics + */ +static void +parse_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct parseunit *parse; + parsectl_t tmpctl; + u_long type; + static char outstatus[400]; /* status output buffer */ + + type = CL_TYPE(unit); + unit = CL_UNIT(unit); + + if (out) + { + out->lencode = 0; + out->lastcode = 0; + out->polls = out->noresponse = 0; + out->badformat = out->baddata = 0; + out->timereset = 0; + out->currentstatus = out->lastevent = CEVNT_NOMINAL; + out->kv_list = (struct ctl_var *)0; + } + + if (unit >= MAXUNITS) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (max %d)", + unit, MAXUNITS-1); + return; + } + + parse = parseunits[unit]; + + if (!parse || !parse->peer) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (UNIT INACTIVE)", + unit); + return; + } + + if (in) + { + if (in->haveflags & CLK_HAVETIME1) + parse->basedelay = in->fudgetime1; + + if (in->haveflags & CLK_HAVETIME2) + { + parse->ppsdelay = in->fudgetime2; + } + + if (in->haveflags & CLK_HAVEVAL1) + { + parse->peer->stratum = (u_char)(in->fudgeval1 & 0xf); + if (parse->peer->stratum <= 1) + memmove((char *)&parse->peer->refid, + parse->parse_type->cl_id, + 4); + else + parse->peer->refid = htonl(PARSEHSREFID); + } + + if (in->haveflags & CLK_HAVEVAL2) + { + parse->peer->refid = in->fudgeval2; + } + + if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4)) + { + parse->flags = (in->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4)) | + (parse->flags & ~PARSE_STAT_FLAGS); + } + + if (in->haveflags & (CLK_HAVEVAL2|CLK_HAVETIME2|CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4)) + { + parsectl_t tmpctl; + tmpctl.parsestatus.flags = parse->flags & PARSE_STAT_FLAGS; + + if (!PARSE_SETSTAT(parse, &tmpctl)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_setstat() FAILED", unit); + } + } + } + + if (out) + { + register u_long sum = 0; + register char *t, *tt; + register struct tm *tm; + register short utcoff; + register char sign; + register int i; + time_t tim; + + outstatus[0] = '\0'; + + out->haveflags = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3; + out->clockdesc = parse->parse_type->cl_description; + + out->fudgetime1 = parse->basedelay; + + out->fudgetime2 = parse->ppsdelay; + + out->fudgeval1 = parse->peer->stratum; + + out->fudgeval2 = parse->peer->refid; + + out->flags = parse->flags & PARSE_STAT_FLAGS; + + out->type = REFCLK_PARSE; + + /* + * 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, 40, 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\"", prettydate(&parse->time.parse_ptime.fp)); + } + + /* + * all this for just finding out the +-xxxx part (there are always + * new and changing fields in the standards 8-(). + * + * but we do it for the human user... + */ + tim = parse->time.parse_time.fp.l_ui - JAN_1970; + tm = gmtime(&tim); + utcoff = tm->tm_hour * 60 + tm->tm_min; + tm = localtime(&tim); + utcoff = tm->tm_hour * 60 + tm->tm_min - utcoff + 12 * 60; + utcoff += 24 * 60; + utcoff %= 24 * 60; + utcoff -= 12 * 60; + if (utcoff < 0) + { + utcoff = -utcoff; + sign = '-'; + } + else + { + sign = '+'; + } + + 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 + { + strcpy(tt, prettydate(&parse->time.parse_time.fp)); + t = tt + strlen(tt); + + sprintf(t, " (%c%02d%02d)\"", sign, utcoff / 60, utcoff % 60); + } + + if (!PARSE_GETTIMECODE(parse, &tmpctl)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit); + } + else + { + tt = add_var(&out->kv_list, 128, 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), sizeof(outstatus)- strlen(outstatus) - 1, + tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1); + + parse->badformat += tmpctl.parsegettc.parse_badformat; + } + + tmpctl.parseformat.parse_format = tmpctl.parsegettc.parse_format; + + if (!PARSE_GETFMT(parse, &tmpctl)) + { + syslog (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 + */ + + tt = add_var(&out->kv_list, 200, RO|DEF); + strcpy(tt, "refclock_states=\""); + tt += strlen(tt); + + for (i = 0; i <= CEVNT_MAX; i++) + { + register u_long stime; + register u_long div = current_time - parse->timestarted; + register u_long percent; + + percent = stime = PARSE_STATETIME(parse, i); + + while (((u_long)(~0) / 10000) < percent) + { + percent /= 10; + div /= 10; + } + + if (div) + percent = (percent * 10000) / div; + else + percent = 10000; + + if (stime) + { + sprintf(tt, "%s%s%s: %s (%d.%02d%%)", + sum ? "; " : "", + (parse->status == i) ? "*" : "", + clockstatus(i), + l_mktime(stime), + (int)(percent / 100), (int)(percent % 100)); + sum += stime; + tt += strlen(tt); + } + } + + 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=\"refclock_parse.c,v 3.59 1994/05/23 16:29:27 kardel Exp\""); + + out->lencode = strlen(outstatus); + out->lastcode = outstatus; + out->timereset = parse->timestarted; + out->polls = parse->polls; + out->noresponse = parse->noresponse; + out->badformat = parse->badformat; + out->baddata = parse->baddata; + out->lastevent = parse->lastevent; + out->currentstatus = parse->status; + } +} + +/**=========================================================================== + ** processing routines + **/ + +/*-------------------------------------------------- + * event handling - note that nominal events will also be posted + */ +static void +parse_event(parse, event) + struct parseunit *parse; + int event; +{ + if (parse->status != (u_char) event) + { + parse->statetime[parse->status] += current_time - parse->lastchange; + parse->lastchange = current_time; + + parse->status = (u_char)event; + if (event != CEVNT_NOMINAL) + parse->lastevent = parse->status; + + report_event(EVNT_PEERCLOCK, parse->peer); + } +} + +/*-------------------------------------------------- + * process a PARSE time sample + */ +static void +parse_process(parse, parsetime) + struct parseunit *parse; + parsetime_t *parsetime; +{ + unsigned char leap; + struct timeval usecdisp; + l_fp off, rectime, reftime, dispersion; + + /* + * check for changes in conversion status + * (only one for each new status !) + */ + if (parse->laststatus != parsetime->parse_status) + { + char buffer[200]; + + syslog(LOG_WARNING, "PARSE receiver #%d: conversion status \"%s\"", + CL_UNIT(parse->unit), 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)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_process: parse_timecode() FAILED", CL_UNIT(parse->unit)); + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: FAILED TIMECODE: \"%s\"", + CL_UNIT(parse->unit), mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1)); + parse->badformat += tmpctl.parsegettc.parse_badformat; + } + } + + parse->laststatus = parsetime->parse_status; + } + + /* + * 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: + break; /* well, still waiting - timeout is handled at higher levels */ + + case CVT_FAIL: + parse->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)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_getfmt() FAILED", CL_UNIT(parse->unit)); + } + else + { + syslog(LOG_INFO, "PARSE receiver #%d: new packet format \"%s\"", + CL_UNIT(parse->unit), 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); + + syslog(LOG_INFO,"PARSE receiver #%d: STATE CHANGE: %s -> %s", + CL_UNIT(parse->unit), 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 U/A 31 the lost synchronisation ist 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); + if (parse->nosynctime) + { + /* + * repeated POWERUP/NOSYNC state - look whether + * the message should be repeated + */ + if (current_time - parse->nosynctime > PARSENOSYNCREPEAT) + { + syslog(LOG_ERR,"PARSE receiver #%d: *STILL* NOT SYNCHRONIZED (POWERUP or no PZF signal)", + CL_UNIT(parse->unit)); + parse->nosynctime = current_time; + } + } + else + { + syslog(LOG_ERR,"PARSE receiver #%d: NOT SYNCHRONIZED", + CL_UNIT(parse->unit)); + parse->nosynctime = current_time; + } + } + 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 U/A 31 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 + */ + parse->nosynctime = 0; /* current state is better than worst state */ + + if (PARSE_SYNC(parsetime->parse_state)) + { + /* + * currently completely synchronized - best possible state + */ + parse->lastsync = current_time; + /* + * log OK status + */ + parse_event(parse, CEVNT_NOMINAL); + } + else + { + /* + * we have had some problems receiving the time code + */ + parse_event(parse, CEVNT_PROP); + } + } + + if (PARSE_TIMECODE(parsetime->parse_state)) + { + l_fp offset; + + /* + * calculate time offset including systematic delays + * off = PARSE-timestamp + propagation delay - kernel time stamp + */ + offset = parse->basedelay; + + off = parsetime->parse_time.fp; + + reftime = off; + + L_ADD(&off, &offset); + rectime = off; /* this makes org time and xmt time somewhat artificial */ + + L_SUB(&off, &parsetime->parse_stime.fp); + + if ((parse->flags & PARSE_STAT_FILTER) && + (off.l_i > -60) && + (off.l_i < 60)) /* take usec error only if within +- 60 secs */ + { + struct timeval usecerror; + /* + * offset is already calculated + */ + usecerror.tv_sec = parsetime->parse_usecerror / 1000000; + usecerror.tv_usec = parsetime->parse_usecerror % 1000000; + + sTVTOTS(&usecerror, &off); + L_ADD(&off, &offset); + } + } + + if (PARSE_PPS(parsetime->parse_state) && CL_PPS(parse->unit)) + { + l_fp offset; + + /* + * we have a PPS signal - much better than the RS232 stuff (we hope) + */ + offset = parsetime->parse_ptime.fp; + + L_ADD(&offset, &parse->ppsdelay); + + 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)) + { + /* + * RS232 offsets within [-0.5..0.5[ - take PPS offsets + */ + + if (parse->parse_type->cl_flags & PARSE_F_PPSONSECOND) + { + reftime = off = offset; + rectime = offset; + /* + * 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 + */ + off = parsetime->parse_time.fp; + + rectime = reftime = off; /* take reference time - fake rectime */ + + L_SUB(&off, &offset); /* true offset */ + } + } + /* + * take RS232 offset when PPS when out of bounds + */ + } + else + { + /* + * Well, no time code to guide us - assume on second pulse + * and pray, that we are within [-0.5..0.5[ + */ + reftime = off = offset; + rectime = offset; + /* + * 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 ... + */ + return; + } + } + + +#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) || defined(PARSEPPS) + if (CL_PPS(parse->unit) && !parse->pollonly && PARSE_SYNC(parsetime->parse_state)) + { + /* + * only provide PPS information when clock + * is in sync + * thus PHASE and EPOCH are correct and PPS is not + * done via the CIOGETEV loopfilter mechanism + */ +#ifdef PPSPPS + if (fdpps != parse->fd) +#endif + (void) pps_sample(&off); + } +#endif /* PPS || PPSCLK || PPSPPS || PARSEPPS */ + + /* + * ready, unless the machine wants a sample + */ + if (!parse->pollonly && !parse->pollneeddata) + return; + + parse->pollneeddata = 0; + + if (PARSE_PPS(parsetime->parse_state)) + { + L_CLR(&dispersion); + } + else + { + /* + * convert usec dispersion into NTP TS world + */ + + usecdisp.tv_sec = parsetime->parse_usecdisp / 1000000; + usecdisp.tv_usec = parsetime->parse_usecdisp % 1000000; + + TVTOTS(&usecdisp, &dispersion); + } + + /* + * 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)) + { + 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). + */ + leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND; + } + else + if (PARSE_LEAPDEL(parsetime->parse_state)) + { + leap = LEAP_DELSECOND; + } + else + { + leap = LEAP_NOWARNING; + } + } + + refclock_receive(parse->peer, &off, 0, LFPTOFP(&dispersion), &reftime, &rectime, leap); +} + +/**=========================================================================== + ** clock polling support + **/ + +struct poll_timer +{ + struct event timer; /* we'd like to poll a a higher rate than 1/64s */ +}; + +typedef struct poll_timer poll_timer_t; + +/*-------------------------------------------------- + * direct poll routine + */ +static void +poll_dpoll(parse) + struct parseunit *parse; +{ + register int rtc; + register char *ps = ((poll_info_t *)parse->parse_type->cl_data)->string; + register int ct = ((poll_info_t *)parse->parse_type->cl_data)->count; + + rtc = write(parse->fd, ps, ct); + if (rtc < 0) + { + syslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd to clock: %m", CL_UNIT(parse->unit)); + } + else + if (rtc != ct) + { + syslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd incomplete (%d of %d bytes sent)", CL_UNIT(parse->unit), rtc, ct); + } +} + +/*-------------------------------------------------- + * periodic poll routine + */ +static void +poll_poll(parse) + struct parseunit *parse; +{ + register poll_timer_t *pt = (poll_timer_t *)parse->localdata; + + poll_dpoll(parse); + + if (pt != (poll_timer_t *)0) + { + pt->timer.event_time = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate; + TIMER_ENQUEUE(timerqueue, &pt->timer); + } +} + +/*-------------------------------------------------- + * init routine - setup timer + */ +static int +poll_init(parse) + struct parseunit *parse; +{ + register poll_timer_t *pt; + + if (((poll_info_t *)parse->parse_type->cl_data)->rate) + { + parse->localdata = (void *)malloc(sizeof(poll_timer_t)); + memset((char *)parse->localdata, 0, sizeof(poll_timer_t)); + + pt = (poll_timer_t *)parse->localdata; + + pt->timer.peer = (struct peer *)parse; /* well, only we know what it is */ + pt->timer.event_handler = poll_poll; + poll_poll(parse); + } + else + { + parse->localdata = (void *)0; + } + + return 0; +} + +/*-------------------------------------------------- + * end routine - clean up timer + */ +static void +poll_end(parse) + struct parseunit *parse; +{ + if (parse->localdata != (void *)0) + { + TIMER_DEQUEUE(&((poll_timer_t *)parse->localdata)->timer); + free((char *)parse->localdata); + parse->localdata = (void *)0; + } +} + +/**=========================================================================== + ** special code for special clocks + **/ + +/*-------------------------------------------------- + * trimble init routine - setup EOL and then do poll_init. + */ +static int +trimble_init(parse) + struct parseunit *parse; +{ +#ifdef HAVE_TERMIOS + struct termios tm; +#endif +#ifdef HAVE_SYSV_TTYS + struct termio tm; +#endif + /* + * configure terminal line for trimble receiver + */ + if (TTY_GETATTR(parse->fd, &tm) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: trimble_init: tcgetattr(fd, &tm): %m", CL_UNIT(parse->unit)); + return 0; + } + else + { + tm.c_cc[VEOL] = TRIMBLESV6_EOL; + + if (TTY_SETATTR(parse->fd, &tm) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: trimble_init: tcsetattr(fd, &tm): %m", CL_UNIT(parse->unit)); + return 0; + } + } + return poll_init(parse); +} +#endif /* defined(REFCLOCK) && defined(PARSE) */ + +/* + * History: + * + * refclock_parse.c,v + * Revision 3.59 1994/05/23 16:29:27 kardel + * IGEL clock - Trimble update + * + * Revision 3.58 1994/05/12 21:03:39 kardel + * adhere to new standard that fudgeval2 is refid + * + * Revision 3.57 1994/05/12 12:50:47 kardel + * printf fmt/arg cleanup + * + * Revision 3.56 1994/05/10 21:15:51 kardel + * var reference level bug, kernel disable fix + * + * Revision 3.55 1994/05/02 00:37:01 kardel + * 3.3t reconcilation + bug fixes (PPS simulation - old kpll) + * + * Revision 3.54 1994/04/11 19:34:42 kardel + * longer input characters for DCF77 raw input (8Bit+parity ignored) + * + * Revision 3.53 1994/03/25 13:07:39 kardel + * fixed offset calculation for large (>4 Min) offsets + * + * Revision 3.52 1994/03/03 09:58:00 kardel + * stick -kv in cvs is no fun + * + * Revision 3.49 1994/02/20 13:26:00 kardel + * rcs id cleanup + * + * Revision 3.48 1994/02/20 13:04:56 kardel + * parse add/delete second support + * + * Revision 3.47 1994/02/02 17:44:30 kardel + * rcs ids fixed + * + * Revision 3.45 1994/01/25 19:06:27 kardel + * 94/01/23 reconcilation + * + * Revision 3.44 1994/01/25 17:32:23 kardel + * settable extended variables + * + * Revision 3.43 1994/01/23 16:28:39 kardel + * HAVE_TERMIOS introduced + * + * Revision 3.42 1994/01/22 11:35:04 kardel + * added HAVE_TERMIOS + * + * Revision 3.41 1993/11/27 18:44:37 kardel + * can't trust GPS166 on unsync + * + * Revision 3.40 1993/11/21 18:03:36 kardel + * useless declaration deleted + * + * Revision 3.39 1993/11/21 15:30:15 kardel + * static funcitions may be declared only at outer level + * + * Revision 3.38 1993/11/15 21:26:49 kardel + * conditional define comments fixed + * + * Revision 3.37 1993/11/11 11:20:49 kardel + * declaration fixes + * + * Revision 3.36 1993/11/10 12:17:14 kardel + * #ifdef glitch + * + * Revision 3.35 1993/11/01 21:15:06 kardel + * comments updated + * + * Revision 3.34 1993/11/01 20:01:08 kardel + * parse Solaris support (initial version) + * + * Revision 3.33 1993/10/30 09:44:58 kardel + * conditional compilation flag cleanup + * + * Revision 3.32 1993/10/22 14:28:43 kardel + * Oct. 22nd 1993 reconcilation + * + * Revision 3.31 1993/10/10 21:19:10 kardel + * compilation cleanup - (minimal porting tests) + * + * Revision 3.30 1993/10/09 21:44:35 kardel + * syslog strings fixed + * + * Revision 3.29 1993/10/09 14:40:15 kardel + * default precision setting fixed + * + * Revision 3.28 1993/10/08 14:48:22 kardel + * Changed offset determination logic: + * Take the PPS offset if it is available and the time + * code offset is within [-0.5..0.5[, otherwise stick + * to the time code offset + * + * Revision 3.27 1993/10/08 00:53:17 kardel + * announce also simulated PPS via CIOGETEV in ntpq cl + * + * Revision 3.26 1993/10/07 23:29:35 kardel + * trimble fixes + * + * Revision 3.25 1993/10/06 21:13:35 kardel + * test reversed (CIOGETEV support) + * + * Revision 3.24 1993/10/03 20:18:26 kardel + * Well, values > 999999 in the usec field from uniqtime() timestamps + * can prove harmful. + * + * Revision 3.23 1993/10/03 19:49:54 kardel + * buftvtots where failing on uninitialized time stamps + * + * Revision 3.22 1993/10/03 19:11:09 kardel + * restructured I/O handling + * + * Revision 3.21 1993/09/29 11:30:18 kardel + * special init for trimble to set EOL + * + * Revision 3.20 1993/09/27 22:46:28 kardel + * preserve module stack if I_PUSH parse fails + * + * Revision 3.19 1993/09/27 21:10:11 kardel + * wrong structure member + * + * Revision 3.18 1993/09/27 13:05:06 kardel + * Trimble is true polling only + * + * Revision 3.17 1993/09/27 12:47:10 kardel + * poll string support generalized + * + * Revision 3.16 1993/09/26 23:40:56 kardel + * new parse driver logic + * + * Revision 3.15 1993/09/24 15:00:51 kardel + * Sep 23rd distribution... + * + * Revision 3.14 1993/09/22 18:21:15 kardel + * support ppsclock streams module (-DSTREAM -DPPSPPS -DPARSEPPS -UPARSESTREAM) + * + * Revision 3.13 1993/09/05 15:38:33 kardel + * not every cpp understands #error... + * + * Revision 3.12 1993/09/02 20:04:19 kardel + * TTY cleanup + * + * Revision 3.11 1993/09/01 21:48:47 kardel + * conditional cleanup + * + * Revision 3.10 1993/09/01 11:32:45 kardel + * assuming HAVE_POSIX_TTYS when STREAM defined + * + * Revision 3.9 1993/08/31 22:31:46 kardel + * SINIX-M SysVR4 integration + * + * Revision 3.8 1993/08/27 00:29:50 kardel + * compilation cleanup + * + * Revision 3.7 1993/08/24 22:27:30 kardel + * cleaned up AUTOCONF DCF77 mess 8-) - wasn't too bad + * + * Revision 3.6 1993/08/24 21:36:23 kardel + * casting and ifdefs + * + * Revision 3.5 1993/07/09 23:36:59 kardel + * HAVE_POSIX_TTYS used to produce errors 8-( - BSD driver support still lacking + * + * Revision 3.4 1993/07/09 12:42:29 kardel + * RAW DCF now officially released + * + * Revision 3.3 1993/07/09 11:50:37 kardel + * running GPS also on 960 to be able to switch GPS/DCF77 + * + * Revision 3.2 1993/07/09 11:37:34 kardel + * Initial restructured version + GPS support + * + * Revision 3.1 1993/07/06 10:01:07 kardel + * DCF77 driver goes generic... + * + */ diff --git a/usr.sbin/xntpd/xntpd/refclock_nmea.c b/usr.sbin/xntpd/xntpd/refclock_nmea.c new file mode 100644 index 000000000000..3058956df516 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_nmea.c @@ -0,0 +1,391 @@ +/* + * refclock_nmea.c - clock driver for an NMEA GPS CLOCK + * Michael Petry Jun 20, 1994 + * based on refclock_heath.c + */ +#if defined(REFCLOCK) && defined(NMEA) + +#define DEBUG 1 + +#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 NMEA GPS Receiver with + * + * Protype was refclock_trak.c, Thanks a lot. + * + * The reciever 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 REFID "GPS\0" /* reference id */ +#define DESCRIPTION "NMEA GPS Clock" /* who we are */ + +#define NSAMPLES 3 /* stages of median filter */ +#define LENNMEA 75 /* min timecode length */ + +/* + * Imported from ntp_timer module + */ +extern u_long current_time; /* current time (s) */ + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * Tables to compute the ddd of year form icky dd/mm timecode. Viva la + * leap. + */ +static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* + * Unit control structure + */ +struct nmeaunit { + int pollcnt; /* poll message counter */ + 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, char *, struct peer *)); + +/* + * 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(unit, peer) + 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 = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; + gps_send(pp->io.fd,"$PMOTG,RMC,0000\n", peer); + return (1); +} + +/* + * nmea_shutdown - shut down a GPS clock + */ +static void +nmea_shutdown(unit, peer) + 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(rbufp) + 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; + char *field_parse(); + + /* + * 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->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>; however, we use + * the timestamp of "now" since this may be a broadcast instead + * of a poll. This needs to be checked empeerically + */ + gettstamp(&up->tstamp); /* HACK */ + pp->lastrec = up->tstamp; + up->pollcnt = 2; + record_clock_stats(&peer->srcadr, pp->lastcode); +#ifdef DEBUG + if (debug) + printf("nmea: timecode %d %s\n", pp->lencode, + pp->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 + */ +#define GPRMC 0 +#define GPXXX 1 + cp = pp->lastcode; + pp->leap = 0; + cmdtype=0; + if(strncmp(cp,"$GPRMC",6)==0) { + cmdtype=GPRMC; + } + else if(strncmp(cp,"$GPXXX",6)==0) { + cmdtype=GPXXX; + } + else + return; + + switch( cmdtype ) { + case GPRMC: + /* + * Check time code format of NMEA + */ + + dp = field_parse(cp,1); + if( !isdigit(dp[0]) || + !isdigit(dp[1]) || + !isdigit(dp[2]) || + !isdigit(dp[3]) || + !isdigit(dp[4]) || + !isdigit(dp[5]) + ) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + break; + case GPXXX: + return; + default: + return; + + } + + 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; + } + + /* + * Test for synchronization Check for quality byte. (soon) + */ + pp->leap = 0; + pp->lasttime = current_time; + + /* + * 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, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->leap); +} + +/* + * 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(unit, peer) + 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++; + + /* + * usually nmea_receive can get a timestamp every second + */ + + gps_send(pp->io.fd,"$PMOTG,RMC,0\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(fd, cmd, peer) + int fd; + char *cmd; + struct peer *peer; +{ + + if (write(fd, cmd, strlen(cmd)) == -1) { + refclock_report(peer, CEVNT_FAULT); + } +} + +char * +field_parse(cp, fn) + char *cp; + int fn; +{ + char *tp; + int i = fn; + + for (tp = cp; *tp != '\0'; tp++) { + if (*tp == ',') + i--; + if (i == 0) + break; + } + return (++tp); +} +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_old/refclock_datum.c b/usr.sbin/xntpd/xntpd/refclock_old/refclock_datum.c new file mode 100644 index 000000000000..680f104568bf --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_old/refclock_datum.c @@ -0,0 +1,645 @@ +/* + * refclock_datum - clock driver for the Datum watchayamacallit + */ +#if defined(REFCLOCK) && (defined(DATUM) || defined(DATUMCLK) || defined(DATUMPPS)) +/* */ +/*...... Include Files .................................................*/ +/* */ + + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> +#include <sys/errno.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.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" + +/* +#include "temp.c" +void set_logfile(); +*/ + + +#define DEBUG_DATUM_PTC + + +/* */ +/*...... #defines ......................................................*/ +/* */ + +/* +#define refclock_datum_pts refclock_wwvb +*/ + +#define MAXUNITS 4 +#define PTSPRECISION (-13) /* precision assumed (about 100 us) */ +#define GMT 7 +#define DATUM_MAX_ERROR 0.100 +#define DATUM_MAX_ERROR2 DATUM_MAX_ERROR*DATUM_MAX_ERROR + +/* */ +/*...... externals .....................................................*/ +/* */ + +extern U_LONG current_time; /* current time (s) */ +extern int debug; /* global debug flag */ + + +/* */ +/*...... My structure ..................................................*/ +/* */ + +struct datum_pts_unit { + struct peer *peer; /* peer used by xntp */ + struct refclockio io; /* io structure used by xntp */ + int PTS_fd; /* file descriptor for PTS */ + int PTS_START; /* ? */ + u_int unit; /* id for unit */ + U_LONG timestarted; /* time started */ + int inuse; /* in use flag */ + l_fp lastrec; + l_fp lastref; + int yearstart; + int coderecv; + int day; /* day */ + int hour; /* hour */ + int minute; /* minutes */ + int second; /* seconds */ + int msec; /* miliseconds */ + int usec; /* miliseconds */ + u_char leap; + char retbuf[8]; /* returned time from the datum pts */ + char nbytes; /* number of bytes received from datum pts */ + double sigma2; /* average squared error (roughly) */ +}; + + +/* */ +/*...... pts static constant variables for internal use ................*/ +/* */ + +static char STOP_GENERATOR[6]; +static char START_GENERATOR[6]; +static char TIME_REQUEST[6]; + +static int nunits; +static struct datum_pts_unit **datum_pts_unit; +static u_char stratumtouse[MAXUNITS]; +static l_fp fudgefactor[MAXUNITS]; + +static FILE *logfile; + +/* */ +/*...... callback functions that xntp knows about ......................*/ +/* */ + +static int datum_pts_start P((u_int, struct peer *)); +static void datum_pts_shutdown P((int)); +static void datum_pts_poll P((int, struct peer *)); +static void datum_pts_control P((u_int, struct refclockstat *, + struct refclockstat *)); +static void datum_pts_init P((void)); +static void datum_pts_buginfo P((int, struct refclockbug *)); + +struct refclock refclock_datum = { + datum_pts_start, + datum_pts_shutdown, + datum_pts_poll, + datum_pts_control, + datum_pts_init, + datum_pts_buginfo, + NOFLAGS +}; + +/* +struct refclock refclock_wvvb = { + datum_pts_start, + datum_pts_shutdown, + datum_pts_poll, + datum_pts_control, + datum_pts_init, + datum_pts_buginfo, + NOFLAGS +}; +*/ + +/* */ +/*...... receive callback functions for xntp ..........................*/ +/* */ + +static void datum_pts_receive P((struct recvbuf *)); + + +/*......................................................................*/ +/* datum_pts_start */ +/*......................................................................*/ + +static int datum_pts_start(unit, peer) + u_int unit; + struct peer *peer; +{ + struct datum_pts_unit **temp_datum_pts_unit; + struct datum_pts_unit *datum_pts; + struct termios arg; + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile, "Starting Datum PTS unit %d\n", unit); + fflush(logfile); +#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; + datum_pts->yearstart = 0; + datum_pts->sigma2 = 0.0; + +/* */ +/*...... open the datum pts device .....................................*/ +/* */ + + datum_pts->PTS_fd = open("/dev/ttya",O_RDWR); + + fcntl(datum_pts->PTS_fd, F_SETFL, 0); + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Opening RS232 port ttya with file descriptor %d\n", + datum_pts->PTS_fd); + fflush(logfile); +#endif + +/* */ +/*...... set up the RS232 terminal device information ..................*/ +/* */ + + arg.c_iflag = IGNBRK; + arg.c_oflag = 0; + arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL; + arg.c_lflag = 0; + arg.c_line = 0; + arg.c_cc[VMIN] = 0; /* start timeout timer right away */ + arg.c_cc[VTIME] = 30; /* this is a 3 second timout on reads */ + + tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg); + +/* */ +/*...... initialize the io structure ...................................*/ +/* */ + + datum_pts->peer = peer; + datum_pts->timestarted = current_time; + + 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 + fprintf(logfile,"Problem adding clock\n"); + fflush(logfile); +#endif + syslog(LOG_ERR, "Datum_PTS: Problem adding clock"); + } + + peer->precision = PTSPRECISION; + peer->rootdelay = 0; + peer->rootdispersion = 0; + peer->stratum = stratumtouse[unit]; + +/* */ +/*...... now add one to the number of units ............................*/ +/* */ + + nunits++; + +/* */ +/*...... return successful code ........................................*/ +/* */ + + return 1; + +} + + +/*......................................................................*/ +/* datum_pts_shutdown */ +/*......................................................................*/ + +static void datum_pts_shutdown(unit) + int unit; +{ + int i,j; + struct datum_pts_unit **temp_datum_pts_unit; + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Shutdown Datum PTS\n"); + fflush(logfile); +#endif + syslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS"); + +/* */ +/*...... loop until the right unit is found ............................*/ +/* */ + + for (i=0; i<nunits; i++) { + if (datum_pts_unit[i]->unit == unit) { + +/* */ +/*...... found it so close the file descriptor and free up memory .....*/ +/* */ + + io_closeclock(&datum_pts_unit[i]->io); + close(datum_pts_unit[i]->PTS_fd); + free(datum_pts_unit[i]); + +/* */ +/*...... clean up the datum_pts_unit array (no holes) ..................*/ +/* */ + + 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 + fprintf(logfile,"Error, could not shut down unit %d\n",unit); + fflush(logfile); +#endif + syslog(LOG_ERR, "Datum_PTS: Could not shut down Datum PTS unit %d",unit); + +} + + +/*......................................................................*/ +/* datum_pts_poll */ +/*......................................................................*/ + +static void datum_pts_poll(unit, peer) + int unit; + struct peer *peer; +{ + int i; + int index; + int error_code; + l_fp tstmp; + struct datum_pts_unit *datum_pts; + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Poll Datum PTS\n"); + fflush(logfile); +#endif + +/* */ +/*...... find the unit and send out a time request .....................*/ +/* */ + + 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; + } + } + + if (index == -1) { +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Error, could not poll unit %d\n",unit); + fflush(logfile); +#endif + syslog(LOG_ERR, "Datum_PTS: Could not poll unit %d",unit); + return; + } + +} + + +/*......................................................................*/ +/* datum_pts_control */ +/*......................................................................*/ + +static void datum_pts_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Control Datum PTS\n"); + fflush(logfile); +#endif +} + + +/*......................................................................*/ +/* datum_pts_init */ +/*......................................................................*/ + +static void datum_pts_init() +{ + +#ifdef DEBUG_DATUM_PTC + logfile = fopen("xntpd.log", "w"); + fprintf(logfile,"Init Datum PTS\n"); + fflush(logfile); +#endif + + memcpy(START_GENERATOR, "//kk01",6); + memcpy(STOP_GENERATOR, "//kk00",6); + memcpy(TIME_REQUEST, "//k/mn",6); + + datum_pts_unit = NULL; + nunits = 0; +} + + +/*......................................................................*/ +/* datum_pts_buginfo */ +/*......................................................................*/ + +static void datum_pts_buginfo(unit, bug) + int unit; + register struct refclockbug *bug; +{ +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Buginfo Datum PTS\n"); + fflush(logfile); +#endif +} + + +/*......................................................................*/ +/* datum_pts_receive */ +/*......................................................................*/ + +static void datum_pts_receive(rbufp) + struct recvbuf *rbufp; +{ + int i; + l_fp tstmp, trtmp, tstmp1; + struct datum_pts_unit *datum_pts; + char *dpt; + int dpend; + time_t tim; + struct tm *loctm; + int tzoff; + int timerr; + double ftimerr, abserr; + + datum_pts = (struct datum_pts_unit *)rbufp->recv_srcclock; + dpt = (char *)&rbufp->recv_space; + dpend = rbufp->recv_length; + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Receive Datum PTS: %d bytes\n", dpend); + fflush(logfile); +#endif + + 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; + } + + +/* */ +/*...... save the ntp system time ......................................*/ +/* */ + + trtmp = rbufp->recv_time; + datum_pts->lastrec = trtmp; + +/* */ +/*...... convert the time from the buffer ..............................*/ +/* */ + + 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 + fprintf(logfile,"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); + fflush(logfile); +#endif + +/* */ +/*...... get the GMT time zone offset ..................................*/ +/* */ + + tim = trtmp.l_ui - JAN_1970; + loctm = localtime(&tim); + tzoff = -loctm->tm_gmtoff/3600; + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Time Zone = %d, time (sec) since 1970 = %d\n",tzoff, tim); + fflush(logfile); +#endif + +/* */ +/*...... make sure that we have a good time from the Datum PTS .........*/ +/* */ + + 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 + fprintf(logfile,"Error: bad clocktime\n"); + fprintf(logfile,"GMT %d, lastrec %d, yearstart %d, lastref %d\n", + tzoff, + datum_pts->lastrec.l_ui, + datum_pts->yearstart, + datum_pts->lastref.l_ui); + fflush(logfile); +#endif + syslog(LOG_ERR, "Datum_PTS: Bad clocktime"); + + return; + + }else{ + +#ifdef DEBUG_DATUM_PTC + fprintf(logfile,"Good clocktime\n"); + fflush(logfile); +#endif + + } + +/* */ +/*...... we have datum_pts->lastref.l_ui set, get useconds now .........*/ +/* */ + + TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf); + +/* */ +/*...... pass the new time to xntpd ....................................*/ +/* */ + + tstmp = datum_pts->lastref; + L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is the time correction */ + datum_pts->coderecv++; + +/* + set_logfile(logfile); +*/ + + refclock_receive( datum_pts->peer, + &tstmp, + tzoff, + 0, + &datum_pts->lastrec, + &datum_pts->lastrec, + datum_pts->leap ); + 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 + fprintf(logfile,"Time error = %f seconds, Sigma Squared = %f\n", + ftimerr, datum_pts->sigma2); + fflush(logfile); +#endif + +/* */ +/*...... make sure that we understand the format for xntp time .........*/ +/* */ + +#ifdef DEBUG_DATUM_PTC + tstmp.l_ui = 2; + TVUTOTSF(123456, tstmp.l_uf); + + timerr = tstmp.l_ui<<20; + timerr |= (tstmp.l_uf>>12) & 0x000fffff; + ftimerr = timerr; + ftimerr /= 1024*1024; + fprintf(logfile,"Test1 2.123456 = %f\n",ftimerr); + fflush(logfile); + + tstmp1.l_ui = 3; + TVUTOTSF(223456, tstmp1.l_uf); + timerr = tstmp1.l_ui<<20; + timerr |= (tstmp1.l_uf>>12) & 0x000fffff; + ftimerr = timerr; + ftimerr /= 1024*1024; + fprintf(logfile,"Test2 3.223456 = %f\n",ftimerr); + fflush(logfile); + + L_SUB(&tstmp, &tstmp1); + timerr = tstmp.l_ui<<20; + timerr |= (tstmp.l_uf>>12) & 0x000fffff; + ftimerr = timerr; + ftimerr /= 1024*1024; + fprintf(logfile,"Test3 -1.100000 = %f\n",ftimerr); + fflush(logfile); +#endif + + +} +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_old/refclock_gpstm.c b/usr.sbin/xntpd/xntpd/refclock_old/refclock_gpstm.c new file mode 100644 index 000000000000..2e81e52c8dae --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_old/refclock_gpstm.c @@ -0,0 +1,998 @@ +/* + * refclock_gpstm - clock driver for the Kinimetrics Truetime GPSTM/TMD rcvr + * Version 1.0 (from Version 2.0 of the GOES driver, as of 03Jan94) + */ + +#if defined(REFCLOCK) && (defined(GPSTM) || defined(GPSTMCLK) \ + || defined(GPSTMPPS)) + +#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 SYS_BSDI +#undef HAVE_BSD_TTYS +#include <sys/ioctl.h> +#endif + +#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(GPSTMCLK) +#include <clkdefs.h> +#endif /* GPSTMCLK */ +#endif /* STREAM */ + +#if defined(GPSTMPPS) +#include <sys/ppsclock.h> +#endif /* GPSTMPPS */ + +#include "ntp_stdlib.h" + +/* + * Support for Kinemetrics Truetime GPS-TM/TMD Receiver + * + * Most of this code is copied from refclock_goes.c with thanks. + * + * the time code looks like follows: + * + * ADDD:HH:MM:SSQCL + * A - control A + * Q Quality indication: indicates possible error of + * ? +/- 500 milliseconds # +/- 50 milliseconds + * * +/- 5 milliseconds . +/- 1 millisecond + * space less than 1 millisecond + * C - Carriage return + * L - Line feed + * The carriage return start bit begins on 0 seconds and extends to 1 bit time. + * + * Flag1 set to 1 will silence the clock side of xntpd, just reading the + * clock without trying to write to it. This is usefull if several + * xntpds listen to the same clock. This has not been tested yet... + */ + +/* + * Definitions + */ +#define MAXUNITS 4 /* max number of GPSTM units */ +#define GPSTM232 "/dev/gpstm%d" +#define SPEED232 B9600 /* 9600 baud */ + +/* + * Radio interface parameters + */ +#define MAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */ +#define PRECISION (-20) /* precision assumed (about 1 ms) */ +#define REFID "GPS\0" /* reference id */ +#define DESCRIPTION "Kinemetrics GPS-TM/TMD Receiver" /* who we are */ +#define GMT 0 /* hour offset from Greenwich */ +#define NCODES 3 /* stages of median filter */ +#define BMAX 99 /* timecode buffer length */ +#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */ +#define TIMEOUT 180 /* ping the clock if it's silent this long */ + +/* + * used by the state machine + */ +enum gpstm_event {e_Init, e_F18, e_F50, e_F51, e_TS}; +static enum {Base, Start, F18, F50, F51, F08} State[MAXUNITS]; +static time_t Last[MAXUNITS]; +static void gpstm_doevent P((int, enum gpstm_event)); +static void gpstm_initstate P((int)); + +/* + * Hack to avoid excercising the multiplier. I have no pride. + */ +#define MULBY10(x) (((x)<<3) + ((x)<<1)) + +/* + * Imported from the timer module + */ +extern U_LONG current_time; +extern struct event timerqueue[]; + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * GPSTM unit control structure + */ +struct gpstm_unit { + struct peer *peer; /* associated peer structure */ + struct refclockio io; /* given to the I/O handler */ + l_fp lastrec; /* last receive time */ + l_fp lastref; /* last timecode time */ + l_fp offset[NCODES]; /* recent sample offsets */ + char lastcode[BMAX]; /* last timecode received */ + u_short polled; /* Hand in a time sample? */ + u_char lencode; /* length of last timecode */ + U_LONG lasttime; /* last time clock heard from */ + 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 year; /* year of eternity */ + u_short day; /* day of year */ + u_char hour; /* hour of day */ + u_char minute; /* minute of hour */ + u_char second; /* seconds of minute */ + u_char leap; /* leap indicators */ + u_short msec; /* millisecond of second */ + u_char quality; /* quality character */ + U_LONG yearstart; /* start of current year */ + /* + * 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 gpstm_unit *gpstm_units[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 fudgefactor1[MAXUNITS]; +static l_fp fudgefactor2[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static u_char readonlyclockflag[MAXUNITS]; +static U_LONG refid[MAXUNITS]; + +/* + * Function prototypes + */ +static void gpstm_init P((void)); +static int gpstm_start P((u_int, struct peer *)); +static void gpstm_shutdown P((int)); +static void gpstm_rep_event P((struct gpstm_unit *, int)); +static void gpstm_receive P((struct recvbuf *)); +static char gpstm_process P((struct gpstm_unit *, l_fp *, u_fp *)); +static void gpstm_poll P((int, struct peer *)); +static void gpstm_control P((u_int, struct refclockstat *, + struct refclockstat *)); +static void gpstm_buginfo P((int, struct refclockbug *)); +static void gpstm_send P((struct gpstm_unit *, char *)); + +struct refclock refclock_gpstm = { + gpstm_start, gpstm_shutdown, gpstm_poll, + gpstm_control, gpstm_init, gpstm_buginfo, NOFLAGS +}; + +/* + * gpstm_init - initialize internal driver data + */ +static void +gpstm_init() +{ + register int i; + /* + * Just zero the data arrays + */ + memset((char *)gpstm_units, 0, sizeof gpstm_units); + memset((char *)unitinuse, 0, sizeof unitinuse); + + /* + * Initialize fudge factors to default. + */ + for (i = 0; i < MAXUNITS; i++) { + fudgefactor1[i].l_ui = 0; + fudgefactor1[i].l_uf = 0; + fudgefactor2[i].l_ui = 0; + fudgefactor2[i].l_uf = 0; + stratumtouse[i] = 0; + readonlyclockflag[i] = 0; + memcpy((char *)&refid[i], REFID, 4); + } +} + + +/* + * gpstm_start - open the device and initialize data for processing + */ +static int +gpstm_start(unit, peer) + u_int unit; + struct peer *peer; +{ + register struct gpstm_unit *gpstm; + register int i; + int fd232; + char dev[20]; + + /* + * Check configuration info + */ + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_start: unit %d invalid", unit); + return 0; + } + if (unitinuse[unit]) { + syslog(LOG_ERR, "gpstm_start: unit %d in use", unit); + return 0; + } + + /* + * Open serial port + */ + (void) sprintf(dev, GPSTM232, unit); + fd232 = open(dev, O_RDWR, 0777); + if (fd232 == -1) { + syslog(LOG_ERR, "gpstm_start: open of %s: %m", dev); + return 0; + } + +#if defined(HAVE_SYSV_TTYS) + /* + * System V serial line parameters (termio interface) + * + */ + { struct termio ttyb; + if (ioctl(fd232, TCGETA, &ttyb) < 0) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TCGETA): %m", dev); + 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) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TCSETA): %m", dev); + goto screwed; + } + } +#endif /* HAVE_SYSV_TTYS */ +#if defined(HAVE_TERMIOS) + /* + * POSIX serial line parameters (termios interface) + * + * The GPSTMCLK option provides timestamping at the driver level. + * It requires the tty_clk streams module. + * + * The GPSTMPPS 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 SunOS 4.1.1 or + * later. + */ + { struct termios ttyb, *ttyp; + ttyp = &ttyb; + + if (tcgetattr(fd232, ttyp) < 0) { + syslog(LOG_ERR, + "gpstm_start: tcgetattr(%s): %m", dev); + 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) { + syslog(LOG_ERR, + "gpstm_start: tcsetattr(%s): %m", dev); + goto screwed; + } + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(LOG_ERR, + "gpstm_start: tcflush(%s): %m", dev); + goto screwed; + } +#if defined(STREAM) +#if defined(GPSTMCLK) + if (ioctl(fd232, I_PUSH, "clk") < 0) + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, I_PUSH, clk): %m", dev); + if (ioctl(fd232, CLK_SETSTR, "\n") < 0) + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, CLK_SETSTR): %m", dev); +#endif /* GPSTMCLK */ +#if defined(GPSTMPPS) + if (ioctl(fd232, I_PUSH, "ppsclock") < 0) + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, I_PUSH, ppsclock): %m", dev); + else + fdpps = fd232; +#endif /* GPSTMPPS */ +#endif /* STREAM */ + } +#endif /* HAVE_TERMIOS */ +#if defined(HAVE_BSD_TTYS) + /* + * 4.3bsd serial line parameters (sgttyb interface) + * + * The GPSTMCLK option provides timestamping at the driver level. + * It requires the tty_clk line discipline and 4.3bsd or later. + */ + { struct sgttyb ttyb; +#if defined(GPSTMCLK) + int ldisc = CLKLDISC; +#endif /* GPSTMCLK */ + + if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TIOCGETP): %m", dev); + goto screwed; + } + ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; +#if defined(GPSTMCLK) + 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 /* GPSTMCLK */ + if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TIOCSETP): %m", dev); + goto screwed; + } +#if defined(GPSTMCLK) + if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { + syslog(LOG_ERR, + "gpstm_start: ioctl(%s, TIOCSETD): %m", dev); + goto screwed; + } +#endif /* GPSTMCLK */ + } +#endif /* HAVE_BSD_TTYS */ + + /* + * Allocate unit structure + */ + if (gpstm_units[unit] != 0) { + gpstm = gpstm_units[unit]; /* The one we want is okay */ + } else { + for (i = 0; i < MAXUNITS; i++) { + if (!unitinuse[i] && gpstm_units[i] != 0) + break; + } + if (i < MAXUNITS) { + /* + * Reclaim this one + */ + gpstm = gpstm_units[i]; + gpstm_units[i] = 0; + } else { + gpstm = (struct gpstm_unit *) + emalloc(sizeof(struct gpstm_unit)); + } + } + memset((char *)gpstm, 0, sizeof(struct gpstm_unit)); + gpstm_units[unit] = gpstm; + + /* + * Set up the structures + */ + gpstm->peer = peer; + gpstm->unit = (u_char)unit; + gpstm->timestarted = current_time; + + gpstm->io.clock_recv = gpstm_receive; + gpstm->io.srcclock = (caddr_t)gpstm; + gpstm->io.datalen = 0; + gpstm->io.fd = fd232; + if (!io_addclock(&gpstm->io)) { + goto screwed; + } + + /* + * All done. Initialize a few random peer variables, then + * return success. + */ + peer->precision = PRECISION; + peer->rootdelay = 0; + peer->rootdispersion = 0; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + unitinuse[unit] = 1; + gpstm_initstate(unit); + return 1; + + /* + * Something broke; abandon ship + */ +screwed: + (void) close(fd232); + return 0; +} + +/* + * gpstm_shutdown - shut down a clock + */ +static void +gpstm_shutdown(unit) + int unit; +{ + register struct gpstm_unit *gpstm; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_shutdown: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "gpstm_shutdown: unit %d not in use", unit); + return; + } + + /* + * Tell the I/O module to turn us off. We're history. + */ + gpstm = gpstm_units[unit]; + io_closeclock(&gpstm->io); + unitinuse[unit] = 0; +} + + +/* + * gpstm_rep_event - note the occurance of an event + */ +static void +gpstm_rep_event(gpstm, code) + struct gpstm_unit *gpstm; + int code; +{ + struct peer *peer; + + peer = gpstm->peer; + if (gpstm->status != (u_char)code) { + gpstm->status = (u_char)code; + if (code != CEVNT_NOMINAL) + gpstm->lastevent = (u_char)code; + syslog(LOG_INFO, + "clock %s event %x\n", ntoa(&peer->srcadr), code); +#ifdef DEBUG + if (debug) { + printf("gpstm_rep_event(gpstm%d, code %d)\n", + gpstm->unit, code); + } +#endif + } + if (code == CEVNT_BADREPLY) + gpstm_initstate(gpstm->unit); +} + + +/* + * gpstm_receive - receive data from the serial interface on a clock + */ +static void +gpstm_receive(rbufp) + struct recvbuf *rbufp; +{ + register int i; + register struct gpstm_unit *gpstm; + register u_char *dpt; + register char *cp; + register u_char *dpend; + l_fp tstmp; + u_fp dispersion; + + /* + * Get the clock this applies to and a pointers to the data + */ + gpstm = (struct gpstm_unit *)rbufp->recv_srcclock; + dpt = (u_char *)&rbufp->recv_space; + + /* + * Edit timecode to remove control chars + */ + dpend = dpt + rbufp->recv_length; + cp = gpstm->lastcode; + while (dpt < dpend) { + if ((*cp = 0x7f & *dpt++) >= ' ') cp++; +#ifdef GPSTMCLK + else if (*cp == '\r') { + if (dpend - dpt < 8) { + /* short timestamp */ + return; + } + if (!buftvtots(dpt,&gpstm->lastrec)) { + /* screwy timestamp */ + return; + } + dpt += 8; + } +#endif + } + *cp = '\0'; + gpstm->lencode = cp - gpstm->lastcode; + if (gpstm->lencode == 0) + return; +#ifndef GPSTMCLK + gpstm->lastrec = rbufp->recv_time; +#endif /* GPSTMCLK */ +#if !defined(GPSTMCLK) && !defined(GPSTMPPS) && defined(TIOCMODT) + do { + auto struct timeval cur, now; + register long usec; + + if (ioctl(gpstm->io.fd, TIOCMODT, &cur) < 0) { + syslog(LOG_ERR, "TIOCMODT: %m"); +#ifdef DEBUG + if (debug) perror("TIOCMODT"); + break; +#endif + } + if (cur.tv_sec == 0) { + /* no timestamps yet */ + if (debug) printf("MODT tv_sec == 0\n"); + break; + } + + gettimeofday(&now, NULL); + usec = 1000000 * (now.tv_sec - cur.tv_sec) + + (now.tv_usec - cur.tv_usec); +#ifdef DEBUG + if (debug) printf("lastmodem: delay=%d us\n", usec); +#endif + if (usec < 0 || usec > 10000) { + /* time warp or stale timestamp */ + break; + } + if (!buftvtots((char *)&cur, &gpstm->lastrec)) { + /* screwy timestamp */ + break; + } + } while (0); +#endif /*TIOCMODT*/ + +#ifdef DEBUG + if (debug) + printf("gpstm: timecode %d %s\n", + gpstm->lencode, gpstm->lastcode); +#endif + + cp = gpstm->lastcode; + gpstm->leap = 0; + if ((cp[0] == 'F' && isdigit(cp[1]) && isdigit(cp[2])) + || (cp[0] == ' ' && cp[1] == 'T' && cp[2] == 'R')) { + enum gpstm_event event; + + syslog(LOG_NOTICE, "gpstm%d: \"%s\"", gpstm->unit, cp); + if (cp[1] == '5' && cp[2] == '0') + event = e_F50; + else if (cp[1] == '5' && cp[2] == '1') + event = e_F51; + else if (!strncmp(" TRUETIME Mk III", cp, 16)) + event = e_F18; + else { + gpstm_rep_event(gpstm, CEVNT_BADREPLY); + return; + } + gpstm_doevent(gpstm->unit, event); + return; + } else if (gpstm->lencode == 13) { + /* + * Check timecode format 0 + */ + if (!isdigit(cp[0]) /* day of year */ + || !isdigit(cp[1]) + || !isdigit(cp[2]) + || cp[3] != ':' /* : separator */ + || !isdigit(cp[4]) /* hours */ + || !isdigit(cp[5]) + || cp[6] != ':' /* : separator */ + || !isdigit(cp[7]) /* minutes */ + || !isdigit(cp[8]) + || cp[9] != ':' /* : separator */ + || !isdigit(cp[10]) /* seconds */ + || !isdigit(cp[11])) + { + gpstm->badformat++; + gpstm_rep_event(gpstm, CEVNT_BADREPLY); + return; + } + + /* + * Convert format 0 and check values + */ + gpstm->year = 0; /* fake */ + gpstm->day = cp[0] - '0'; + gpstm->day = MULBY10(gpstm->day) + cp[1] - '0'; + gpstm->day = MULBY10(gpstm->day) + cp[2] - '0'; + gpstm->hour = MULBY10(cp[4] - '0') + cp[5] - '0'; + gpstm->minute = MULBY10(cp[7] - '0') + cp[8] - '0'; + gpstm->second = MULBY10(cp[10] - '0') + cp[11] - '0'; + gpstm->msec = 0; + + if (cp[12] != ' ' && cp[12] != '.' && cp[12] != '*') + gpstm->leap = LEAP_NOTINSYNC; + else + gpstm->lasttime = current_time; + + if (gpstm->day < 1 || gpstm->day > 366) { + gpstm->baddata++; + gpstm_rep_event(gpstm, CEVNT_BADDATE); + return; + } + if (gpstm->hour > 23 || gpstm->minute > 59 + || gpstm->second > 59) { + gpstm->baddata++; + gpstm_rep_event(gpstm, CEVNT_BADTIME); + return; + } + gpstm_doevent(gpstm->unit, e_TS); + } else { + gpstm_rep_event(gpstm, CEVNT_BADREPLY); + 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 (!gpstm->polled) + return; + + /* + * Now, compute the reference time value. Use the heavy + * machinery for the seconds and the millisecond field for the + * fraction when present. + * + * this code does not yet know how to do the years + */ + tstmp = gpstm->lastrec; + if (!clocktime(gpstm->day, gpstm->hour, gpstm->minute, + gpstm->second, GMT, tstmp.l_ui, + &gpstm->yearstart, &gpstm->lastref.l_ui)) + { + gpstm->baddata++; + gpstm_rep_event(gpstm, CEVNT_BADTIME); + return; + } + MSUTOTSF(gpstm->msec, gpstm->lastref.l_uf); + + i = ((int)(gpstm->coderecv)) % NCODES; + gpstm->offset[i] = gpstm->lastref; + L_SUB(&gpstm->offset[i], &tstmp); + if (gpstm->coderecv == 0) + for (i = 1; i < NCODES; i++) + gpstm->offset[i] = gpstm->offset[0]; + + gpstm->coderecv++; + + /* + * Process the median filter, and pass the + * offset and dispersion along. 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 (!gpstm_process(gpstm, &tstmp, &dispersion)) { + gpstm->baddata++; + gpstm_rep_event(gpstm, CEVNT_BADTIME); + return; + } + refclock_receive(gpstm->peer, &tstmp, GMT, dispersion, + &gpstm->lastrec, &gpstm->lastrec, gpstm->leap); + + /* + * We have succedded in answering the poll. Turn off the flag + */ + gpstm->polled = 0; +} + +/* + * gpstm_send - time to send the clock a signal to cough up a time sample + */ +static void +gpstm_send(gpstm, cmd) + struct gpstm_unit *gpstm; + char *cmd; +{ +#ifdef DEBUG + if (debug) { + printf("gpstm_send(gpstm%d): %s\n", gpstm->unit, cmd); + } +#endif + if (!readonlyclockflag[gpstm->unit]) { + register int len = strlen(cmd); + + if (write(gpstm->io.fd, cmd, len) != len) { + syslog(LOG_ERR, "gpstm_send: unit %d: %m", + gpstm->unit); + gpstm_rep_event(gpstm, CEVNT_FAULT); + } + } +} + +/* + * state machine for initializing the clock + */ + +static void +gpstm_doevent(unit, event) + int unit; + enum gpstm_event event; +{ + struct gpstm_unit *gpstm = gpstm_units[unit]; + +#ifdef DEBUG + if (debug) { + printf("gpstm_doevent(gpstm%d, %d)\n", unit, (int)event); + } +#endif + if (event == e_TS && State[unit] != F51 && State[unit] != F08) { + gpstm_send(gpstm, "\03\r"); + } + + switch (event) { + case e_Init: + gpstm_send(gpstm, "F18\r"); + State[unit] = Start; + break; + case e_F18: + gpstm_send(gpstm, "F50\r"); + State[unit] = F18; + break; + case e_F50: + gpstm_send(gpstm, "F51\r"); + State[unit] = F50; + break; + case e_F51: + gpstm_send(gpstm, "F08\r"); + State[unit] = F51; + break; + case e_TS: + /* nothing to send - we like this mode */ + State[unit] = F08; + break; + } +} + +static void +gpstm_initstate(unit) { + State[unit] = Base; /* just in case */ + gpstm_doevent(unit, e_Init); +} + +/* + * gpstm_process - process a pile of samples from the clock + */ +static char +gpstm_process(gpstm, offset, dispersion) + struct gpstm_unit *gpstm; + l_fp *offset; + u_fp *dispersion; +{ + register int i, j; + register U_LONG tmp_ui, tmp_uf; + int not_median1 = -1; /* XXX correct? */ + int not_median2 = -1; /* XXX correct? */ + int median; + u_fp disp_tmp, disp_tmp2; + + /* + * This code implements a three-stage median filter. First, we + * check if the samples are within 125 ms of each other. If not, + * dump the sample set. We take the median of the three offsets + * and use that as the sample offset. We take the maximum + * difference and use that as the sample dispersion. There + * probably is not much to be gained by a longer filter, since + * the clock filter in ntp_proto should do its thing. + */ + disp_tmp2 = 0; + for (i = 0; i < NCODES-1; i++) { + for (j = i+1; j < NCODES; j++) { + tmp_ui = gpstm->offset[i].l_ui; + tmp_uf = gpstm->offset[i].l_uf; + M_SUB(tmp_ui, tmp_uf, gpstm->offset[j].l_ui, + gpstm->offset[j].l_uf); + if (M_ISNEG(tmp_ui, tmp_uf)) { + M_NEG(tmp_ui, tmp_uf); + } + if (tmp_ui != 0 || tmp_uf > CODEDIFF) { + return 0; + } + disp_tmp = MFPTOFP(0, tmp_uf); + if (disp_tmp > disp_tmp2) { + disp_tmp2 = disp_tmp; + not_median1 = i; + not_median2 = j; + } + } + } + + /* + * It seems as if all are within 125 ms of each other. + * Now to determine the median of the three. Whlie the + * 125 ms check was going on, we also subtly catch the + * dispersion and set-up for a very easy median calculation. + * The largest difference between any two samples constitutes + * the dispersion. The sample not involve in the dispersion is + * the median sample. EASY! + */ + if (gpstm->lasttime == 0 || disp_tmp2 > MAXDISPERSE) + disp_tmp2 = MAXDISPERSE; + if (not_median1 == 0) { + if (not_median2 == 1) + median = 2; + else + median = 1; + } else { + median = 0; + } + *offset = gpstm->offset[median]; + *dispersion = disp_tmp2; + return 1; +} + +/* + * gpstm_poll - called by the transmit procedure + */ +static void +gpstm_poll(unit, peer) + int unit; + struct peer *peer; +{ + struct gpstm_unit *gpstm; + + /* + * 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 (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_poll: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "gpstm_poll: unit %d not in use", unit); + return; + } + gpstm = gpstm_units[unit]; + if ((current_time - gpstm->lasttime) > 150) { + gpstm->noreply++; + gpstm_rep_event(gpstm_units[unit], CEVNT_TIMEOUT); + gpstm_initstate(gpstm->unit); + } + + /* + * polled every 64 seconds. Ask our receiver to hand in a timestamp. + */ + gpstm->polled = 1; + gpstm->polls++; +} + +/* + * gpstm_control - set fudge factors, return statistics + */ +static void +gpstm_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct gpstm_unit *gpstm; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_control: unit %d invalid", unit); + return; + } + + if (in != 0) { + if (in->haveflags & CLK_HAVETIME1) + fudgefactor1[unit] = in->fudgetime1; + if (in->haveflags & CLK_HAVETIME2) + fudgefactor2[unit] = in->fudgetime2; + if (in->haveflags & CLK_HAVEVAL1) + stratumtouse[unit] = (u_char)(in->fudgeval1); + if (in->haveflags & CLK_HAVEVAL2) + refid[unit] = in->fudgeval2; + if (in->haveflags & CLK_HAVEFLAG1) + readonlyclockflag[unit] = in->flags & CLK_FLAG1; + if (unitinuse[unit]) { + struct peer *peer; + + peer = gpstm_units[unit]->peer; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + } + } + + if (out != 0) { + memset((char *)out, 0, sizeof (struct refclockstat)); + out->type = REFCLK_GPSTM_TRUETIME; + out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 | + CLK_HAVEVAL2 | CLK_HAVEFLAG1; + out->clockdesc = DESCRIPTION; + out->fudgetime1 = fudgefactor1[unit]; + out->fudgetime2 = fudgefactor2[unit]; + out->fudgeval1 = (LONG)stratumtouse[unit]; + out->fudgeval2 = refid[unit]; + out->flags = readonlyclockflag[unit]; + if (unitinuse[unit]) { + gpstm = gpstm_units[unit]; + out->lencode = gpstm->lencode; + out->lastcode = gpstm->lastcode; + out->timereset = current_time - gpstm->timestarted; + out->polls = gpstm->polls; + out->noresponse = gpstm->noreply; + out->badformat = gpstm->badformat; + out->baddata = gpstm->baddata; + out->lastevent = gpstm->lastevent; + out->currentstatus = gpstm->status; + } + } +} + +/* + * gpstm_buginfo - return clock dependent debugging info + */ +static void +gpstm_buginfo(unit, bug) + int unit; + register struct refclockbug *bug; +{ + register struct gpstm_unit *gpstm; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "gpstm_buginfo: unit %d invalid", unit); + return; + } + + if (!unitinuse[unit]) + return; + gpstm = gpstm_units[unit]; + + bug->nvalues = 11; + bug->ntimes = 5; + if (gpstm->lasttime != 0) + bug->values[0] = current_time - gpstm->lasttime; + else + bug->values[0] = 0; + bug->values[1] = (U_LONG)gpstm->reason; + bug->values[2] = (U_LONG)gpstm->year; + bug->values[3] = (U_LONG)gpstm->day; + bug->values[4] = (U_LONG)gpstm->hour; + bug->values[5] = (U_LONG)gpstm->minute; + bug->values[6] = (U_LONG)gpstm->second; + bug->values[7] = (U_LONG)gpstm->msec; + bug->values[8] = gpstm->noreply; + bug->values[9] = gpstm->yearstart; + bug->values[10] = gpstm->quality; + bug->stimes = 0x1c; + bug->times[0] = gpstm->lastref; + bug->times[1] = gpstm->lastrec; + bug->times[2] = gpstm->offset[0]; + bug->times[3] = gpstm->offset[1]; + bug->times[4] = gpstm->offset[2]; +} + +#endif /*GPSTM et al*/ diff --git a/usr.sbin/xntpd/xntpd/refclock_old/refclock_leitch.c b/usr.sbin/xntpd/xntpd/refclock_old/refclock_leitch.c new file mode 100644 index 000000000000..808f65bb4b89 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_old/refclock_leitch.c @@ -0,0 +1,709 @@ +/* + * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock + */ +#if defined(REFCLOCK) && (defined(LEITCH) || defined(LEITCHCLK) || defined(LEITCHPPS)) + +#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" + +#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 +#ifdef STREAM +#include <stropts.h> +#if defined(LEITCHCLK) +#include <sys/clkdefs.h> +#endif /* LEITCHCLK */ +#endif /* STREAM */ + +#if defined (LEITCHPPS) +#include <sys/ppsclock.h> +#endif /* LEITCHPPS */ + +#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 \ + syslog(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 + +extern struct event timerqueue[]; + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * LEITCH unit control structure + */ +struct leitchunit { + struct peer *peer; + struct event leitchtimer; + 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((u_int, struct peer *)); +static void leitch_shutdown P((int)); +static void leitch_poll P((int, struct peer *)); +static void leitch_control P((u_int, struct refclockstat *, struct refclockstat *)); +#define leitch_buginfo noentry +static void leitch_receive P((struct recvbuf *)); +static void leitch_process P((struct leitchunit *)); +static void leitch_timeout P((struct peer *)); +static int leitch_get_date P((struct recvbuf *, struct leitchunit *)); +static int leitch_get_time P((struct recvbuf *, struct leitchunit *, int)); +static int dysize P((int)); + +static struct leitchunit leitchunits[MAXUNITS]; +static u_char unitinuse[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static U_LONG 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() +{ + 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(unit) +int unit; +{ +#ifdef DEBUG + if (debug) + fprintf(stderr, "leitch_shutdown()\n"); +#endif +} + +/* + * leitch_poll - called by the transmit procedure + */ +static void +leitch_poll(unit, peer) + 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(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + if (unit >= MAXUNITS) { + syslog(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 = (LONG)stratumtouse[unit]; + out->fudgeval2 = refid[unit]; + out->lastcode = ""; + out->clockdesc = LEITCH_DESCRIPTION; + } +} + +/* + * leitch_start - open the LEITCH devices and initialize data for processing + */ +static int +leitch_start(unit, peer) + u_int unit; + struct peer *peer; +{ + struct leitchunit *leitch; + int fd232; + char leitchdev[20]; + + /* + * Check configuration info. + */ + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "leitch_start: unit %d invalid", unit); + return (0); + } + + if (unitinuse[unit]) { + syslog(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) { + syslog(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) { + syslog(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) { + syslog(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. + * + * The LEITCHPPS 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 SunOS 4.1.1 or + * later. + */ + { struct termios ttyb, *ttyp; + + ttyp = &ttyb; + if (tcgetattr(fd232, ttyp) < 0) { + syslog(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) { + syslog(LOG_ERR, + "leitch_start: tcsetattr(%s): %m", leitchdev); + goto screwed; + } + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(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) + syslog(LOG_ERR, + "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev); + if (ioctl(fd232, CLK_SETSTR, "\n") < 0) + syslog(LOG_ERR, + "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev); +#endif /* LEITCHCLK */ +#if defined(LEITCHPPS) + if (ioctl(fd232, I_PUSH, "ppsclock") < 0) + syslog(LOG_ERR, + "leitch_start: ioctl(%s, I_PUSH, ppsclock): %m", leitchdev); + else + fdpps = fd232; +#endif /* LEITCHPPS */ +#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) { + syslog(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) { + syslog(LOG_ERR, + "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev); + goto screwed; + } +#if defined(LEITCHCLK) + if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { + syslog(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. Note that root delay and root dispersion are + * always zero for this clock. + */ + peer->precision = 0; + peer->rootdelay = 0; + peer->rootdispersion = 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(rbufp) + 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, 0, rbufp->recv_time.l_ui, + &leitch->yearstart, &leitch->reftime1.l_ui)) { + leitch->state = STATE_IDLE; + break; + } +#ifdef DEBUG + if (debug) + fprintf(stderr, "%u\n", 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, 0, rbufp->recv_time.l_ui, + &leitch->yearstart, &leitch->reftime2.l_ui)) { + leitch->state = STATE_IDLE; + break; + } +#ifdef DEBUG + if (debug) + fprintf(stderr, "%u\n", 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, 0, rbufp->recv_time.l_ui, + &leitch->yearstart, &leitch->reftime3.l_ui)) { + leitch->state = STATE_IDLE; + break; + } +#ifdef DEBUG + if (debug) + fprintf(stderr, "%u\n", 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: + syslog(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(leitch) + struct leitchunit *leitch; +{ + l_fp off; + s_fp delay; + l_fp codetime; + l_fp tmp_fp; + int isinsync = 1; + u_fp dispersion = 10; + + delay = 20; + + codetime = leitch->codetime3; + + off = leitch->reftime1; + L_SUB(&off,&leitch->codetime1); + +#ifdef DEBUG + if (debug) + fprintf(stderr,"%u %u %u %u %d %d\n", + leitch->codetime1.l_ui, leitch->codetime1.l_uf, + leitch->reftime1.l_ui, leitch->reftime1.l_uf, + off.l_ui, off.l_uf); +#endif + tmp_fp = leitch->reftime2; + L_SUB(&tmp_fp,&leitch->codetime2); + if (L_ISGEQ(&off,&tmp_fp)) + off = tmp_fp; +#ifdef DEBUG + if (debug) + fprintf(stderr,"%u %u %u %u %d %d\n", + leitch->codetime2.l_ui, leitch->codetime2.l_uf, + leitch->reftime2.l_ui, leitch->reftime2.l_uf, + off.l_ui, off.l_uf); +#endif + tmp_fp = leitch->reftime3; + L_SUB(&tmp_fp,&leitch->codetime3); + + if (L_ISGEQ(&off,&tmp_fp)) + off = tmp_fp; + +#ifdef DEBUG + if (debug) + fprintf(stderr,"%u %u %u %u %d %d\n", + leitch->codetime3.l_ui, leitch->codetime3.l_uf, + leitch->reftime3.l_ui, leitch->reftime3.l_uf, + off.l_ui, off.l_uf); +#endif + refclock_receive(leitch->peer, &off, 0, dispersion, &codetime, + &codetime, isinsync); +} + +/* + * leitch_timeout + */ +static void +leitch_timeout(fp) + struct peer *fp; +{ + +#ifdef DEBUG + if (debug) + fprintf(stderr, "leitch_timeout()\n"); +#endif + +#ifdef NOTYET + { struct leitchunit *leitch = (struct leitchunit *)fp; + + switch(leitch->state) { + case STATE_IDLE: + leitch_send(leitch,"D\r"); + leitch->state = STATE_DATE; + break; + case STATE_DATE: + leitch_send(leitch,"T\r"); + leitch->state = STATE_TIME1; + break; + case STATE_TIME1: + case STATE_TIME2: + case STATE_TIME3: + default: + break; + } + + leitch->leitchtimer.event_time += 30; + TIMER_ENQUEUE(timerqueue, &leitch->leitchtimer); + } +#endif /* NOTYET */ +} + +/* + * dysize + */ +static int +dysize(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(rbufp,leitch) + struct recvbuf *rbufp; + struct leitchunit *leitch; +{ + int i; + + if (rbufp->recv_length < 6) + return(0); +#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 ((dysize((leitch->year>90?1900:2000)+leitch->year)==365) && + leitch->month > 2) + leitch->yearday--; + + return(1); +} + +/* + * leitch_get_time + */ +static int +leitch_get_time(rbufp,leitch,which) + 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); +} + +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_old/refclock_msfees.c b/usr.sbin/xntpd/xntpd/refclock_old/refclock_msfees.c new file mode 100644 index 000000000000..255d74f285de --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_old/refclock_msfees.c @@ -0,0 +1,1575 @@ +/* refclock_ees - clock driver for the EES M201 receiver */ + +#if defined(REFCLOCK) && defined(MSFEESPPS) && defined(STREAM) + +/* 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> +#include <sys/time.h> + +#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 */ +#include <termios.h> +#include <stropts.h> +#include <sys/ppsclock.h> +#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 }; */ + +/* Imported from the timer module */ +extern u_long current_time; + +#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) +static int ees_get_precision() +{ + struct timeval tp; + struct timezone tzp; + long last; + int i; + long diff; + long val; + gettimeofday(&tp, &tzp); + + last = tp.tv_usec; + for (i=0; i< 100000; i++) { + gettimeofday(&tp, &tzp); + diff = tp.tv_usec - last; + if (diff < 0) diff += USECS; + if (diff > MINSTEP) break; + last = tp.tv_usec; + } + syslog(LOG_INFO, + "I: ees: precision calculation given %duS after %d loop%s", + diff, i, (i==1) ? "" : "s"); + + if (i == 0) return -20 /* assume 1uS */; + if (i >= MAXLOOPS) return EESPRECISION /* Lies ! */; + for (i=0, val=USECS; val > 0; i--, val /= 2) if (diff > val) return i; + return EESPRECISION /* Lies ! */; +} + +static void dump_buf(coffs, from, to, text) +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) syslog(LOG_DEBUG, "D: %s", ptr=buff); + sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295); + } + syslog(LOG_DEBUG, "D: %s", buff); +} + +/* msfees_init - initialize internal ees driver data */ +static void msfees_init() +{ + 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(unit, peer) + u_int unit; + struct peer *peer; +{ + register struct eesunit *ees; + register int i; + int fd232 = -1; + char eesdev[20]; + struct termios ttyb, *ttyp; + static void ees_receive(); + extern int io_addclock(); + extern void io_closeclock(); + extern char *emalloc(); + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)", + unit, MAXUNITS-1); + return 0; + } + if (unitinuse[unit]) { + syslog(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) { + syslog(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) { + syslog(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) { + syslog(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) { + syslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev); + goto screwed; + } + + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(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. + */ + + { + 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) { + syslog(LOG_ERR, + "ees clock: Push of `%s' and `%s' to %s failed %m", + STREAM_PP1, STREAM_PP2, eesdev); + goto screwed; + } + else { + syslog(LOG_INFO, "I: ees clock: PUSHed %s on %s", + (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev); + ees->ttytype |= T_PPS; + } + } + + /* Add the clock */ + if (!io_addclock(&ees->io)) { + /* Oh shit. Just close and return. */ + syslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev); + goto screwed; + } + + + /* All done. Initialize a few random peer variables, then + * return success. */ + peer->precision = ees_get_precision(); + peer->stratum = stratumtouse[unit]; + peer->rootdelay = 0; /* ++++ */ + peer->rootdispersion = 0; /* ++++ */ + if (stratumtouse[unit] <= 1) { + memmove((char *)&peer->refid, EESREFID, 4); + if (unit > 0 && unit < 10) + ((char *)&peer->refid)[3] = '0' + unit; + } else { + peer->refid = htonl(EESHSREFID); + } + unitinuse[unit] = 1; + syslog(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(unit) + int unit; +{ + register struct eesunit *ees; + extern void io_closeclock(); + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, + "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)", + unit, MAXUNITS); + return; + } + if (!unitinuse[unit]) { + syslog(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(ees, code) + 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(rbufp) + 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; + static void ees_process(); + 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; + extern errno; + int rc; + + /* 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) { + syslog(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) syslog(LOG_INFO, + "I: ees clock %d: %d == %d: await more", + ees->unit, dpt, dpend); + return; + } + + /* This shouldn't happen ... ! */ + if ((*dpt & 0x7f) != 0x7f) { + syslog(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: + syslog(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(&ppsclockev, 0, sizeof ppsclockev); + + rc = ioctl(ees->io.fd, CIOGETEV, (char *) &ppsclockev); + if (debug & DB_PRINT_EV) fprintf(stderr, + "[%x] CIOGETEV u%d %d (%x %d) gave %d (%d): %08x %08x %d\n", + DB_PRINT_EV, ees->unit, ees->io.fd, CIOGETEV, 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) syslog(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 %d (%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 %x.%08x and %x.%08x -> %x.%08x @ %s", + DB_PRINT_CDT, ees->arrvtime.l_ui, ees->arrvtime.l_uf, + pps_arrvstamp.l_ui, pps_arrvstamp.l_uf, + diff.l_ui, 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); +syslog(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, + 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] %08x %08x %d u%d (%d %d)\n", + DB_PRINT_CDTC, pps_arrvstamp.l_ui, + 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 *ptr=text; + int i; + for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) { + sprintf(ptr, " %d.%04d", + msec(deltas[i]), subms(deltas[i])); + while (*ptr) ptr++; + } + syslog(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 delta = (this_step - other_step); + if (delta < 0) delta = - delta; + if (third_step == 0 && ( + (delta == 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++; + } +syslog(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]) +syslog(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=%3d.%04d@%d :%d:%d:$%d:%d:%d\n", +ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs); +syslog(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) syslog(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), + msec(delta_f_abs), + 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 ees_step_notes = EES_STEP_NOTES; + if (ees_step_notes > 0) { + ees_step_notes--; +printf("MSF%d: D=%3d.%04d@%02d :%d%s\n", +ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !"); +syslog(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) syslog(LOG_ERR, + "[%x] MSF%d: %3d/%03d -> d=%11d (%d|%d)", + 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: %3d/%03d -> d=%11d (%d|%d)\n", + ees->unit, sec_of_ramp, ees->last_step_secs, fsecs, + 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) syslog(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); + (void) pps_sample(&pps_arrvstamp); + } + + /* Subtract off the local clock time stamp */ + L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime); + if (debug & DB_LOG_SAMPLES) syslog(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! */ +} + + +static void set_x(fp_offset) +l_fp *fp_offset; +{ + step_systime_real(fp_offset); +} + + +/* offcompare - auxiliary comparison routine for offset sort */ + +static int +offcompare(a, b) +l_fp *a, *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(ees) + struct eesunit *ees; +{ + static last_samples = -1; + register int i, j; + register int noff; + register l_fp *coffs = ees->codeoffsets; + l_fp offset, tmp; + u_fp dispersion; /* ++++ */ + int lostsync, isinsync; + int samples = ees->nsamples; + int samplelog; + int samplereduce = (samples + 1) / 2; + + /* 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)) { + syslog(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, 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)) syslog( + (debug & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO, + "I: [%x] Offset=%06d (%d), disp=%06d%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))) + { syslog(LOG_INFO, "I: %x != %x (%x %x), 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)) syslog( + (debug & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO, + "I: [%x] Smooth data: %d -> %d, dispersion now %d", + 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)) syslog( + (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO, + "[%x] No smooth as delta not %d < %d < %d", + debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE), + - FRACT_SEC(100), diff, FRACT_SEC(100)); + } + else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) syslog( + (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) { + syslog(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; + set_x(&offset); + L_CLR(&offset); + } + refclock_receive(ees->peer, + &offset, + 0, /* delay */ + dispersion, + &ees->reftime, + &ees->lastsampletime, /* receive time */ + (isinsync) ? 0 : LEAP_NOTINSYNC); + ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL); +} + +/* msfees_poll - called by the transmit procedure */ +static void msfees_poll(unit, peer) + int unit; + char *peer; +{ + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid", + unit); + return; + } + if (!unitinuse[unit]) { + syslog(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); +} + +/* msfees_leap - called when a leap second occurs */ +static void msfees_leap() +{ + register int i; + + /* This routine should be entered a few seconds after + * midnight UTC when a leap second occurs. To ensure we + * don't believe foolish time from the clock(s) we set a + * 40 minute hold on them. It shouldn't take anywhere + * near this amount of time to adjust if the clock is getTING + * data, but doing anything else is complicated. + */ + for (i = 0; i < MAXUNITS; i++) if (unitinuse[i]) + eesunits[i]->leaphold = current_time + EESLEAPHOLD; +} + +/* msfees_control - set fudge factors, return statistics */ +static void msfees_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct eesunit *ees = eesunits[unit]; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "ees clock: unit %d invalid (max %d)", + unit, MAXUNITS-1); + return; + } + + if (in != 0) { + if (in->haveflags & CLK_HAVETIME1) + fudgefactor[unit] = in->fudgetime1; + if (in->haveflags & CLK_HAVETIME2) + os_delay[unit] = in->fudgetime2; + offset_fudge[unit] = os_delay[unit]; + L_ADD(&offset_fudge[unit], &fudgefactor[unit]); + L_ADD(&offset_fudge[unit], &inherent_delay[unit]); + if (in->haveflags & CLK_HAVEVAL1) { + stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf); + if (unitinuse[unit]) { + /* Should actually reselect clock, but + * will wait for the next timecode + */ + struct peer *peer = ees->peer; + peer->stratum = stratumtouse[unit]; + if (stratumtouse[unit] <= 1) { + memmove((char *)&peer->refid, + EESREFID, 4); + if (unit>0 && unit<10) + ((char *)&peer->refid)[3] = + '0' + unit; + } + else peer->refid = htonl(EESHSREFID); + } + } + if (in->haveflags & CLK_HAVEVAL2) { + printf("Debug: %x -> %x\n", debug, in->fudgeval2); + syslog(LOG_ERR, "MSF%d: debug %x -> %x", + unit, debug, in->fudgeval2); + debug = in->fudgeval2; + } + if (in->haveflags & CLK_HAVEFLAG1) { + sloppyclockflag[unit] = in->flags & CLK_FLAG1; + } + if (in->haveflags & CLK_HAVEFLAG2) { + ees->fix_pending++; + /* if (in->flags & CLK_FLAG2 && unitinuse[unit]) + ees->leaphold = 0; */ + } + if (in->haveflags & CLK_HAVEFLAG3 && unitinuse[unit]) { + printf("dump_vals: %x -> %x\n", ees->dump_vals, in->flags & CLK_FLAG3); + ees->dump_vals = in->flags & CLK_FLAG3; + } + if (in->haveflags & CLK_HAVEFLAG4 && unitinuse[unit]) { + ees->usealldata = in->flags & CLK_FLAG4; + } + } + + if (out != 0) { + out->type = REFCLK_MSF_EES; + out->haveflags + = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1|CLK_HAVEFLAG3|CLK_HAVEFLAG4; + out->clockdesc = EESDESCRIPTION; + out->fudgetime1 = fudgefactor[unit]; + out->fudgetime2 = os_delay[unit]; + out->fudgeval1 = (long)stratumtouse[unit]; + /*out->fudgeval2= debug*/; + memmove((char *)&out->fudgeval2, EESREFID, 4); + if (unit > 0 && unit < 10) + ((char *)&out->fudgeval2)[3] = '0' + unit; + out->flags = sloppyclockflag[unit]; + if (unitinuse[unit]) { + out->flags |= ees->dump_vals | ees->usealldata; + out->lencode = ees->lencode; + out->lastcode = ees->lastcode; + out->timereset = current_time - ees->timestarted; + out->polls = 0; /* we don't poll */ + out->noresponse = 0; /* ditto */ + out->badformat = ees->badformat; + out->baddata = ees->baddata; + out->lastevent = ees->lastevent; + out->currentstatus = ees->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; + } + } +} + + +/* msfees_buginfo - return clock dependent debugging info */ +static void msfees_buginfo(unit, bug) + int unit; + register struct refclockbug *bug; +{ + register struct eesunit *ees; + + bug->nvalues = bug->ntimes = 0; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "ees clock: unit %d invalid (max %d)", + unit, MAXUNITS-1); + return; + } + + if (!unitinuse[unit]) + return; + ees = eesunits[unit]; + + bug->nvalues = 16; + bug->svalues = 0x0800; + 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->ntimes = 11; + bug->stimes = 0x3f8; + 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].l_ui = ees->yearstart; + bug->times[10].l_uf = 0; +} + +struct refclock refclock_msfees = { + msfees_start, msfees_shutdown, msfees_poll, + msfees_control, msfees_init, msfees_buginfo, NOFLAGS +}; +#endif /* defined(REFCLOCK) && defined(MSFEESPPS) && defined(STREAM) */ diff --git a/usr.sbin/xntpd/xntpd/refclock_old/refclock_mx4200.c b/usr.sbin/xntpd/xntpd/refclock_old/refclock_mx4200.c new file mode 100644 index 000000000000..7cfa3cf0326b --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_old/refclock_mx4200.c @@ -0,0 +1,1342 @@ +/* + * 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. + */ + +#if defined(REFCLOCK) && (defined(MX4200) || defined(MX4200CLK) || defined(MX4200PPS)) + +#if !defined(lint) && !defined(__GNUC__) +static char rcsid[] = + "@(#) /src/master/xntp-930612/xntpd/refclock_mx4200.c,v 1.5 1993/06/18 21:19:54 jbj Exp (LBL) "; +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_calendar.h" +#include "ntp_unixtime.h" + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#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(MX4200CLK) +#include <sys/clkdefs.h> +#endif /* MX4200CLK */ +#endif /* STREAM */ + +#include <sys/ppsclock.h> + +#include "mx4200.h" +#include "ntp_stdlib.h" + +/* + * This driver supports the Magnavox Model MX4200 GPS Receiver. + */ + +/* + * Definitions + */ +#define MAXUNITS 2 /* max number of mx4200 units */ +#define MX4200232 "/dev/gps%d" +#define SPEED232 B4800 /* baud */ + +/* + * The number of raw samples which we acquire to derive a single estimate. + */ +#define NSTAMPS 64 + +/* + * Radio interface parameters + */ +#define MX4200PRECISION (-18) /* precision assumed (about 4 us) */ +#define MX4200REFID "GPS" /* reference id */ +#define MX4200DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */ +#define DEFFUDGETIME 0 /* default fudge time (ms) */ + +/* Leap stuff */ +extern U_LONG leap_hoursfromleap; +extern U_LONG leap_happened; +static int leap_debug; + +/* + * mx4200_reset - reset the count back to zero + */ +#define mx4200_reset(mx4200) \ + do { \ + (mx4200)->nsamples = 0; \ + } while (0) + +/* + * mx4200_event - record and report an event + */ +#define mx4200_event(mx4200, evcode) \ + do { \ + if ((mx4200)->status != (u_char)(evcode)) \ + mx4200_report_event((mx4200), (evcode)); \ + } while (0) + +/* + * Imported from the timer module + */ +extern U_LONG current_time; +extern struct event timerqueue[]; + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * MX4200 unit control structure. + */ +struct mx4200unit { + struct peer *peer; /* associated peer structure */ + struct refclockio io; /* given to the I/O handler */ + U_LONG gpssamples[NSTAMPS]; /* the GPS time samples */ + l_fp unixsamples[NSTAMPS]; /* the UNIX time samples */ + + + l_fp lastsampletime; /* time of last estimate */ + u_int lastserial; /* last pps serial number */ +#ifdef notdef + l_fp lastrec; /* last receive time */ + l_fp lastref; /* last timecode time */ +#endif + char lastcode[RX_BUFF_SIZE]; /* last timecode received */ + U_LONG lasttime; /* last time clock heard from */ + u_char nsamples; /* number of samples we've collected */ + 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 lencode; /* length of last timecode */ + u_char year; /* year of eternity */ + u_short monthday; /* day of month */ + u_char hour; /* hour of day */ + u_char minute; /* minute of hour */ + u_char second; /* seconds of minute */ + u_char leap; /* leap indicators */ + /* + * Status tallies + */ +#ifdef notdef + U_LONG polls; /* polls sent */ + U_LONG noresponse; /* number of nonresponses */ +#endif + U_LONG badformat; /* bad format */ + U_LONG baddata; /* bad data */ + U_LONG timestarted; /* time we started this */ +}; + +/* + * We demand that consecutive PPS samples are more than 0.995 seconds + * and less than 1.005 seconds apart. + */ +#define PPSLODIFF_UI 0 /* 0.900 as an l_fp */ +#define PPSLODIFF_UF 0xe6666610 + +#define PPSHIDIFF_UI 1 /* 1.100 as an l_fp */ +#define PPSHIDIFF_UF 0x19999990 + +/* + * reason codes + */ +#define PPSREASON 20 +#define CODEREASON 40 +#define PROCREASON 60 + +/* + * Data space for the unit structures. Note that we allocate these on + * the fly, but never give them back. + */ +static struct mx4200unit *mx4200units[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]; +static U_LONG refid[MAXUNITS]; + +static const char pmvxg[] = "PMVXG"; + +/* + * Function prototypes + */ +static void mx4200_init P((void)); +static int mx4200_start P((u_int, struct peer *)); +static void mx4200_shutdown P((int)); +static void mx4200_receive P((struct recvbuf *)); +static void mx4200_process P((struct mx4200unit *)); +static void mx4200_report_event P((struct mx4200unit *, int)); +static void mx4200_poll P((int, struct peer *)); +static void mx4200_control P((u_int, struct refclockstat *, struct refclockstat *)); +static void mx4200_buginfo P((int, struct refclockbug *)); + +static char * mx4200_parse P((char *, struct calendar *, int *, int *)); +static int mx4200_needconf P((char *)); +static void mx4200_config P((struct mx4200unit *)); +static void mx4200_send P((int, const char *, ...)); +static int mx4200_cmpl_fp P((void *, void *)); +static u_char cksum P((char *, u_int)); + +#ifdef DEBUG +static void opendfile P((int)); +static void checkdfile P((void)); +#endif /* DEBUG */ + +/* + * Transfer vector + */ +struct refclock refclock_mx4200 = { + mx4200_start, mx4200_shutdown, mx4200_poll, + mx4200_control, mx4200_init, mx4200_buginfo, NOFLAGS +}; + +/* + * mx4200_init - initialize internal mx4200 driver data + */ +static void +mx4200_init() +{ + register int i; + /* + * Just zero the data arrays + */ + memset((char *)mx4200units, 0, sizeof mx4200units); + memset((char *)unitinuse, 0, sizeof unitinuse); + + /* + * Initialize fudge factors to default. + */ + for (i = 0; i < MAXUNITS; i++) { + fudgefactor[i].l_ui = 0; + fudgefactor[i].l_uf = DEFFUDGETIME; + stratumtouse[i] = 0; + sloppyclockflag[i] = 0; + memcpy((char *)&refid[i], MX4200REFID, 4); + } +} + +#ifdef DEBUG +static char dfile[] = "/var/tmp/MX4200.debug"; +static FILE *df = NULL; + +static void +opendfile(create) + int create; +{ + if (!create && access(dfile, F_OK) < 0) { + syslog(LOG_ERR, "mx4200: open %s: %m", dfile); + return; + } + df = fopen(dfile, "a"); + if (df == NULL) + syslog(LOG_ERR, "mx4200: open %s: %m", dfile); + else if (setvbuf(df, NULL, _IOLBF, 0) < 0) + syslog(LOG_ERR, "mx4200: setvbuf %s: %m", dfile); +} + +static void +checkdfile() +{ + + if (df == NULL) + return; + + if (access(dfile, F_OK) < 0) { + fclose(df); + opendfile(1); + } +} + +#endif + + +/* + * mx4200_start - open the MX4200 devices and initialize data for processing + */ +static int +mx4200_start(unit, peer) + u_int unit; + struct peer *peer; +{ + register struct mx4200unit *mx4200; + register int i; + int fd232; + char mx4200dev[20]; + + /* + * Check configuration info + */ + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "mx4200_start: unit %d invalid", unit); + return (0); + } + if (unitinuse[unit]) { + syslog(LOG_ERR, "mx4200_start: unit %d in use", unit); + return (0); + } + + /* + * Open serial port + */ + (void) sprintf(mx4200dev, MX4200232, unit); + fd232 = open(mx4200dev, O_RDWR, 0777); + if (fd232 == -1) { + syslog(LOG_ERR, + "mx4200_start: open of %s: %m", mx4200dev); + return (0); + } + +#if defined(HAVE_SYSV_TTYS) + /* + * System V serial line parameters (termio interface) + * + */ + { struct termio ttyb; + if (ioctl(fd232, TCGETA, &ttyb) < 0) { + syslog(LOG_ERR, + "mx4200_start: ioctl(%s, TCGETA): %m", mx4200dev); + 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) { + syslog(LOG_ERR, + "mx4200_start: ioctl(%s, TCSETA): %m", mx4200dev); + goto screwed; + } + } +#endif /* HAVE_SYSV_TTYS */ +#if defined(HAVE_TERMIOS) + /* + * POSIX serial line parameters (termios interface) + * + * The MX4200CLK option provides timestamping at the driver level. + * It requires the tty_clk streams module. + * + * The MX4200PPS 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 SunOS 4.1.1 or + * later. + */ + { struct termios ttyb, *ttyp; + + ttyp = &ttyb; + if (tcgetattr(fd232, ttyp) < 0) { + syslog(LOG_ERR, + "mx4200_start: tcgetattr(%s): %m", mx4200dev); + 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) { + syslog(LOG_ERR, + "mx4200_start: tcsetattr(%s): %m", mx4200dev); + goto screwed; + } + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(LOG_ERR, + "mx4200_start: tcflush(%s): %m", mx4200dev); + goto screwed; + } + } +#endif /* HAVE_TERMIOS */ +#ifdef STREAM +#if defined(MX4200CLK) + if (ioctl(fd232, I_PUSH, "clk") < 0) + syslog(LOG_ERR, + "mx4200_start: ioctl(%s, I_PUSH, clk): %m", mx4200dev); + if (ioctl(fd232, CLK_SETSTR, "\n") < 0) + syslog(LOG_ERR, + "mx4200_start: ioctl(%s, CLK_SETSTR): %m", mx4200dev); +#endif /* MX4200CLK */ +#if defined(MX4200PPS) + if (ioctl(fd232, I_PUSH, "ppsclock") < 0) + syslog(LOG_ERR, + "mx4200_start: ioctl(%s, I_PUSH, ppsclock): %m", mx4200dev); + else + fdpps = fd232; +#endif /* MX4200PPS */ +#endif /* STREAM */ +#if defined(HAVE_BSD_TTYS) + /* + * 4.3bsd serial line parameters (sgttyb interface) + * + * The MX4200CLK option provides timestamping at the driver level. + * It requires the tty_clk line discipline and 4.3bsd or later. + */ + { struct sgttyb ttyb; +#if defined(MX4200CLK) + int ldisc = CLKLDISC; +#endif /* MX4200CLK */ + + if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { + syslog(LOG_ERR, + "mx4200_start: ioctl(%s, TIOCGETP): %m", mx4200dev); + goto screwed; + } + ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; +#if defined(MX4200CLK) + 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 /* MX4200CLK */ + if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { + syslog(LOG_ERR, + "mx4200_start: ioctl(%s, TIOCSETP): %m", mx4200dev); + goto screwed; + } +#if defined(MX4200CLK) + if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { + syslog(LOG_ERR, + "mx4200_start: ioctl(%s, TIOCSETD): %m",mx4200dev); + goto screwed; + } +#endif /* MX4200CLK */ + } +#endif /* HAVE_BSD_TTYS */ + + /* + * Allocate unit structure + */ + if (mx4200units[unit] != 0) { + mx4200 = mx4200units[unit]; /* The one we want is okay */ + } else { + for (i = 0; i < MAXUNITS; i++) { + if (!unitinuse[i] && mx4200units[i] != 0) + break; + } + if (i < MAXUNITS) { + /* + * Reclaim this one + */ + mx4200 = mx4200units[i]; + mx4200units[i] = 0; + } else { + mx4200 = (struct mx4200unit *) + emalloc(sizeof(struct mx4200unit)); + } + } + + memset((char *)mx4200, 0, sizeof(struct mx4200unit)); + mx4200units[unit] = mx4200; + + /* + * Set up the structures + */ + mx4200->peer = peer; + mx4200->unit = (u_char)unit; + mx4200->timestarted = current_time; + + mx4200->io.clock_recv = mx4200_receive; + mx4200->io.srcclock = (caddr_t)mx4200; + mx4200->io.datalen = 0; + mx4200->io.fd = fd232; + if (!io_addclock(&mx4200->io)) + goto screwed; + + /* + * All done. Initialize a few random peer variables, then + * return success. + */ + peer->precision = MX4200PRECISION; + peer->rootdelay = 0; + peer->rootdispersion = 0; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + unitinuse[unit] = 1; + + /* Insure the receiver is properly configured */ + mx4200_config(mx4200); + +#ifdef DEBUG + opendfile(0); +#endif + return (1); + + /* + * Something broke; abandon ship + */ +screwed: + (void) close(fd232); + return (0); +} + +/* + * mx4200_shutdown - shut down a MX4200 clock + */ +static void +mx4200_shutdown(unit) + int unit; +{ + register struct mx4200unit *mx4200; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "mx4200_shutdown: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "mx4200_shutdown: unit %d not in use", unit); + return; + } + + /* + * Tell the I/O module to turn us off. We're history. + */ + mx4200 = mx4200units[unit]; + io_closeclock(&mx4200->io); + unitinuse[unit] = 0; +} + +static void +mx4200_config(mx4200) + register struct mx4200unit *mx4200; +{ + register int fd = mx4200->io.fd; + +syslog(LOG_DEBUG, "mx4200_config"); + + /* Zero the output list (do it twice to flush possible junk) */ + mx4200_send(fd, "%s,%03d,,%d,,,,,,", pmvxg, PMVXG_S_PORTCONF, 1); + mx4200_send(fd, "%s,%03d,,%d,,,,,,", pmvxg, PMVXG_S_PORTCONF, 1); + + /* Switch to 2d mode */ + mx4200_send(fd, "%s,%03d,%d,,%.1f,%.1f,,%d,%d,%c,%d", + pmvxg, PMVXG_S_INITMODEB, + 2, /* 2d mode */ + 0.1, /* hor accel fact as per Steve */ + 0.1, /* ver accel fact as per Steve */ + 10, /* hdop limit as per Steve */ + 5, /* elevation limit as per Steve */ + 'U', /* time output mode */ + 0); /* local time offset from gmt */ + + /* Configure time recovery */ + mx4200_send(fd, "%s,%03d,%c,%c,%c,%d,%d,%d,", + pmvxg, PMVXG_S_TRECOVCONF, +#ifdef notdef + 'K', /* known position */ + 'D', /* dynamic position */ +#else + 'S', /* static position */ +#endif + 'U', /* steer clock to gps time */ + 'A', /* always output time pulse */ + 500, /* max time error in ns */ + 0, /* user bias in ns */ + 1); /* output to control port */ +} + +/* + * mx4200_report_event - note the occurrence of an event + */ +static void +mx4200_report_event(mx4200, code) + struct mx4200unit *mx4200; + int code; +{ + struct peer *peer; + + peer = mx4200->peer; + if (mx4200->status != (u_char)code) { + mx4200->status = (u_char)code; + if (code != CEVNT_NOMINAL) + mx4200->lastevent = (u_char)code; + syslog(LOG_INFO, + "mx4200 clock %s event %x", ntoa(&peer->srcadr), code); + } +} + +/* + * mx4200_poll - mx4200 watchdog routine + */ +static void +mx4200_poll(unit, peer) + int unit; + struct peer *peer; +{ + register struct mx4200unit *mx4200; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "mx4200_poll: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "mx4200_poll: unit %d not used", unit); + return; + } + + mx4200 = mx4200units[unit]; + if ((current_time - mx4200->lasttime) > 150) { + mx4200_event(mx4200, CEVNT_FAULT); + + /* Request a status message which should trigger a reconfig */ + mx4200_send(mx4200->io.fd, "%s,%03d", "CDGPQ", PMVXG_D_STATUS); + syslog(LOG_DEBUG, "mx4200_poll: request status"); + } +} + +static const char char2hex[] = "0123456789ABCDEF"; + +/* + * mx4200_receive - receive gps data + */ +static void +mx4200_receive(rbufp) + struct recvbuf *rbufp; +{ + register struct mx4200unit *mx4200; + register char *dpt, *cp; + register U_LONG tmp_ui; + register U_LONG tmp_uf; + register U_LONG gpstime; + struct ppsclockev ev; + register struct calendar *jt; + struct calendar sjt; + register int n; + int valid, leapsec; + register u_char ck; + + mx4200 = (struct mx4200unit *)rbufp->recv_srcclock; + +#ifdef DEBUG + if (debug > 3) + printf("mx4200_receive: nsamples = %d\n", mx4200->nsamples); +#endif + + /* Record the time of this event */ + mx4200->lasttime = current_time; + + /* Get the pps value */ + if (ioctl(mx4200->io.fd, CIOGETEV, (char *)&ev) < 0) { + /* XXX Actually, if this fails, we're pretty much screwed */ +#ifdef DEBUG + if (debug) { + fprintf(stderr, "mx4200_receive: "); + perror("CIOGETEV"); + } +#endif + mx4200->reason = PPSREASON + 1; + mx4200_event(mx4200, CEVNT_FAULT); + mx4200_reset(mx4200); + return; + } + tmp_ui = ev.tv.tv_sec + (U_LONG)JAN_1970; + TVUTOTSF(ev.tv.tv_usec, tmp_uf); + + /* Get buffer and length; sock away last timecode */ + n = rbufp->recv_length; + dpt = rbufp->recv_buffer; + if (n <= 1) + return; + mx4200->lencode = n; + memmove(mx4200->lastcode, dpt, n); + + /* + * We expect to see something like: + * + * $PMVXG,830,T,1992,07,09,04:18:34,U,S,-02154,00019,000000,00*1D\n + * + * Reject if any important landmarks are missing. + */ + cp = dpt + n - 4; + if (cp < dpt || *dpt != '$' || cp[0] != '*' || cp[3] != '\n') { +#ifdef DEBUG + if (debug) + printf("mx4200_receive: bad format\n"); +#endif + mx4200->badformat++; + mx4200->reason = PPSREASON + 2; + mx4200_event(mx4200, CEVNT_BADREPLY); + mx4200_reset(mx4200); + return; + } + + /* Check checksum */ + ck = cksum(&dpt[1], n - 5); + if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) { +#ifdef DEBUG + if (debug) + printf("mx4200_receive: bad checksum\n"); +#endif + mx4200->badformat++; + mx4200->reason = PPSREASON + 3; + mx4200_event(mx4200, CEVNT_BADREPLY); + mx4200_reset(mx4200); + return; + } + + /* Truncate checksum (and the buffer for that matter) */ + *cp = '\0'; + + /* Leap second debugging stuff */ + if ((leap_hoursfromleap && !leap_happened) || leap_debug > 0) { + /* generate reports for awhile after leap */ + if (leap_hoursfromleap && !leap_happened) + leap_debug = 3600; + else + --leap_debug; + syslog(LOG_INFO, "mx4200 leap: %s \"%s\"", + umfptoa(tmp_ui, tmp_uf, 6), dpt); + } + + /* Parse time recovery message */ + jt = &sjt; + if ((cp = mx4200_parse(dpt, jt, &valid, &leapsec)) != NULL) { + /* Configure the receiver if necessary */ + if (mx4200_needconf(dpt)) + mx4200_config(mx4200); +#ifdef DEBUG + if (debug) + printf("mx4200_receive: mx4200_parse: %s\n", cp); +#endif + mx4200->badformat++; + mx4200->reason = PPSREASON + 5; + mx4200_event(mx4200, CEVNT_BADREPLY); + mx4200_reset(mx4200); + return; + } + + /* Setup leap second indicator */ + if (leapsec == 0) + mx4200->leap = LEAP_NOWARNING; + else if (leapsec == 1) + mx4200->leap = LEAP_ADDSECOND; + else if (leapsec == -1) + mx4200->leap = LEAP_DELSECOND; + else + mx4200->leap = LEAP_NOTINSYNC; /* shouldn't happen */ + + /* Check parsed time (allow for possible leap seconds) */ + if (jt->second >= 61 || jt->minute >= 60 || jt->hour >= 24) { +#ifdef DEBUG + if (debug) { + printf("mx4200_receive: bad time %d:%02d:%02d", + jt->hour, jt->minute, jt->second); + if (leapsec != 0) + printf(" (leap %+d)", leapsec); + putchar('\n'); + } +#endif + mx4200->baddata++; + mx4200->reason = PPSREASON + 6; + mx4200_event(mx4200, CEVNT_BADTIME); + mx4200_reset(mx4200); + /* Eat the next pulse which the clock claims will be bad */ + mx4200->nsamples = -1; + return; + } + + /* Check parsed date */ + if (jt->monthday > 31 || jt->month > 12 || jt->year < 1900) { +#ifdef DEBUG + if (debug) + printf("mx4200_receive: bad date (%d/%d/%d)\n", + jt->monthday, jt->month, jt->year); +#endif + mx4200->baddata++; + mx4200->reason = PPSREASON + 7; + mx4200_event(mx4200, CEVNT_BADDATE); + mx4200_reset(mx4200); + return; + } + + /* Convert to ntp time */ + gpstime = caltontp(jt); + + /* The gps message describes the *next* pulse; pretend it's this one */ + --gpstime; + + /* Debugging */ +#ifdef DEBUG + checkdfile(); + if (df != NULL) { + l_fp t; + + t.l_ui = gpstime; + t.l_uf = 0; + M_SUB(t.l_ui, t.l_uf, tmp_ui, tmp_uf); + fprintf(df, "%s\t%s", + umfptoa(tmp_ui, tmp_uf, 6), mfptoa(t.l_ui, t.l_uf, 6)); + if (debug > 3) + fprintf(df, "\t(gps: %lu)", gpstime); + if (leapsec != 0) + fprintf(df, "\t(leap sec %+d)", leapsec); + if (!valid) + fprintf(df, "\t(pulse not valid)"); + fputc('\n', df); + } +#endif + + /* Check pps serial number against last one */ + if (mx4200->lastserial + 1 != ev.serial && mx4200->lastserial != 0) { +#ifdef DEBUG + if (debug) { + if (ev.serial == mx4200->lastserial) + printf("mx4200_receive: no new pps event\n"); + else + printf("mx4200_receive: missed %d pps events\n", + ev.serial - mx4200->lastserial - 1); + } +#endif + mx4200->reason = PPSREASON + 8; + mx4200_event(mx4200, CEVNT_FAULT); + mx4200_reset(mx4200); + /* fall through and this one collect as first sample */ + } + mx4200->lastserial = ev.serial; + +/* + * XXX + * Since this message is for the next pulse, it's really the next pulse + * that the clock might be telling us will be invalid. + */ + /* Toss if not designated "valid" by the gps */ + if (!valid) { +#ifdef DEBUG + if (debug) + printf("mx4200_receive: pps not valid\n"); +#endif + mx4200->reason = PPSREASON + 9; + mx4200_event(mx4200, CEVNT_BADTIME); + mx4200_reset(mx4200); + return; + } + + /* Copy time into mx4200unit struct */ + /* XXX (why?) */ + mx4200->year = jt->year; + mx4200->monthday = jt->monthday; + mx4200->hour = jt->hour; + mx4200->minute = jt->minute; + mx4200->second = jt->second; + + /* Sock away the GPS and UNIX timesamples */ + n = mx4200->nsamples++; + if (n < 0) + return; /* oops, this pulse is bad */ + mx4200->gpssamples[n] = gpstime; + mx4200->unixsamples[n].l_ui = mx4200->lastsampletime.l_ui = tmp_ui; + mx4200->unixsamples[n].l_uf = mx4200->lastsampletime.l_uf = tmp_uf; + if (mx4200->nsamples >= NSTAMPS) { + /* + * Here we've managed to complete an entire NSTAMPS + * second cycle without major mishap. Process what has + * been received. + */ + mx4200_process(mx4200); + mx4200_reset(mx4200); + } +} + +/* Compare two l_fp's, used with qsort() */ +static int +mx4200_cmpl_fp(p1, p2) + register void *p1, *p2; +{ + + if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2)) + return (-1); + if (L_ISEQU((l_fp *)p1, (l_fp *)p2)) + return (0); + return (1); +} + +/* + * mx4200_process - process a pile of samples from the clock + */ +static void +mx4200_process(mx4200) + struct mx4200unit *mx4200; +{ + register int i, n; + register l_fp *fp, *op; + register U_LONG *lp; + l_fp off[NSTAMPS]; + register U_LONG tmp_ui, tmp_uf; + register U_LONG date_ui, date_uf; + u_fp dispersion; + + /* Compute offsets from the raw data. */ + fp = mx4200->unixsamples; + op = off; + lp = mx4200->gpssamples; + for (i = 0; i < NSTAMPS; ++i, ++lp, ++op, ++fp) { + op->l_ui = *lp; + op->l_uf = 0; + L_SUB(op, fp); + } + + /* Sort offsets into ascending order. */ + qsort((char *)off, NSTAMPS, sizeof(l_fp), mx4200_cmpl_fp); + + /* + * Reject the furthest from the median until 8 samples left + */ + i = 0; + n = NSTAMPS; + while ((n - i) > 8) { + tmp_ui = off[n-1].l_ui; + tmp_uf = off[n-1].l_uf; + date_ui = off[(n+i)/2].l_ui; + date_uf = off[(n+i)/2].l_uf; + M_SUB(tmp_ui, tmp_uf, date_ui, date_uf); + M_SUB(date_ui, date_uf, off[i].l_ui, off[i].l_uf); + if (M_ISHIS(date_ui, date_uf, tmp_ui, tmp_uf)) { + /* + * reject low end + */ + i++; + } else { + /* + * reject high end + */ + n--; + } + } + + /* + * Compute the dispersion based on the difference between the + * extremes of the remaining offsets. + */ + tmp_ui = off[n-1].l_ui; + tmp_uf = off[n-1].l_uf; + M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf); + dispersion = MFPTOFP(tmp_ui, tmp_uf); + + /* + * Now compute the offset estimate. If the sloppy clock + * flag is set, average the remainder, otherwise pick the + * median. + */ + if (sloppyclockflag[mx4200->unit]) { + tmp_ui = tmp_uf = 0; + while (i < n) { + M_ADD(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf); + i++; + } + M_RSHIFT(tmp_ui, tmp_uf); + M_RSHIFT(tmp_ui, tmp_uf); + M_RSHIFT(tmp_ui, tmp_uf); + i = 0; + off[0].l_ui = tmp_ui; + off[0].l_uf = tmp_uf; + } else { + i = (n + i) / 2; + } + + /* + * Add the default MX4200 QT delay into this. + */ +#ifdef notdef + L_ADDUF(&off[i], MX4200QTFUDGE); +#endif + + /* + * Done. Use lastref as the reference time and lastrec + * as the receive time. ** note this can result in tossing + * out the peer in the protocol module if lastref > lastrec, + * so last rec is used for both values - dlm *** + */ + refclock_receive(mx4200->peer, &off[i], + (s_fp)0, /* delay */ + dispersion, + &mx4200->unixsamples[NSTAMPS-1], /* reftime */ + &mx4200->unixsamples[NSTAMPS-1], /* rectime */ + mx4200->leap); + + mx4200_event(mx4200, CEVNT_NOMINAL); +} + +/* + * mx4200_control - set fudge factors, return statistics + */ +static void +mx4200_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct mx4200unit *mx4200; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "mx4200_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); + if (in->haveflags & CLK_HAVEVAL2) + refid[unit] = in->fudgeval2; + if (in->haveflags & CLK_HAVEFLAG1) + sloppyclockflag[unit] = in->flags & CLK_FLAG1; + if (unitinuse[unit]) { + struct peer *peer; + + peer = mx4200units[unit]->peer; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + } + } + + if (out != 0) { + memset((char *)out, 0, sizeof (struct refclockstat)); + out->type = REFCLK_GPS_MX4200; + out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 | CLK_HAVEVAL2 | + CLK_HAVEFLAG1; + out->clockdesc = MX4200DESCRIPTION; + out->fudgetime1 = fudgefactor[unit]; + out->fudgeval1 = (LONG)stratumtouse[unit]; + out->fudgeval2 = refid[unit];; + out->flags = sloppyclockflag[unit]; + if (unitinuse[unit]) { + mx4200 = mx4200units[unit]; + out->lencode = mx4200->lencode; + out->lastcode = mx4200->lastcode; + out->lastevent = mx4200->lastevent; + out->currentstatus = mx4200->status; + + out->polls = 0; /* mx4200->polls; */ + out->noresponse = 0; /* mx4200->noresponse; */ + out->badformat = mx4200->badformat; + out->baddata = mx4200->baddata; + out->timereset = current_time - mx4200->timestarted; + } + } +} + +/* + * mx4200_buginfo - return clock dependent debugging info + */ +static void +mx4200_buginfo(unit, bug) + int unit; + register struct refclockbug *bug; +{ + register struct mx4200unit *mx4200; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "mx4200_buginfo: unit %d invalid", unit); + return; + } + + if (!unitinuse[unit]) + return; + mx4200 = mx4200units[unit]; + + memset((char *)bug, 0, sizeof(*bug)); + bug->nvalues = 10; + bug->ntimes = 2; + if (mx4200->lasttime != 0) + bug->values[0] = current_time - mx4200->lasttime; + else + bug->values[0] = 0; + bug->values[1] = (U_LONG)mx4200->reason; + bug->values[2] = (U_LONG)mx4200->year; + bug->values[3] = (U_LONG)mx4200->monthday; + bug->values[4] = (U_LONG)mx4200->hour; + bug->values[5] = (U_LONG)mx4200->minute; + bug->values[6] = (U_LONG)mx4200->second; +#ifdef notdef + bug->values[7] = mx4200->msec; + bug->values[8] = mx4200->noreply; + bug->values[9] = mx4200->yearstart; +#endif + bug->stimes = 0x1c; +#ifdef notdef + bug->times[0] = mx4200->lastref; + bug->times[1] = mx4200->lastrec; +#endif +} + +/* + * Returns true if the this is a status message. We use this as + * an indication that the receiver needs to be initialized. + */ +static int +mx4200_needconf(buf) + char *buf; +{ + register LONG v; + char *cp; + + cp = buf; + + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Record type */ + v = strtol(cp, &cp, 10); + if (v != PMVXG_D_STATUS) + return (0); + /* + * 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. + */ +#ifdef notdef + ++cp; + + /* Receiver status */ + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Number of satellites which should be visible */ + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Number of satellites being tracked */ + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Time since last NAV */ + if ((cp = strchr(cp, ',')) == NULL) + return (0); + ++cp; + + /* Initialization status */ + v = strtol(cp, &cp, 10); + if (v == 0) +#endif + return (1); +} + +/* Parse a mx4200 time recovery message. Returns a string if error */ +static char * +mx4200_parse(buf, jt, validp, leapsecp) + register char *buf; + register struct calendar *jt; + register int *validp, *leapsecp; +{ + register LONG v; + char *cp; + + cp = buf; + memset((char *)jt, 0, sizeof(*jt)); + + if ((cp = strchr(cp, ',')) == NULL) + return ("no rec-type"); + ++cp; + + /* Record type */ + v = strtol(cp, &cp, 10); + if (v != PMVXG_D_TRECOVOUT) + return ("wrong rec-type"); + + /* Pulse valid indicator */ + if (*cp++ != ',') + return ("no pulse-valid"); + if (*cp == 'T') + *validp = 1; + else if (*cp == 'F') + *validp = 0; + else + return ("bad pulse-valid"); + ++cp; + + /* Year */ + if (*cp++ != ',') + return ("no year"); + jt->year = strtol(cp, &cp, 10); + + /* Month of year */ + if (*cp++ != ',') + return ("no month"); + jt->month = strtol(cp, &cp, 10); + + /* Day of month */ + if (*cp++ != ',') + return ("no month day"); + jt->monthday = strtol(cp, &cp, 10); + + /* Hour */ + if (*cp++ != ',') + return ("no hour"); + jt->hour = strtol(cp, &cp, 10); + + /* Minute */ + if (*cp++ != ':') + return ("no minute"); + jt->minute = strtol(cp, &cp, 10); + + /* Second */ + if (*cp++ != ':') + return ("no second"); + jt->second = strtol(cp, &cp, 10); + + /* Time indicator */ + if (*cp++ != ',' || *cp++ == '\0') + return ("no time indicator"); + + /* Time recovery mode */ + if (*cp++ != ',' || *cp++ == '\0') + return ("no time mode"); + + /* Oscillator offset */ + if ((cp = strchr(cp, ',')) == NULL) + return ("no osc off"); + ++cp; + + /* Time mark error */ + if ((cp = strchr(cp, ',')) == NULL) + return ("no time mark err"); + ++cp; + + /* User time bias */ + if ((cp = strchr(cp, ',')) == NULL) + return ("no user bias"); + ++cp; + + /* Leap second flag */ + if ((cp = strchr(cp, ',')) == NULL) + return ("no leap"); + ++cp; + *leapsecp = strtol(cp, &cp, 10); + + return (NULL); +} + +/* Calculate the checksum */ +static u_char +cksum(cp, n) + register char *cp; + register u_int n; +{ + register u_char ck; + + for (ck = 0; n-- > 0; ++cp) + ck ^= *cp; + return (ck); +} + +static void +#if __STDC__ +mx4200_send(register int fd, const char *fmt, ...) +#else +mx4200_send(fd, fmt, va_alist) + register int fd; + const char *fmt; + va_dcl +#endif +{ + 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 + 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 + ck = 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 + + m = write(fd, buf, n); + if (m < 0) + syslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf); + else if (m != n) + syslog(LOG_ERR, "mx4200_send: write: %d != %d (%s)", m, n, buf); + va_end(ap); +} +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_old/refclock_omega.c b/usr.sbin/xntpd/xntpd/refclock_old/refclock_omega.c new file mode 100644 index 000000000000..95a20a0d2c44 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_old/refclock_omega.c @@ -0,0 +1,999 @@ +/* + * refclock_omega - clock driver for the Kinemetrics Truetime OM-DC OMEGA + * receiver. + * + * Version 1.0 11-Dec-92 Steve Clift (clift@ml.csiro.au) + * Initial version, mostly lifted from refclock_goes.c. + * + * 1.1 03-May-93 Steve Clift + * Tarted up the sample filtering mechanism to give improved + * one-off measurements. Improved measurement dispersion code + * to account for accumulated drift when the clock loses lock. + * + */ + +#if defined(REFCLOCK) && (defined(OMEGA) || defined(OMEGACLK) || defined(OMEGAPPS)) + +#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" + +#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(OMEGACLK) +#include <sys/clkdefs.h> +#endif /* OMEGACLK */ +#endif /* STREAM */ + +#if defined (OMEGAPPS) +#include <sys/ppsclock.h> +#endif /* OMEGAPPS */ + +#include "ntp_stdlib.h" + +/* + * Support for Kinemetrics Truetime OM-DC OMEGA Receiver + * + * Most of this code is copied from refclock_goes.c with thanks. + * + * the time code looks like follows; Send the clock a R or C and once per + * second a timestamp will appear that looks like this: + * ADDD:HH:MM:SSQCL + * A - control A + * Q Quality indication: indicates possible error of + * > >+- 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. + * C - Carriage return + * L - Line feed + * The carriage return start bit begins on 0 seconds and extends to 1 bit time. + */ + +/* + * Definitions + */ +#define MAXUNITS 4 /* max number of OMEGA units */ +#define OMEGA232 "/dev/omega%d" +#define SPEED232 B9600 /* 9600 baud */ + +/* + * Radio interface parameters + */ +#define OMEGADESCRIPTION "Kinemetrics OM-DC OMEGA Receiver" /* who we are */ +#define OMEGAMAXDISPERSE (FP_SECOND/32) /* max allowed sample dispersion */ +#define OMEGAPRECISION (-10) /* precision assumed (about 1 ms) */ +#define OMEGAREFID "VLF\0" /* reference id */ +#define LENOMEGA 13 /* length of standard response */ +#define GMT 0 /* hour offset from Greenwich */ +#define NSTAMPS 9 /* samples collected when polled */ +#define NSKEEP 5 /* samples to keep after discards */ +#define BMAX 50 /* timecode buffer length */ + +/* + * The OM-DC puts out the start bit of the <CR> on the second, but + * we see the result after the <LF> is received, about 2ms later at + * 9600 baud. Use this as the default fudge time, and let the user + * fiddle it to account for driver latency etc. + */ +#define DEFFUDGETIME 0x00830000 /* default fudge time (~2ms) */ + +/* + * Clock drift errors as u_fp values. + */ +#define U_FP5000MS (5*FP_SECOND) /* 5 seconds */ +#define U_FP500MS (FP_SECOND/2) /* 500 msec */ +#define U_FP50MS (FP_SECOND/20) /* 50 msec */ +#define U_FP5MS (FP_SECOND/200) /* 5 msec */ + +/* + * Station codes + */ +#define STATION_NONE 0 +#define STATION_NORWAY 1 +#define STATION_LIBERIA 2 +#define STATION_HAWAII 3 +#define STATION_N_DAKOTA 4 +#define STATION_LA_REUNION 5 +#define STATION_ARGENTINA 6 +#define STATION_AUSTRALIA 7 +#define STATION_JAPAN 8 + +/* + * Hack to avoid excercising the multiplier. I have no pride. + */ +#define MULBY10(x) (((x)<<3) + ((x)<<1)) + +/* + * Imported from the timer module + */ +extern U_LONG current_time; +extern struct event timerqueue[]; + +/* + * Imported from ntp_loopfilter module + */ +extern int fdpps; /* pps file descriptor */ + +/* + * Imported from ntpd module + */ +extern int debug; /* global debug flag */ + +/* + * OMEGA unit control structure + */ +struct omegaunit { + struct peer *peer; /* associated peer structure */ + struct refclockio io; /* given to the I/O handler */ + l_fp lastrec; /* last receive time */ + l_fp lastref; /* last timecode time */ + l_fp offset[NSTAMPS]; /* recent sample offsets */ + char lastcode[BMAX]; /* last timecode received */ + u_short station; /* which station we're locked to */ + u_short polled; /* Hand in a time sample? */ + U_LONG coderecv; /* timecodes received */ + u_char lencode; /* length of last timecode */ + U_LONG lasttime; /* last time clock heard from */ + 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 failure */ + u_char year; /* year of eternity */ + u_short day; /* day of year */ + u_char hour; /* hour of day */ + u_char minute; /* minute of hour */ + u_char second; /* seconds of minute */ + u_char leap; /* leap indicators */ + u_short msec; /* millisecond of second */ + u_char quality; /* quality char from last timecode */ + U_LONG yearstart; /* start of current year */ + /* + * Status tallies + */ + U_LONG polls; /* polls sent */ + U_LONG noreply; /* no replies to polls */ + 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 omegaunit *omegaunits[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 fudgefactor1[MAXUNITS]; +static l_fp fudgefactor2[MAXUNITS]; +static u_char stratumtouse[MAXUNITS]; +static u_char readonlyclockflag[MAXUNITS]; +static U_LONG refid[MAXUNITS]; + +/* + * Function prototypes + */ +static void omega_init P((void)); +static int omega_start P((u_int, struct peer *)); +static void omega_shutdown P((int)); +static void omega_report_event P((struct omegaunit *, int)); +static void omega_receive P((struct recvbuf *)); +static char omega_process P((struct omegaunit *, l_fp *, u_fp *)); +static void omega_poll P((int, struct peer *)); +static void omega_control P((u_int, struct refclockstat *, struct refclockstat *)); +static void omega_buginfo P((int, struct refclockbug *)); +static void omega_send P((struct omegaunit *, char *)); + +/* + * Transfer vector + */ +struct refclock refclock_omega = { + omega_start, omega_shutdown, omega_poll, + omega_control, omega_init, omega_buginfo, NOFLAGS +}; + +/* + * omega_init - initialize internal omega driver data + */ +static void +omega_init() +{ + register int i; + /* + * Just zero the data arrays + */ + memset((char *)omegaunits, 0, sizeof omegaunits); + memset((char *)unitinuse, 0, sizeof unitinuse); + + /* + * Initialize fudge factors to default. + */ + for (i = 0; i < MAXUNITS; i++) { + fudgefactor1[i].l_ui = 0; + fudgefactor1[i].l_uf = DEFFUDGETIME; + fudgefactor2[i].l_ui = 0; + fudgefactor2[i].l_uf = 0; + stratumtouse[i] = 0; + readonlyclockflag[i] = 0; + memcpy((char *)&refid[i], OMEGAREFID, 4); + } +} + + +/* + * omega_start - open the OMEGA devices and initialize data for processing + */ +static int +omega_start(unit, peer) + u_int unit; + struct peer *peer; +{ + register struct omegaunit *omega; + register int i; + int fd232; + char omegadev[20]; + + /* + * Check configuration info + */ + if (unit >= MAXUNITS) { + syslog(LOG_ERR,"omega_start: unit %d invalid", unit); + return 0; + } + if (unitinuse[unit]) { + syslog(LOG_ERR, "omega_start: unit %d in use", unit); + return 0; + } + + /* + * Open serial port + */ + (void) sprintf(omegadev, OMEGA232, unit); + fd232 = open(omegadev, O_RDWR, 0777); + if (fd232 == -1) { + syslog(LOG_ERR, "omega_start: open of %s: %m", omegadev); + return 0; + } + +#if defined(HAVE_SYSV_TTYS) + /* + * System V serial line parameters (termio interface) + * + */ + { struct termio ttyb; + if (ioctl(fd232, TCGETA, &ttyb) < 0) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TCGETA): %m", omegadev); + 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) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TCSETA): %m", omegadev); + goto screwed; + } + } +#endif /* HAVE_SYSV_TTYS */ +#if defined(HAVE_TERMIOS) + /* + * POSIX serial line parameters (termios interface) + * + * The OMEGACLK option provides timestamping at the driver level. + * It requires the tty_clk streams module. + * + * The OMEGAPPS 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 SunOS 4.1.1 or + * later. + */ + { struct termios ttyb, *ttyp; + + ttyp = &ttyb; + if (tcgetattr(fd232, ttyp) < 0) { + syslog(LOG_ERR, + "omega_start: tcgetattr(%s): %m", omegadev); + 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) { + syslog(LOG_ERR, + "omega_start: tcsetattr(%s): %m", omegadev); + goto screwed; + } + if (tcflush(fd232, TCIOFLUSH) < 0) { + syslog(LOG_ERR, + "omega_start: tcflush(%s): %m", omegadev); + goto screwed; + } + } +#endif /* HAVE_TERMIOS */ +#ifdef STREAM +#if defined(OMEGACLK) + if (ioctl(fd232, I_PUSH, "clk") < 0) + syslog(LOG_ERR, + "omega_start: ioctl(%s, I_PUSH, clk): %m", omegadev); + if (ioctl(fd232, CLK_SETSTR, "\n") < 0) + syslog(LOG_ERR, + "omega_start: ioctl(%s, CLK_SETSTR): %m", omegadev); +#endif /* OMEGACLK */ +#if defined(OMEGAPPS) + if (ioctl(fd232, I_PUSH, "ppsclock") < 0) + syslog(LOG_ERR, + "omega_start: ioctl(%s, I_PUSH, ppsclock): %m", omegadev); + else + fdpps = fd232; +#endif /* OMEGAPPS */ +#endif /* STREAM */ +#if defined(HAVE_BSD_TTYS) + /* + * 4.3bsd serial line parameters (sgttyb interface) + * + * The OMEGACLK option provides timestamping at the driver level. + * It requires the tty_clk line discipline and 4.3bsd or later. + */ + { struct sgttyb ttyb; +#if defined(OMEGACLK) + int ldisc = CLKLDISC; +#endif /* OMEGACLK */ + + if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TIOCGETP): %m", omegadev); + goto screwed; + } + ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; +#if defined(OMEGACLK) + 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 /* OMEGACLK */ + if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TIOCSETP): %m", omegadev); + goto screwed; + } +#if defined(OMEGACLK) + if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { + syslog(LOG_ERR, + "omega_start: ioctl(%s, TIOCSETD): %m",omegadev); + goto screwed; + } +#endif /* OMEGACLK */ + } +#endif /* HAVE_BSD_TTYS */ + + /* + * Allocate unit structure + */ + if (omegaunits[unit] != 0) { + omega = omegaunits[unit]; /* The one we want is okay */ + } else { + for (i = 0; i < MAXUNITS; i++) { + if (!unitinuse[i] && omegaunits[i] != 0) + break; + } + if (i < MAXUNITS) { + /* + * Reclaim this one + */ + omega = omegaunits[i]; + omegaunits[i] = 0; + } else { + omega = (struct omegaunit *) + emalloc(sizeof(struct omegaunit)); + } + } + memset((char *)omega, 0, sizeof(struct omegaunit)); + omegaunits[unit] = omega; + + /* + * Set up the structures + */ + omega->peer = peer; + omega->unit = (u_char)unit; + omega->timestarted = current_time; + omega->station = STATION_NONE; + + omega->io.clock_recv = omega_receive; + omega->io.srcclock = (caddr_t)omega; + omega->io.datalen = 0; + omega->io.fd = fd232; + if (!io_addclock(&omega->io)) { + goto screwed; + } + + /* + * All done. Initialize a few random peer variables, then + * return success. + */ + peer->precision = OMEGAPRECISION; + peer->rootdelay = 0; + peer->rootdispersion = 0; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + unitinuse[unit] = 1; + return 1; + + /* + * Something broke; abandon ship + */ +screwed: + (void) close(fd232); + return 0; +} + + +/* + * omega_shutdown - shut down a OMEGA clock + */ +static void +omega_shutdown(unit) + int unit; +{ + register struct omegaunit *omega; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "omega_shutdown: unit %d invalid", + unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "omega_shutdown: unit %d not in use", unit); + return; + } + + /* + * Tell the I/O module to turn us off. We're history. + */ + omega = omegaunits[unit]; + io_closeclock(&omega->io); + unitinuse[unit] = 0; +} + + +/* + * omega_report_event - note the occurance of an event + */ +static void +omega_report_event(omega, code) + struct omegaunit *omega; + int code; +{ + struct peer *peer; + + peer = omega->peer; + if (omega->status != (u_char)code) { + omega->status = (u_char)code; + if (code != CEVNT_NOMINAL) + omega->lastevent = (u_char)code; + syslog(LOG_INFO, + "omega clock %s event %x\n", ntoa(&peer->srcadr), code); + } +} + + +/* + * omega_receive - receive data from the serial interface on a + * Kinemetrics OM-DC OMEGA clock. + */ +static void +omega_receive(rbufp) + struct recvbuf *rbufp; +{ + register int i; + register struct omegaunit *omega; + register u_char *dpt; + register char *cp, *cpend; + register u_char *dpend; + l_fp tstmp; + u_fp dispersion, drift; + + /* + * Get the clock this applies to and a pointers to the data + */ + omega = (struct omegaunit *)rbufp->recv_srcclock; + dpt = (u_char *)&rbufp->recv_space; + +#ifndef PEDANTIC + /* + * The OM-DC outputs a timecode every second, but we only want + * a set of NSTAMPS timecodes when polled (every 64 seconds). + * Setting PEDANTIC causes a sanity check on every timecode. + */ + if (!omega->polled) + return; +#endif + + /* + * Edit timecode to remove control chars + */ + dpend = dpt + rbufp->recv_length; + cp = omega->lastcode; + cpend = omega->lastcode + BMAX - 1; + while (dpt < dpend && cp < cpend) { + if ((*cp = 0x7f & *dpt++) >= ' ') cp++; +#ifdef OMEGACLK + else if (*cp == '\r') { + if (dpend - dpt < 8) { + /* short timestamp */ + return; + } + if (!buftvtots(dpt,&omega->lastrec)) { + /* screwy timestamp */ + return; + } + dpt += 8; + } +#endif + } + *cp = '\0'; + omega->lencode = cp - omega->lastcode; + + if (omega->lencode == 0) + return; + else if (omega->lencode != LENOMEGA) { + omega->badformat++; + /* Sometimes get a lot of these, filling the log with noise */ + /* omega_report_event(omega, CEVNT_BADREPLY); */ + return; + } + +#ifndef OMEGACLK + omega->lastrec = rbufp->recv_time; +#endif + +#ifdef DEBUG + if (debug) + printf("omega: timecode %d %s\n", + omega->lencode, omega->lastcode); +#endif + + /* + * We get down to business, check the timecode format + * and decode its contents. + */ + cp = omega->lastcode; + omega->leap = 0; + /* + * Check timecode format. + */ + if (!isdigit(cp[0]) || /* day of year */ + !isdigit(cp[1]) || + !isdigit(cp[2]) || + cp[3] != ':' || /* <sp> */ + !isdigit(cp[4]) || /* hours */ + !isdigit(cp[5]) || + cp[6] != ':' || /* : separator */ + !isdigit(cp[7]) || /* minutes */ + !isdigit(cp[8]) || + cp[9] != ':' || /* : separator */ + !isdigit(cp[10]) || /* seconds */ + !isdigit(cp[11])) { + omega->badformat++; + omega_report_event(omega, CEVNT_BADREPLY); + return; + } + + /* + * Convert and check values. + */ + omega->year = 0; /* fake */ + omega->day = cp[0] - '0'; + omega->day = MULBY10(omega->day) + cp[1] - '0'; + omega->day = MULBY10(omega->day) + cp[2] - '0'; + omega->hour = MULBY10(cp[4] - '0') + cp[5] - '0'; + omega->minute = MULBY10(cp[7] - '0') + cp[8] - '0'; + omega->second = MULBY10(cp[10] - '0') + cp[11] - '0'; + omega->msec = 0; + + if (omega->day < 1 || omega->day > 366) { + omega->baddata++; + omega_report_event(omega, CEVNT_BADDATE); + return; + } + if (omega->hour > 23 || omega->minute > 59 || omega->second > 59) { + omega->baddata++; + omega_report_event(omega, CEVNT_BADTIME); + return; + } + + /* + * Check quality/station-id flag. The OM-DC should normally stay + * permanently locked to a station, and its time error should be less + * than 1 msec. If it loses lock for any reason, it makes a worst + * case drift estimate based on the internally stored stability figure + * for its reference oscillator. The stability figure can be adjusted + * by the user based on experience. The default value is 1E05, which + * is pretty bad - 2E07 is about right for the unit I have. + * + * The following is arbitrary, change it if you're offended: + * For errors less than 50 msec, just clear the station indicator. + * For errors greater than 50 msec, flag loss of sync and report a + * propagation problem. If the error is greater than 500 msec, + * something is dreadfully wrong - report a clock fault. + * + * In each case, we set a drift estimate which is used below as an + * estimate of measurement accuracy. + */ + omega->quality = cp[12]; + if (cp[12] == '>' || cp[12] == '?') { + /* Error 500 to 5000 msec */ + omega_report_event(omega, CEVNT_FAULT); + omega->leap = LEAP_NOTINSYNC; + omega->station = STATION_NONE; + drift = U_FP5000MS; + } else if (cp[12] == '#') { + /* Error 50 to 500 msec */ + omega_report_event(omega, CEVNT_PROP); + omega->leap = LEAP_NOTINSYNC; + omega->station = STATION_NONE; + drift = U_FP500MS; + } else if (cp[12] == '*') { + /* Error 5 to 50 msec */ + omega->lasttime = current_time; + omega->station = STATION_NONE; + drift = U_FP50MS; + } else if (cp[12] == '.') { + /* Error 1 to 5 msec */ + omega->lasttime = current_time; + omega->station = STATION_NONE; + drift = U_FP5MS; + } else if ('A' <= cp[12] && cp[12] <= 'H') { + /* Error less than 1 msec */ + omega->lasttime = current_time; + omega->station = cp[12] - 'A' + 1; + drift = 0; + } else { + omega->badformat++; + omega_report_event(omega, CEVNT_BADREPLY); + return; + } + +#ifdef PEDANTIC + /* If we haven't been polled, bail out. */ + if (!omega->polled) + return; +#endif + + /* + * Now, compute the reference time value. Use the heavy + * machinery for the seconds and the millisecond field for the + * fraction when present. + * + * this code does not yet know how to do the years + */ + tstmp = omega->lastrec; + if (!clocktime(omega->day, omega->hour, omega->minute, + omega->second, GMT, tstmp.l_ui, + &omega->yearstart, &omega->lastref.l_ui)) { + omega->baddata++; + omega_report_event(omega, CEVNT_BADTIME); + return; + } + MSUTOTSF(omega->msec, omega->lastref.l_uf); + + /* + * Adjust the read value by fudgefactor1 to correct RS232 delays. + */ + L_ADD(&omega->lastref, &fudgefactor1[omega->unit]); + + /* Carousel of NSTAMPS offsets. */ + i = omega->coderecv % NSTAMPS; + omega->offset[i] = omega->lastref; + L_SUB(&omega->offset[i], &tstmp); + omega->coderecv++; + + /* If we don't yet have a full set, return. */ + if (omega->coderecv < NSTAMPS) + return; + + /* + * Filter the samples, add the fudge factor and pass the + * offset and dispersion along. 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 the sample filter chokes because of excessive + * dispersion or whatever, get a new sample (omega->coderecv + * is still >= NSTAMPS) and try again. + */ + if (!omega_process(omega, &tstmp, &dispersion)) { + omega->baddata++; + omega_report_event(omega, CEVNT_BADTIME); + return; + } + + /* + * Add accumulated clock drift to the dispersion to get + * a (hopefully) meaningful measurement accuracy estimate. + */ + dispersion += drift; + refclock_receive(omega->peer, &tstmp, GMT, dispersion, + &omega->lastrec, &omega->lastrec, omega->leap); + + /* + * We have succeeded in answering the poll. If the clock + * is locked, we're nominal. + */ + omega->polled = 0; + omega->coderecv = 0; + if (omega->leap != LEAP_NOTINSYNC) + omega_report_event(omega, CEVNT_NOMINAL); +} + + +/* + * omega_send - time to send the clock a signal to cough up a time sample + */ +static void +omega_send(omega,cmd) + struct omegaunit *omega; + char *cmd; +{ + if (!readonlyclockflag[omega->unit]) { + /* + * Send a command to the clock. + */ + if (write(omega->io.fd, cmd, 1) != 1) { + syslog(LOG_ERR, "omega_send: unit %d: %m", omega->unit); + omega_report_event(omega, CEVNT_FAULT); + } + } +} + + +/* + * Compare two l_fp's, used with qsort() + */ +static int +omega_cmpl_fp(p1, p2) + register void *p1, *p2; +{ + + if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2)) + return (-1); + if (L_ISEQU((l_fp *)p1, (l_fp *)p2)) + return (0); + return (1); +} + + +/* + * omega_process - process a pile of samples from the clock + */ +static char +omega_process(omega, offset, dispersion) + struct omegaunit *omega; + l_fp *offset; + u_fp *dispersion; +{ + register int i, n; + register U_LONG med_ui, med_uf, tmp_ui, tmp_uf; + l_fp off[NSTAMPS]; + u_fp disp; + + /* Copy in offsets and sort into ascending order */ + for (i = 0; i < NSTAMPS; i++) + off[i] = omega->offset[i]; + qsort((char *)off, NSTAMPS, sizeof(l_fp), omega_cmpl_fp); + /* + * Reject the furthest from the median until NSKEEP samples remain + */ + i = 0; + n = NSTAMPS; + while ((n - i) > NSKEEP) { + tmp_ui = off[n-1].l_ui; + tmp_uf = off[n-1].l_uf; + med_ui = off[(n+i)/2].l_ui; + med_uf = off[(n+i)/2].l_uf; + M_SUB(tmp_ui, tmp_uf, med_ui, med_uf); + M_SUB(med_ui, med_uf, off[i].l_ui, off[i].l_uf); + if (M_ISHIS(med_ui, med_uf, tmp_ui, tmp_uf)) { + /* reject low end */ + i++; + } else { + /* reject high end */ + n--; + } + } + + /* + * Compute the dispersion based on the difference between the + * extremes of the remaining offsets. If this is greater than + * the allowed sample set dispersion, bail out. Otherwise, + * return the median offset and the dispersion. + */ + tmp_ui = off[n-1].l_ui; + tmp_uf = off[n-1].l_uf; + M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf); + disp = MFPTOFP(tmp_ui, tmp_uf); + if (disp > OMEGAMAXDISPERSE) + return 0; + *offset = off[(n+1)/2]; + *dispersion = disp; + return 1; +} + + +/* + * omega_poll - called by the transmit procedure + */ +static void +omega_poll(unit, peer) + int unit; + struct peer *peer; +{ + struct omegaunit *omega; + + /* + * 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 (unit >= MAXUNITS) { + syslog(LOG_ERR, "omega_poll: unit %d invalid", unit); + return; + } + if (!unitinuse[unit]) { + syslog(LOG_ERR, "omega_poll: unit %d not in use", unit); + return; + } + omega = omegaunits[unit]; + if ((current_time - omega->lasttime) > 150) { + omega->noreply++; + omega_report_event(omegaunits[unit], CEVNT_TIMEOUT); + } + + /* + * polled every 64 seconds. Ask OMEGA_RECEIVE to hand in a timestamp. + */ + omega->polled = 1; + omega->polls++; + /* + * Ensure the clock is running in the correct mode - on-second + * timestamps. + */ + omega_send(omega,"C"); +} + + +/* + * omega_control - set fudge factors, return statistics + */ +static void +omega_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct omegaunit *omega; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "omega_control: unit %d invalid", unit); + return; + } + + if (in != 0) { + if (in->haveflags & CLK_HAVETIME1) + fudgefactor1[unit] = in->fudgetime1; + if (in->haveflags & CLK_HAVETIME2) + fudgefactor2[unit] = in->fudgetime2; + if (in->haveflags & CLK_HAVEVAL1) + stratumtouse[unit] = (u_char)(in->fudgeval1); + if (in->haveflags & CLK_HAVEVAL2) + refid[unit] = in->fudgeval2; + if (in->haveflags & CLK_HAVEFLAG1) + readonlyclockflag[unit] = in->flags & CLK_FLAG1; + if (unitinuse[unit]) { + struct peer *peer; + + peer = omegaunits[unit]->peer; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; + } + } + + if (out != 0) { + out->type = REFCLK_OMEGA_TRUETIME; + out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 | + CLK_HAVEVAL2| CLK_HAVEFLAG1; + out->clockdesc = OMEGADESCRIPTION; + out->fudgetime1 = fudgefactor1[unit]; + out->fudgetime2 = fudgefactor2[unit]; + out->fudgeval1 = (LONG)stratumtouse[unit]; + out->fudgeval2 = refid[unit]; + out->flags = readonlyclockflag[unit]; + if (unitinuse[unit]) { + omega = omegaunits[unit]; + out->flags |= omega->station << 1; + out->lencode = omega->lencode; + out->lastcode = omega->lastcode; + out->timereset = current_time - omega->timestarted; + out->polls = omega->polls; + out->noresponse = omega->noreply; + out->badformat = omega->badformat; + out->baddata = omega->baddata; + out->lastevent = omega->lastevent; + out->currentstatus = omega->status; + } + } +} + + +/* + * omega_buginfo - return clock dependent debugging info + */ +static void +omega_buginfo(unit, bug) + int unit; + register struct refclockbug *bug; +{ + register struct omegaunit *omega; + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, "omega_buginfo: unit %d invalid", unit); + return; + } + + if (!unitinuse[unit]) + return; + omega = omegaunits[unit]; + + bug->nvalues = 11; + bug->ntimes = 5; + if (omega->lasttime != 0) + bug->values[0] = current_time - omega->lasttime; + else + bug->values[0] = 0; + bug->values[1] = (U_LONG)omega->reason; + bug->values[2] = (U_LONG)omega->year; + bug->values[3] = (U_LONG)omega->day; + bug->values[4] = (U_LONG)omega->hour; + bug->values[5] = (U_LONG)omega->minute; + bug->values[6] = (U_LONG)omega->second; + bug->values[7] = (U_LONG)omega->msec; + bug->values[8] = omega->noreply; + bug->values[9] = omega->yearstart; + bug->values[10] = omega->quality; + bug->stimes = 0x1c; + bug->times[0] = omega->lastref; + bug->times[1] = omega->lastrec; + bug->times[2] = omega->offset[0]; + bug->times[3] = omega->offset[1]; + bug->times[4] = omega->offset[2]; +} +#endif diff --git a/usr.sbin/xntpd/xntpd/refclock_old/refclock_parse.c b/usr.sbin/xntpd/xntpd/refclock_old/refclock_parse.c new file mode 100644 index 000000000000..0d95d18908a5 --- /dev/null +++ b/usr.sbin/xntpd/xntpd/refclock_old/refclock_parse.c @@ -0,0 +1,3605 @@ +#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) +/* + * /src/NTP/REPOSITORY/v3/xntpd/refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp + * + * refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp + * + * generic reference clock driver for receivers + * + * 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 (new - careful!) + * + * Copyright (c) 1989,1990,1991,1992,1993,1994 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * 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. + * + */ + +/* + * Defines: + * REFCLOCK && (PARSE||PARSEPPS) + * - enable this mess + * STREAM - allow for STREAMS modules + * ("parse", "ppsclocd", "ppsclock") + * PARSEPPS - provide PPS information to loopfilter (for + * backward compatibilty only) + * PPS - supply loopfilter with PPS samples (if configured) + * PPSPPS - notify loopfilter of PPS file descriptor + * + * FREEBSD_CONRAD - Make very cheap "Conrad DCF77 RS-232" gadget work + * with FreeBSD. + * TTY defines: + * HAVE_BSD_TTYS - currently unsupported + * HAVE_SYSV_TTYS - will use termio.h + * HAVE_TERMIOS - will use termios.h + * STREAM - will use streams and implies HAVE_TERMIOS + */ + +/* + * This driver currently provides the support for + * - Meinberg DCF77 receiver DCF77 PZF 535 (TCXO version) (DCF) + * - Meinberg DCF77 receiver DCF77 PZF 535 (OCXO version) (DCF) + * - Meinberg DCF77 receiver U/A 31 (DCF) + * - ELV DCF7000 (DCF) + * - Schmid clock (DCF) + * - Conrad DCF77 receiver module (DCF) + * - FAU DCF77 NTP receiver (TimeBrick) (DCF) + * - Meinberg GPS166 (GPS) + * - Trimble SV6 (GPS) + * + */ + +/* + * Meinberg receivers are connected via a 9600 baud serial line + * + * Receivers that do NOT support: + * - leap second indication + * DCF U/A 31 + * DCF PZF535 (stock version) + * + * so... + * - for PZF535 please ask for revision PZFUERL4.6 or higher + * (support for leap second and alternate antenna) + * + * The Meinberg GPS receiver also has a special NTP time stamp + * format. The firmware release is Uni-Erlangen. Only this + * firmware release is supported by xntp3. + * + * Meinberg generic receiver setup: + * output time code every second + * Baud rate 9600 7E2S + */ + +#include "ntpd.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_control.h" + +#include <stdio.h> +#include <ctype.h> +#include <time.h> + +#include <sys/errno.h> +#ifdef FREEBSD_CONRAD +#include <sys/ioctl.h> +#endif +extern int errno; + +#if !defined(STREAM) && !defined(HAVE_SYSV_TTYS) && !defined(HAVE_BSD_TTYS) && !defined(HAVE_TERMIOS) +/* #error NEED TO DEFINE ONE OF "STREAM" or "HAVE_SYSV_TTYS" */ +NEED TO DEFINE ONE OF "STREAM", "HAVE_SYSV_TTYS" or "HAVE_TERMIOS" +#endif + +#ifdef STREAM +#include <sys/stream.h> +#include <sys/stropts.h> +#ifndef HAVE_TERMIOS +#define HAVE_TERMIOS +#endif +#endif + +#ifdef HAVE_TERMIOS +#include <termios.h> +#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 +#include <termio.h> +#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 */ +CURRENTLY NO BSD TTY SUPPORT +#endif + +#if !defined(O_RDWR) /* XXX SOLARIS */ +#include <fcntl.h> +#endif /* !def(O_RDWR) */ + +#ifdef PPSPPS +#include <sys/ppsclock.h> +#endif + +#include "ntp_select.h" +#include "ntp_stdlib.h" + +#include "parse.h" + +#if !defined(NO_SCCSID) && !defined(lint) && !defined(__GNUC__) +static char rcsid[]="refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp"; +#endif + +/**=========================================================================== + ** external interface to xntp mechanism + **/ + +static void parse_init P((void)); +static int parse_start P((u_int, struct peer *)); +static void parse_shutdown P((int)); +static void parse_poll P((int, struct peer *)); +static void parse_control P((u_int, struct refclockstat *, struct refclockstat *)); + +#define parse_buginfo noentry + +struct refclock refclock_parse = { + parse_start, + parse_shutdown, + parse_poll, + parse_control, + parse_init, + parse_buginfo, + NOFLAGS +}; + +/* + * the unit field selects for one the prototype to be used (lower 4 bits) + * and for the other the clock type in case of different but similar + * receivers (bits 4-6) + * the most significant bit encodes PPS support + * when the most significant bit is set the pps telegrams will be used + * for controlling the local clock (ntp_loopfilter.c) + * receiver specific configration data is kept in the clockinfo field. + */ + +/* + * Definitions + */ +#define MAXUNITS 4 /* maximum number of "PARSE" units permitted */ +#define PARSEDEVICE "/dev/refclock-%d" /* device to open %d is unit number */ + +/**=========================================================================== + ** function vector for dynamically binding io handling mechanism + **/ + +typedef struct bind +{ + char *bd_description; /* name of type of binding */ + int (*bd_init)(); /* initialize */ + void (*bd_end)(); /* end */ + int (*bd_setcs)(); /* set character size */ + int (*bd_disable)(); /* disable */ + int (*bd_enable)(); /* enable */ + int (*bd_getfmt)(); /* get format */ + int (*bd_setfmt)(); /* setfmt */ + int (*bd_getstat)(); /* getstat */ + int (*bd_setstat)(); /* setstat */ + int (*bd_timecode)(); /* get time code */ + void (*bd_receive)(); /* receive operation */ + void (*bd_poll)(); /* poll 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_GETSTAT(_X_, _DCT_) (*(_X_)->binding->bd_getstat)(_X_, _DCT_) +#define PARSE_SETSTAT(_X_, _DCT_) (*(_X_)->binding->bd_setstat)(_X_, _DCT_) +#define PARSE_GETTIMECODE(_X_, _DCT_) (*(_X_)->binding->bd_timecode)(_X_, _DCT_) +#define PARSE_POLL(_X_) (*(_X_)->binding->bd_poll)(_X_) + +/* + * io modes + */ +#define PARSE_F_NOPOLLONLY 0x0001 /* always do async io (possible PPS support via PARSE) */ +#define PARSE_F_POLLONLY 0x0002 /* never do async io (no PPS support via PARSE) */ +#define PARSE_F_PPSPPS 0x0004 /* use loopfilter PPS code (CIOGETEV) */ +#define PARSE_F_PPSONSECOND 0x0008 /* PPS pulses are on second */ + +/**=========================================================================== + ** refclock instance data + **/ + +struct parseunit +{ + /* + * XNTP management + */ + struct peer *peer; /* backlink to peer structure - refclock inactive if 0 */ + int fd; /* device file descriptor */ + u_char unit; /* encoded unit/type/PPS */ + + /* + * XNTP io + */ + struct refclockio io; /* io system structure (used in PPS mode) */ + bind_t *binding; /* io handling binding */ + + /* + * parse state + */ + parse_t parseio; /* io handling structure (user level parsing) */ + + /* + * type specific parameters + */ + struct clockinfo *parse_type; /* link to clock description */ + + /* + * clock specific configuration + */ + l_fp basedelay; /* clock local phase offset */ + l_fp ppsdelay; /* clock local pps phase offset */ + + /* + * clock state handling/reporting + */ + u_char flags; /* flags (leap_control) */ + u_char status; /* current status */ + u_char lastevent; /* last not NORMAL status */ + U_LONG lastchange; /* time (xntp) when last state change accured */ + U_LONG statetime[CEVNT_MAX+1]; /* accumulated time of clock states */ + struct event stattimer; /* statistics timer */ + U_LONG polls; /* polls from NTP protocol machine */ + U_LONG noresponse; /* number of expected but not seen datagrams */ + U_LONG badformat; /* bad format (failed format conversions) */ + U_LONG baddata; /* usually bad receive length, bad format */ + + u_char pollonly; /* 1 for polling only (no PPS mode) */ + u_char pollneeddata; /* 1 for receive sample expected in PPS mode */ + U_LONG laststatus; /* last packet status (error indication) */ + u_short lastformat; /* last format used */ + U_LONG lastsync; /* time (xntp) when clock was last seen fully synchronized */ + U_LONG timestarted; /* time (xntp) when peer clock was instantiated */ + U_LONG nosynctime; /* time (xntp) when last nosync message was posted */ + U_LONG lastmissed; /* time (xntp) 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 data */ +}; + + +/**=========================================================================== + ** Clockinfo section all parameter for specific clock types + ** includes NTP paramaters, TTY parameters and IO handling parameters + **/ + +static void poll_dpoll P((struct parseunit *)); +static void poll_poll P((struct parseunit *)); +static int poll_init P((struct parseunit *)); +static void poll_end P((struct parseunit *)); + +typedef struct poll_info +{ + U_LONG rate; /* poll rate - once every "rate" seconds - 0 off */ + char * string; /* string to send for polling */ + U_LONG count; /* number of charcters in string */ +} poll_info_t; + +#define NO_FLAGS 0 +#define NO_POLL (void (*)())0 +#define NO_INIT (int (*)())0 +#define NO_END (void (*)())0 +#define NO_DATA (void *)0 +#define NO_FORMAT "" +#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 0x00000000 +#define NOCLOCK_BASEDELAY 0x00000000 +#define NOCLOCK_DESCRIPTION ((char *)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 NO_FORMAT +#define NOCLOCK_TYPE CTL_SST_TS_UNSPEC + +#define DCF_TYPE CTL_SST_TS_LF +#define GPS_TYPE CTL_SST_TS_UHF + +/* + * receiver specific constants + */ +#define MBG_CFLAG19200 (B19200|CS7|PARENB|CREAD|HUPCL) +#define MBG_CFLAG (B9600|CS7|PARENB|CREAD|HUPCL) +#define MBG_IFLAG (IGNBRK|IGNPAR|ISTRIP) +#define MBG_OFLAG 0 +#define MBG_LFLAG 0 +/* + * Meinberg DCF U/A 31 (AM) receiver + */ +#define DCFUA31_ROOTDELAY 0x00000D00 /* 50.78125ms */ +#define DCFUA31_BASEDELAY 0x02C00000 /* 10.7421875ms: 10 ms (+/- 3 ms) */ +#define DCFUA31_DESCRIPTION "Meinberg DCF U/A 31" +#define DCFUA31_MAXUNSYNC 60*30 /* only trust clock for 1/2 hour */ +#define DCFUA31_CFLAG MBG_CFLAG +#define DCFUA31_IFLAG MBG_IFLAG +#define DCFUA31_OFLAG MBG_OFLAG +#define DCFUA31_LFLAG MBG_LFLAG + +/* + * Meinberg DCF PZF535/TCXO (FM/PZF) receiver + */ +#define DCFPZF535_ROOTDELAY 0x00000034 /* 800us */ +#define DCFPZF535_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */ +#define DCFPZF535_DESCRIPTION "Meinberg DCF PZF 535/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_CFLAG MBG_CFLAG +#define DCFPZF535_IFLAG MBG_IFLAG +#define DCFPZF535_OFLAG MBG_OFLAG +#define DCFPZF535_LFLAG MBG_LFLAG + + +/* + * Meinberg DCF PZF535/OCXO receiver + */ +#define DCFPZF535OCXO_ROOTDELAY 0x00000034 /* 800us (max error * 10) */ +#define DCFPZF535OCXO_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */ +#define DCFPZF535OCXO_DESCRIPTION "Meinberg DCF PZF 535/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_CFLAG MBG_CFLAG +#define DCFPZF535OCXO_IFLAG MBG_IFLAG +#define DCFPZF535OCXO_OFLAG MBG_OFLAG +#define DCFPZF535OCXO_LFLAG MBG_LFLAG + +/* + * Meinberg GPS166 receiver + */ +#define GPS166_ROOTDELAY 0x00000000 /* nothing here */ +#define GPS166_BASEDELAY 0x00800000 /* XXX to be fixed ! 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */ +#define GPS166_DESCRIPTION "Meinberg GPS166 receiver" +#define GPS166_MAXUNSYNC 0 /* this clock is immediately lost */ +#define GPS166_CFLAG MBG_CFLAG +#define GPS166_IFLAG MBG_IFLAG +#define GPS166_OFLAG MBG_OFLAG +#define GPS166_LFLAG MBG_LFLAG +#define GPS166_POLL NO_POLL +#define GPS166_INIT NO_INIT +#define GPS166_END NO_END +#define GPS166_DATA NO_DATA +#define GPS166_ID GPS_ID +#define GPS166_FORMAT NO_FORMAT + +/* + * ELV DCF7000 Wallclock-Receiver/Switching Clock (Kit) + * + * This is really not the hottest clock - but before you have nothing ... + */ +#define DCF7000_ROOTDELAY 0x00000364 /* 13 ms */ +#define DCF7000_BASEDELAY 0x67AE0000 /* 405 ms - slow blow */ +#define DCF7000_DESCRIPTION "ELV DCF7000" +#define DCF7000_MAXUNSYNC (60*5) /* sorry - but it just was not build as a clock */ +#define DCF7000_CFLAG (B9600|CS8|CREAD|PARENB|PARODD|CLOCAL|HUPCL) +#define DCF7000_IFLAG (IGNBRK) +#define DCF7000_OFLAG 0 +#define DCF7000_LFLAG 0 + +/* + * 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 poll_end +#define WSDCF_DATA ((void *)(&wsdcf_pollinfo)) +#define WSDCF_ROOTDELAY 0X00004A3D /* ~ 290ms */ +#define WSDCF_BASEDELAY 0x028F5C29 /* ~ 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_CFLAG (B1200|CS8|CREAD|CLOCAL) +#define WSDCF_IFLAG 0 +#define WSDCF_OFLAG 0 +#define WSDCF_LFLAG 0 + +/* + * RAW DCF77 - input of DCF marks via RS232 - many variants + */ +#define RAWDCF_FLAGS PARSE_F_NOPOLLONLY +#define RAWDCF_ROOTDELAY 0x00000364 /* 13 ms */ +#define RAWDCF_FORMAT "RAW DCF77 Timecode" +#define RAWDCF_MAXUNSYNC (0) /* sorry - its a true receiver - no signal - no time */ + +#ifdef FREEBSD_CONRAD +#define RAWDCF_CFLAG (CS8|CREAD|CLOCAL) +#else +#define RAWDCF_CFLAG (B50|CS8|CREAD|CLOCAL) +#endif +#define RAWDCF_IFLAG 0 +#define RAWDCF_OFLAG 0 +#define RAWDCF_LFLAG 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 0x420C49B0 /* ~258 ms - Conrad receiver @ 50 Baud on a Sun */ +#define CONRAD_DESCRIPTION "RAW DCF77 CODE (Conrad DCF77 receiver module)" + +/* + * TimeBrick receiver + */ +#define TIMEBRICK_BASEDELAY 0x35C29000 /* ~210 ms - TimeBrick @ 50 Baud on a Sun */ +#define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)" + +/* + * Trimble SV6 GPS receiver + */ +#define TRIM_POLLRATE 0 /* only true direct polling */ +#define TRIM_POLLCMD ">QTM<" +#define TRIM_CMDSIZE 5 + +static poll_info_t trimble_pollinfo = { TRIM_POLLRATE, TRIM_POLLCMD, TRIM_CMDSIZE }; +static int trimble_init P((struct parseunit *)); + +#define TRIMBLESV6_CFLAG (B4800|CS8|CREAD) +#define TRIMBLESV6_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON) +#define TRIMBLESV6_OFLAG (OPOST|ONLCR) +#define TRIMBLESV6_LFLAG (ICANON|ECHOK) +#define TRIMBLESV6_FLAGS (PARSE_F_PPSPPS|PARSE_F_PPSONSECOND) +#define TRIMBLESV6_POLL poll_dpoll +#define TRIMBLESV6_INIT trimble_init +#define TRIMBLESV6_END poll_end +#define TRIMBLESV6_DATA ((void *)(&trimble_pollinfo)) +#define TRIMBLESV6_ID GPS_ID +#define TRIMBLESV6_FORMAT NO_FORMAT +#define TRIMBLESV6_ROOTDELAY 0x0 +#define TRIMBLESV6_BASEDELAY 0x0 +#define TRIMBLESV6_DESCRIPTION "Trimble SV6 GPS receiver" +#define TRIMBLESV6_MAXUNSYNC 0 +#define TRIMBLESV6_EOL '<' + +static struct clockinfo +{ + U_LONG cl_flags; /* operation flags (io modes) */ + void (*cl_poll)(); /* active poll routine */ + int (*cl_init)(); /* active poll init routine */ + void (*cl_end)(); /* active poll end routine */ + void *cl_data; /* local data area for "poll" mechanism */ + u_fp cl_rootdelay; /* rootdelay */ + U_LONG cl_basedelay; /* current offset - unsigned l_fp fractional part */ + U_LONG cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional part */ + char *cl_id; /* ID code (usually "DCF") */ + char *cl_description; /* device name */ + 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_cflag; /* terminal io flags */ + U_LONG cl_iflag; /* terminal io flags */ + U_LONG cl_oflag; /* terminal io flags */ + U_LONG cl_lflag; /* terminal io flags */ +} clockinfo[] = +{ /* 0. 0.0.128 - base offset for PPS support */ + { /* 127.127.8.<device> */ + NO_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + DCFPZF535_ROOTDELAY, + DCFPZF535_BASEDELAY, + NO_PPSDELAY, + DCF_P_ID, + DCFPZF535_DESCRIPTION, + NO_FORMAT, + DCF_TYPE, + DCFPZF535_MAXUNSYNC, + DCFPZF535_CFLAG, + DCFPZF535_IFLAG, + DCFPZF535_OFLAG, + DCFPZF535_LFLAG + }, + { /* 127.127.8.4+<device> */ + NO_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + DCFPZF535OCXO_ROOTDELAY, + DCFPZF535OCXO_BASEDELAY, + NO_PPSDELAY, + DCF_P_ID, + DCFPZF535OCXO_DESCRIPTION, + NO_FORMAT, + DCF_TYPE, + DCFPZF535OCXO_MAXUNSYNC, + DCFPZF535OCXO_CFLAG, + DCFPZF535OCXO_IFLAG, + DCFPZF535OCXO_OFLAG, + DCFPZF535OCXO_LFLAG + }, + { /* 127.127.8.8+<device> */ + NO_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + DCFUA31_ROOTDELAY, + DCFUA31_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + DCFUA31_DESCRIPTION, + NO_FORMAT, + DCF_TYPE, + DCFUA31_MAXUNSYNC, + DCFUA31_CFLAG, + DCFUA31_IFLAG, + DCFUA31_OFLAG, + DCFUA31_LFLAG + }, + { /* 127.127.8.12+<device> */ + NO_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + DCF7000_ROOTDELAY, + DCF7000_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + DCF7000_DESCRIPTION, + NO_FORMAT, + DCF_TYPE, + DCF7000_MAXUNSYNC, + DCF7000_CFLAG, + DCF7000_IFLAG, + DCF7000_OFLAG, + DCF7000_LFLAG + }, + { /* 127.127.8.16+<device> */ + NO_FLAGS, + WSDCF_POLL, + WSDCF_INIT, + WSDCF_END, + WSDCF_DATA, + WSDCF_ROOTDELAY, + WSDCF_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + WSDCF_DESCRIPTION, + WSDCF_FORMAT, + DCF_TYPE, + WSDCF_MAXUNSYNC, + WSDCF_CFLAG, + WSDCF_IFLAG, + WSDCF_OFLAG, + WSDCF_LFLAG + }, + { /* 127.127.8.20+<device> */ + RAWDCF_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + RAWDCF_ROOTDELAY, + CONRAD_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + CONRAD_DESCRIPTION, + RAWDCF_FORMAT, + DCF_TYPE, + RAWDCF_MAXUNSYNC, + RAWDCF_CFLAG, + RAWDCF_IFLAG, + RAWDCF_OFLAG, + RAWDCF_LFLAG + }, + { /* 127.127.8.24+<device> */ + RAWDCF_FLAGS, + NO_POLL, + NO_INIT, + NO_END, + NO_DATA, + RAWDCF_ROOTDELAY, + TIMEBRICK_BASEDELAY, + NO_PPSDELAY, + DCF_A_ID, + TIMEBRICK_DESCRIPTION, + RAWDCF_FORMAT, + DCF_TYPE, + RAWDCF_MAXUNSYNC, + RAWDCF_CFLAG, + RAWDCF_IFLAG, + RAWDCF_OFLAG, + RAWDCF_LFLAG + }, + { /* 127.127.8.28+<device> */ + NO_FLAGS, + GPS166_POLL, + GPS166_INIT, + GPS166_END, + GPS166_DATA, + GPS166_ROOTDELAY, + GPS166_BASEDELAY, + NO_PPSDELAY, + GPS166_ID, + GPS166_DESCRIPTION, + GPS166_FORMAT, + GPS_TYPE, + GPS166_MAXUNSYNC, + GPS166_CFLAG, + GPS166_IFLAG, + GPS166_OFLAG, + GPS166_LFLAG + }, + { /* 127.127.8.32+<device> */ + TRIMBLESV6_FLAGS, + TRIMBLESV6_POLL, + TRIMBLESV6_INIT, + TRIMBLESV6_END, + TRIMBLESV6_DATA, + TRIMBLESV6_ROOTDELAY, + TRIMBLESV6_BASEDELAY, + NO_PPSDELAY, + TRIMBLESV6_ID, + TRIMBLESV6_DESCRIPTION, + TRIMBLESV6_FORMAT, + GPS_TYPE, + TRIMBLESV6_MAXUNSYNC, + TRIMBLESV6_CFLAG, + TRIMBLESV6_IFLAG, + TRIMBLESV6_OFLAG, + TRIMBLESV6_LFLAG + } +}; + +static int ncltypes = sizeof(clockinfo) / sizeof(struct clockinfo); + +#define CL_REALTYPE(x) (((x) >> 2) & 0x1F) +#define CL_TYPE(x) ((CL_REALTYPE(x) >= ncltypes) ? ~0 : CL_REALTYPE(x)) +#define CL_PPS(x) ((x) & 0x80) +#define CL_UNIT(x) ((x) & 0x3) + +/* + * Other constant stuff + */ +#define PARSEHSREFID 0x7f7f08ff /* 127.127.8.255 refid for hi strata */ + +#define PARSENOSYNCREPEAT (10*60) /* mention uninitialized clocks all 10 minutes */ +#define PARSESTATISTICS (60*60) /* output state statistics every hour */ + +static struct parseunit *parseunits[MAXUNITS]; + +extern U_LONG current_time; +extern s_char sys_precision; +extern struct event timerqueue[]; +#ifdef PPSPPS +extern int fdpps; +#endif + +static int notice = 0; + +#define PARSE_STATETIME(parse, i) ((parse->status == 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 *)); + +/**=========================================================================== + ** 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_nop 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_getstat P((struct parseunit *, parsectl_t *)); +static int stream_setstat P((struct parseunit *, parsectl_t *)); +static int stream_timecode P((struct parseunit *, parsectl_t *)); +static void stream_receive P((struct recvbuf *)); +static void stream_poll P((struct parseunit *)); +#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_getstat P((struct parseunit *, parsectl_t *)); +static int local_setstat P((struct parseunit *, parsectl_t *)); +static int local_timecode P((struct parseunit *, parsectl_t *)); +static void local_receive P((struct recvbuf *)); +static void local_poll P((struct parseunit *)); + +static bind_t io_bindings[] = +{ +#ifdef STREAM + { + "parse STREAM", + stream_init, + stream_nop, + stream_setcs, + stream_disable, + stream_enable, + stream_getfmt, + stream_setfmt, + stream_getstat, + stream_setstat, + stream_timecode, + stream_receive, + stream_poll + }, + { + "ppsclock STREAM", + ppsclock_init, + local_end, + local_setcs, + local_nop, + local_nop, + local_getfmt, + local_setfmt, + local_getstat, + local_setstat, + local_timecode, + local_receive, + local_poll + }, +#endif + { + "normal", + local_init, + local_end, + local_setcs, + local_nop, + local_nop, + local_getfmt, + local_setfmt, + local_getstat, + local_setstat, + local_timecode, + local_receive, + local_poll + }, + { + (char *)0, + } +}; + +#ifdef STREAM +/*-------------------------------------------------- + * ppsclock STREAM init + */ +static int +ppsclock_init(parse) + struct parseunit *parse; +{ + /* + * now push the parse streams module + * it will ensure exclusive access to the device + */ + if (ioctl(parse->fd, I_PUSH, (caddr_t)"ppsclocd") == -1 && + ioctl(parse->fd, I_PUSH, (caddr_t)"ppsclock") == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: ppsclock_init: ioctl(fd, I_PUSH, \"ppsclock\"): %m", + CL_UNIT(parse->unit)); + return 0; + } + if (!local_init(parse)) + { + (void)ioctl(parse->fd, I_POP, (caddr_t)0); + return 0; + } + + parse->flags |= PARSE_PPSCLOCK; + return 1; +} + +/*-------------------------------------------------- + * parse STREAM init + */ +static int +stream_init(parse) + struct parseunit *parse; +{ + /* + * now push the parse streams module + * to test whether it is there (Oh boy - neat kernel interface) + */ + if (ioctl(parse->fd, I_PUSH, (caddr_t)"parse") == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CL_UNIT(parse->unit)); + return 0; + } + else + { + while(ioctl(parse->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->fd, I_PUSH, (caddr_t)"parse") == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CL_UNIT(parse->unit)); + return 0; + } + else + { + return 1; + } + } +} + + /*-------------------------------------------------- + * STREAM setcs + */ +static int +stream_setcs(parse, tcl) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_setcs: ioctl(fd, I_STR, PARSEIOC_SETCS): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM nop + */ +static void +stream_nop(parse) + struct parseunit *parse; +{ +} + +/*-------------------------------------------------- + * STREAM enable + */ +static int +stream_enable(parse) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_enable: ioctl(fd, I_STR, PARSEIOC_ENABLE): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM disable + */ +static int +stream_disable(parse) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_disable: ioctl(fd, I_STR, PARSEIOC_DISABLE): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM getfmt + */ +static int +stream_getfmt(parse, tcl) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: ioctl(fd, I_STR, PARSEIOC_GETFMT): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM setfmt + */ +static int +stream_setfmt(parse, tcl) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_setfmt: ioctl(fd, I_STR, PARSEIOC_SETFMT): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM getstat + */ +static int +stream_getstat(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + struct strioctl strioc; + + strioc.ic_cmd = PARSEIOC_GETSTAT; + strioc.ic_timout = 0; + strioc.ic_dp = (char *)tcl; + strioc.ic_len = sizeof (*tcl); + + if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_getstat: ioctl(fd, I_STR, PARSEIOC_GETSTAT): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM setstat + */ +static int +stream_setstat(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + struct strioctl strioc; + + strioc.ic_cmd = PARSEIOC_SETSTAT; + strioc.ic_timout = 0; + strioc.ic_dp = (char *)tcl; + strioc.ic_len = sizeof (*tcl); + + if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: stream_setstat: ioctl(fd, I_STR, PARSEIOC_SETSTAT): %m", CL_UNIT(parse->unit)); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM timecode + */ +static int +stream_timecode(parse, tcl) + 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->fd, I_STR, (caddr_t)&strioc) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_process: ioctl(fd, I_STR, PARSEIOC_TIMECODE): %m", CL_UNIT(parse->unit), parse->fd); + return 0; + } + return 1; +} + +/*-------------------------------------------------- + * STREAM receive + */ +static void +stream_receive(rbufp) + struct recvbuf *rbufp; +{ + struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock; + parsetime_t parsetime; + + if (rbufp->recv_length != sizeof(parsetime_t)) + { + syslog(LOG_ERR,"PARSE receiver #%d: parse_receive: bad size (got %d expected %d)", + CL_UNIT(parse->unit), rbufp->recv_length, sizeof(parsetime_t)); + parse->baddata++; + parse_event(parse, CEVNT_BADREPLY); + return; + } + memmove((caddr_t)&parsetime, + (caddr_t)&rbufp->recv_space, + sizeof(parsetime_t)); + + /* + * switch time stamp world - be sure to normalize small usec field + * errors. + */ + +#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)) \ + { \ + syslog(LOG_ERR,"parse: stream_receive: timestamp conversion error (buftvtots) (%s) (%d.%06d) ", (_Y_), (&(_X_))->tv.tv_sec, (&(_X_))->tv.tv_usec);\ + return; \ + } \ + else \ + { \ + (&(_X_))->fp = ts; \ + } \ + } + + if (PARSE_TIMECODE(parsetime.parse_state)) + { + cvt_ts(parsetime.parse_time, "parse_time"); + cvt_ts(parsetime.parse_stime, "parse_stime"); + } + + if (PARSE_PPS(parsetime.parse_state)) + cvt_ts(parsetime.parse_ptime, "parse_ptime"); + + parse_process(parse, &parsetime); +} + +/*-------------------------------------------------- + * STREAM poll + */ +static void +stream_poll(parse) + struct parseunit *parse; +{ + register int fd, i, rtc; + fd_set fdmask; + struct timeval timeout, starttime, curtime, selecttime; + parsetime_t parsetime; + + /* + * now we do the following: + * - read the first packet from the parse module (OLD !!!) + * - read the second packet from the parse module (fresh) + * - compute values for xntp + */ + + FD_ZERO(&fdmask); + fd = parse->fd; + FD_SET(fd, &fdmask); + timeout.tv_sec = 0; + timeout.tv_usec = 500000; /* 0.5 sec */ + + if (parse->parse_type->cl_poll) + { + parse->parse_type->cl_poll(parse); + } + + if (GETTIMEOFDAY(&starttime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + + selecttime = timeout; + + while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1) + { + /* no data from the radio clock */ + + if (rtc == -1) + { + if (errno == EINTR) + { + if (GETTIMEOFDAY(&curtime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec; + if (curtime.tv_usec < starttime.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec; + } + else + { + selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec; + } + + + if (timercmp(&selecttime, &timeout, >)) + { + /* + * elapsed real time passed timeout value - consider it timed out + */ + break; + } + + /* + * calculate residual timeout value + */ + selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec; + + if (selecttime.tv_usec > timeout.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec; + } + else + { + selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec; + } + + FD_SET(fd, &fdmask); + continue; + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device (select() error: %m)", CL_UNIT(parse->unit)); + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device", CL_UNIT(parse->unit)); + } + parse->noresponse++; + parse->lastmissed = current_time; + parse_event(parse, CEVNT_TIMEOUT); + + return; + } + + while (((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime))) + { + /* bad packet */ + if ( i == -1) + { + if (errno == EINTR) + { + continue; + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (read() error: %m)", CL_UNIT(parse->unit), i, sizeof(parsetime)); + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (got %d bytes - expected %d bytes)", CL_UNIT(parse->unit), i, sizeof(parsetime)); + } + parse->baddata++; + parse_event(parse, CEVNT_BADREPLY); + + return; + } + + if (parse->parse_type->cl_poll) + { + parse->parse_type->cl_poll(parse); + } + + timeout.tv_sec = 1; + timeout.tv_usec = 500000; /* 1.500 sec */ + FD_ZERO(&fdmask); + FD_SET(fd, &fdmask); + + if (GETTIMEOFDAY(&starttime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + + selecttime = timeout; + + while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1) + { + /* no data from the radio clock */ + + if (rtc == -1) + { + if (errno == EINTR) + { + if (GETTIMEOFDAY(&curtime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec; + if (curtime.tv_usec < starttime.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec; + } + else + { + selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec; + } + + + if (timercmp(&selecttime, &timeout, >)) + { + /* + * elapsed real time passed timeout value - consider it timed out + */ + break; + } + + /* + * calculate residual timeout value + */ + selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec; + + if (selecttime.tv_usec > timeout.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec; + } + else + { + selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec; + } + + FD_SET(fd, &fdmask); + continue; + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device (select() error: %m)", CL_UNIT(parse->unit)); + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device", CL_UNIT(parse->unit)); + } + + /* + * we will return here iff we got a good old sample as this would + * be misinterpreted. bad samples are passed on to be logged into the + * state statistics + */ + if ((parsetime.parse_status & CVT_MASK) == CVT_OK) + { + parse->noresponse++; + parse->lastmissed = current_time; + parse_event(parse, CEVNT_TIMEOUT); + return; + } + } + + /* + * we get here either by a possible read() (rtc == 1 - while assertion) + * or by a timeout or a system call error. when a read() is possible we + * get the new data, otherwise we stick with the old + */ + if ((rtc == 1) && ((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime))) + { + /* bad packet */ + if ( i== -1) + { + syslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (read() error: %m)", CL_UNIT(parse->unit), i, sizeof(parsetime)); + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (got %d bytes - expected %d bytes)", CL_UNIT(parse->unit), i, sizeof(parsetime)); + } + parse->baddata++; + parse_event(parse, CEVNT_BADREPLY); + + return; + } + + /* + * process what we got + */ + parse_process(parse, &parsetime); +} +#endif + +/*-------------------------------------------------- + * local init + */ +static int +local_init(parse) + struct parseunit *parse; +{ + return parse_ioinit(&parse->parseio); +} + +/*-------------------------------------------------- + * local end + */ +static void +local_end(parse) + struct parseunit *parse; +{ + parse_ioend(&parse->parseio); +} + + +/*-------------------------------------------------- + * local nop + */ +static int +local_nop(parse) + struct parseunit *parse; +{ + return 1; +} + +/*-------------------------------------------------- + * local setcs + */ +static int +local_setcs(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_setcs(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local getfmt + */ +static int +local_getfmt(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_getfmt(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local setfmt + */ +static int +local_setfmt(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_setfmt(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local getstat + */ +static int +local_getstat(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_getstat(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local setstat + */ +static int +local_setstat(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_setstat(tcl, &parse->parseio); +} + +/*-------------------------------------------------- + * local timecode + */ +static int +local_timecode(parse, tcl) + struct parseunit *parse; + parsectl_t *tcl; +{ + return parse_timecode(tcl, &parse->parseio); +} + + +/*-------------------------------------------------- + * local receive + */ +static void +local_receive(rbufp) + struct recvbuf *rbufp; +{ + struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock; + register int count; + register char *s; +#ifdef FREEBSD_CONRAD + struct timeval foo; +#endif + + /* + * eat all characters, parsing then and feeding complete samples + */ + count = rbufp->recv_length; + s = rbufp->recv_buffer; +#ifdef FREEBSD_CONRAD + ioctl(parse->fd,TIOCTIMESTAMP,&foo); + TVTOTS(&foo, &rbufp->recv_time); + rbufp->recv_time.l_uf += TS_ROUNDBIT; + rbufp->recv_time.l_ui += JAN_1970; + rbufp->recv_time.l_uf &= TS_MASK; +#endif + + while (count--) + { + if (parse_ioread(&parse->parseio, *s++, &rbufp->recv_time)) + { + /* + * got something good to eat + */ +#ifdef PPSPPS + if (!PARSE_PPS(parse->parseio.parse_dtime.parse_state) && + (parse->flags & PARSE_PPSCLOCK)) + { + l_fp ts; + struct ppsclockev ev; + + if (ioctl(parse->fd, CIOGETEV, (caddr_t)&ev) == 0) + { + if (ev.serial != parse->ppsserial) + { + /* + * add PPS time stamp if available via ppsclock module + * and not supplied already. + */ + if (!buftvtots((const char *)&ev.tv, &ts)) + { + syslog(LOG_ERR,"parse: local_receive: timestamp conversion error (buftvtots) (ppsclockev.tv)"); + } + else + { + parse->parseio.parse_dtime.parse_ptime.fp = ts; + parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS; + } + } + parse->ppsserial = ev.serial; + } + } +#endif + parse_process(parse, &parse->parseio.parse_dtime); + parse_iodone(&parse->parseio); + } + } +} + +/*-------------------------------------------------- + * local poll + */ +static void +local_poll(parse) + struct parseunit *parse; +{ + register int fd, i, rtc; + fd_set fdmask; + struct timeval timeout, starttime, curtime, selecttime; + static struct timeval null_time = { 0, 0}; + timestamp_t ts; + + FD_ZERO(&fdmask); + fd = parse->fd; + FD_SET(fd, &fdmask); + timeout.tv_sec = 1; + timeout.tv_usec = 500000; /* 1.5 sec */ + + if (parse->parse_type->cl_poll) + { + parse->parse_type->cl_poll(parse); + } + + if (GETTIMEOFDAY(&starttime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + + selecttime = timeout; + + do + { + while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1) + { + /* no data from the radio clock */ + + if (rtc == -1) + { + if (errno == EINTR) + { + if (GETTIMEOFDAY(&curtime, 0L) == -1) + { + syslog(LOG_ERR,"gettimeofday failed: %m"); + exit(1); + } + selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec; + if (curtime.tv_usec < starttime.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec; + } + else + { + selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec; + } + + + if (!timercmp(&selecttime, &timeout, >)) + { + /* + * calculate residual timeout value + */ + selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec; + + if (selecttime.tv_usec > timeout.tv_usec) + { + selecttime.tv_sec -= 1; + selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec; + } + else + { + selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec; + } + + FD_SET(fd, &fdmask); + continue; + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data from device (select() error: %m)", CL_UNIT(parse->unit)); + } + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: no data from device", CL_UNIT(parse->unit)); + } + + parse->noresponse++; + parse->lastmissed = current_time; + parse_event(parse, CEVNT_TIMEOUT); + + return; + } + + /* + * at least 1 character is available - gobble everthing up that is available + */ + do + { + char inbuf[256]; + + register char *s = inbuf; + + rtc = i = read(fd, inbuf, sizeof(inbuf)); + + get_systime(&ts.fp); + + while (i-- > 0) + { + if (parse_ioread(&parse->parseio, *s++, ts)) + { + /* + * got something good to eat + */ + parse_process(parse, &parse->parseio.parse_dtime); + parse_iodone(&parse->parseio); + /* + * done if no more characters are available + */ + FD_SET(fd, &fdmask); + if ((i == 0) && + (select(fd + 1, &fdmask, 0, 0, &null_time) == 0)) + return; + } + } + FD_SET(fd, &fdmask); + } while ((rtc = select(fd + 1, &fdmask, 0, 0, &null_time)) == 1); + FD_SET(fd, &fdmask); + } while (1); +} + +/*-------------------------------------------------- + * init_iobinding - find and initialize lower layers + */ +static bind_t * +init_iobinding(parse) + struct parseunit *parse; +{ + register 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(state, buffer) + unsigned LONG state; + char *buffer; +{ + static struct bits + { + unsigned LONG bit; + 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 + { + unsigned LONG bit; + 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 & state) + { + if (buffer[0]) + strcat(buffer, "; "); + strcat(buffer, flagstrings[i].name); + } + i++; + } + + if (state & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION)) + { + register 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 & state) + { + 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(state, buffer) + unsigned LONG state; + char *buffer; +{ + static struct bits + { + unsigned LONG bit; + 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" }, + { 0 } + }; + int i; + + *buffer = '\0'; + + i = 0; + while (flagstrings[i].bit) + { + if (flagstrings[i].bit & state) + { + if (buffer[0]) + strcat(buffer, "; "); + strcat(buffer, flagstrings[i].name); + } + i++; + } + + return buffer; +} + +/*-------------------------------------------------- + * convert a clock status flag field to a string + */ +static char * +clockstatus(state) + unsigned LONG state; +{ + static char buffer[20]; + static struct status + { + unsigned LONG value; + 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" }, + { ~0 } + }; + int i; + + i = 0; + while (flagstrings[i].value != ~0) + { + if (flagstrings[i].value == state) + { + return flagstrings[i].name; + } + i++; + } + + sprintf(buffer, "unknown #%d", state); + + return buffer; +} + +/*-------------------------------------------------- + * mkascii - make a printable ascii string + * assumes (unless defined better) 7-bit ASCII + */ +#ifndef isprint +#define isprint(_X_) (((_X_) > 0x1F) && ((_X_) < 0x7F)) +#endif + +static char * +mkascii(buffer, blen, src, srclen) + register char *buffer; + register LONG blen; + register char *src; + register LONG srclen; +{ + register char *b = buffer; + register 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 ((*src != '\\') && isprint(*src)) + { /* printables 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; + } + 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; +} + + +/*-------------------------------------------------- + * l_mktime - make representation of a relative time + */ +static char * +l_mktime(delta) + unsigned LONG delta; +{ + unsigned LONG tmp, m, s; + static char buffer[40]; + + buffer[0] = '\0'; + + if ((tmp = delta / (60*60*24)) != 0) + { + sprintf(buffer, "%dd+", tmp); + delta -= tmp * 60*60*24; + } + + s = delta % 60; + delta /= 60; + m = delta % 60; + delta /= 60; + + sprintf(buffer+strlen(buffer), "%02d:%02d:%02d", + delta, m, s); + + return buffer; +} + + +/*-------------------------------------------------- + * parse_statistics - list summary of clock states + */ +static void +parse_statistics(parse) + register struct parseunit *parse; +{ + register int i; + + syslog(LOG_INFO, "PARSE receiver #%d: running time: %s", + CL_UNIT(parse->unit), + l_mktime(current_time - parse->timestarted)); + + syslog(LOG_INFO, "PARSE receiver #%d: current status: %s", + CL_UNIT(parse->unit), + clockstatus(parse->status)); + + for (i = 0; i <= CEVNT_MAX; i++) + { + register unsigned LONG stime; + register unsigned LONG percent, div = current_time - parse->timestarted; + + percent = stime = PARSE_STATETIME(parse, i); + + while (((unsigned LONG)(~0) / 10000) < percent) + { + percent /= 10; + div /= 10; + } + + if (div) + percent = (percent * 10000) / div; + else + percent = 10000; + + if (stime) + syslog(LOG_INFO, "PARSE receiver #%d: state %18s: %13s (%3d.%02d%%)", + CL_UNIT(parse->unit), + clockstatus(i), + l_mktime(stime), + percent / 100, percent % 100); + } +} + +/*-------------------------------------------------- + * cparse_statistics - wrapper for statistics call + */ +static void +cparse_statistics(peer) + register struct peer *peer; +{ + register struct parseunit *parse = (struct parseunit *)peer; + + parse_statistics(parse); + parse->stattimer.event_time = current_time + PARSESTATISTICS; + TIMER_ENQUEUE(timerqueue, &parse->stattimer); +} + +/**=========================================================================== + ** xntp interface routines + **/ + +/*-------------------------------------------------- + * parse_init - initialize internal parse driver data + */ +static void +parse_init() +{ + memset((caddr_t)parseunits, 0, sizeof parseunits); +} + + +/*-------------------------------------------------- + * parse_shutdown - shut down a PARSE clock + */ +static void +parse_shutdown(unit) + int unit; +{ + register struct parseunit *parse; + + unit = CL_UNIT(unit); + + if (unit >= MAXUNITS) { + syslog(LOG_ERR, + "PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit invalid (max %d)", + unit,MAXUNITS); + return; + } + + parse = parseunits[unit]; + + if (parse && !parse->peer) { + syslog(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); + TIMER_DEQUEUE(&parse->stattimer); + +#if PPSPPS + { + /* + * kill possible PPS association + */ + if (fdpps == parse->fd) + fdpps = -1; + } +#endif + + 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. + */ + if (!parse->pollonly) + io_closeclock(&parse->io); + else + (void) close(parse->fd); + + syslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" removed", + CL_UNIT(parse->unit), parse->parse_type->cl_description); + + parse->peer = (struct peer *)0; /* unused now */ +} + +/*-------------------------------------------------- + * parse_start - open the PARSE devices and initialize data for processing + */ +static int +parse_start(sysunit, peer) + u_int sysunit; + struct peer *peer; +{ + u_int unit; + int fd232, i; +#ifdef HAVE_TERMIOS + struct termios tm; /* NEEDED FOR A LONG TIME ! */ +#endif +#ifdef HAVE_SYSV_TTYS + struct termio tm; /* NEEDED FOR A LONG TIME ! */ +#endif + struct parseunit * parse; + char parsedev[sizeof(PARSEDEVICE)+20]; + parsectl_t tmp_ctl; + u_int type; + + type = CL_TYPE(sysunit); + unit = CL_UNIT(sysunit); + + if (unit >= MAXUNITS) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit number invalid (max %d)", + unit, MAXUNITS-1); + return 0; + } + + if ((type == ~0) || (clockinfo[type].cl_description == (char *)0)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unsupported clock type %d (max %d)", + unit, CL_REALTYPE(sysunit), ncltypes-1); + return 0; + } + + if (parseunits[unit] && parseunits[unit]->peer) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit in use", unit); + 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, 0777); + if (fd232 == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: open of %s failed: %m", unit, parsedev); + return 0; + } + + /* + * 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 (parseunits[unit]) + { + parse = parseunits[unit]; /* The one we want is okay - and free */ + } + else + { + for (i = 0; i < MAXUNITS; i++) + { + if (parseunits[i] && !parseunits[i]->peer) + break; + } + if (i < MAXUNITS) + { + /* + * Reclaim this one + */ + parse = parseunits[i]; + parseunits[i] = (struct parseunit *)0; + } + else + { + parse = (struct parseunit *) + emalloc(sizeof(struct parseunit)); + } + } + + memset((char *)parse, 0, sizeof(struct parseunit)); + parseunits[unit] = parse; + + /* + * Set up the structures + */ + parse->unit = (u_char)sysunit; + parse->timestarted = current_time; + parse->lastchange = current_time; + /* + * we want to filter input for the sake of + * getting an impression on dispersion + * also we like to average the median range + */ + parse->flags = PARSE_STAT_FILTER|PARSE_STAT_AVG; + parse->pollneeddata = 0; + parse->pollonly = 1; /* go for default polling mode */ + parse->lastformat = ~0; /* assume no format known */ + parse->status = CEVNT_TIMEOUT; /* expect the worst */ + parse->laststatus = ~0; /* be sure to mark initial status change */ + parse->nosynctime = 0; /* assume clock reasonable */ + parse->lastmissed = 0; /* assume got everything */ + parse->ppsserial = 0; + parse->localdata = (void *)0; + + parse->parse_type = &clockinfo[type]; + + parse->basedelay.l_ui = 0; /* we can only pre-configure delays less than 1 second */ + parse->basedelay.l_uf = parse->parse_type->cl_basedelay; + + parse->ppsdelay.l_ui = 0; /* we can only pre-configure delays less than 1 second */ + parse->ppsdelay.l_uf = parse->parse_type->cl_ppsdelay; + + peer->rootdelay = parse->parse_type->cl_rootdelay; + peer->sstclktype = parse->parse_type->cl_type; + peer->precision = sys_precision; + peer->stratum = STRATUM_REFCLOCK; + if (peer->stratum <= 1) + memmove((char *)&peer->refid, parse->parse_type->cl_id, 4); + else + peer->refid = htonl(PARSEHSREFID); + + parse->fd = fd232; + + parse->peer = peer; /* marks it also as busy */ + + parse->binding = init_iobinding(parse); + + if (parse->binding == (bind_t *)0) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: io sub system initialisation failed."); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; /* well, ok - special initialisation broke */ + } + + /* + * configure terminal line + */ + if (TTY_GETATTR(fd232, &tm) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcgetattr(%d, &tm): %m", unit, fd232); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; + } + else + { +#ifndef _PC_VDISABLE + memset((char *)tm.c_cc, 0, sizeof(tm.c_cc)); +#else + int disablec; + errno = 0; /* pathconf can deliver -1 without changing errno ! */ + + disablec = fpathconf(parse->fd, _PC_VDISABLE); + if (disablec == -1 && errno) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: fpathconf(fd, _PC_VDISABLE): %m", CL_UNIT(parse->unit)); + memset((char *)tm.c_cc, 0, sizeof(tm.c_cc)); /* best guess */ + } + else + if (disablec != -1) + memset((char *)tm.c_cc, disablec, sizeof(tm.c_cc)); +#endif + + tm.c_cflag = clockinfo[type].cl_cflag; + tm.c_iflag = clockinfo[type].cl_iflag; + tm.c_oflag = clockinfo[type].cl_oflag; + tm.c_lflag = clockinfo[type].cl_lflag; +#ifdef FREEBSD_CONRAD + tm.c_ispeed = 50; + tm.c_ospeed = 50; +#endif + if (TTY_SETATTR(fd232, &tm) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcsetattr(%d, &tm): %m", unit, fd232); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; + } + } + + /* + * 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 (tm.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)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setcs() FAILED.", unit); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; /* well, ok - special initialisation broke */ + } + +#ifdef FREEBSD_CONRAD + { + int i,j; + struct timeval tv; + ioctl(parse->fd,TIOCTIMESTAMP,&tv); + j = TIOCM_RTS; + i = ioctl(fd232, TIOCMBIC, &j); + if (i < 0) { + syslog(LOG_ERR, + "PARSE receiver #%d: lowrts_poll: failed to lower RTS: %m", + CL_UNIT(parse->unit)); + } + } +#endif + + 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)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setfmt() FAILED.", unit); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; /* well, ok - special initialisation broke */ + } + +#ifdef TCFLSH + /* + * get rid of all IO accumulated so far + */ + { +#ifndef TCIOFLUSH +#define TCIOFLUSH 2 +#endif + int flshcmd = TCIOFLUSH; + + (void) ioctl(parse->fd, TCFLSH, (caddr_t)&flshcmd); + } +#endif + + tmp_ctl.parsestatus.flags = parse->flags & PARSE_STAT_FLAGS; + + if (!PARSE_SETSTAT(parse, &tmp_ctl)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setstat() FAILED.", unit); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; /* well, ok - special initialisation broke */ + } + + /* + * try to do any special initializations + */ + if (parse->parse_type->cl_init) + { + if (parse->parse_type->cl_init(parse)) + { + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; /* well, ok - special initialisation broke */ + } + } + + if (!(parse->parse_type->cl_flags & PARSE_F_POLLONLY) && + (CL_PPS(parse->unit) || (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY))) + { + /* + * Insert in async io device list. + */ + parse->io.clock_recv = parse->binding->bd_receive; /* pick correct receive routine */ + parse->io.srcclock = (caddr_t)parse; + parse->io.datalen = 0; + parse->io.fd = parse->fd; /* replicated, but what the heck */ + if (!io_addclock(&parse->io)) + { + if (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY) + { + syslog(LOG_ERR, + "PARSE receiver #%d: parse_start: addclock %s fails (ABORT - clock type requires async io)", CL_UNIT(parse->unit), parsedev); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; + } + else + { + syslog(LOG_ERR, + "PARSE receiver #%d: parse_start: addclock %s fails (switching to polling mode)", CL_UNIT(parse->unit), parsedev); + } + } + else + { + parse->pollonly = 0; /* + * update at receipt of time_stamp - also + * supports PPS processing + */ + } + } + +#ifdef PPSPPS + if (parse->pollonly || (parse->parse_type->cl_flags & PARSE_F_PPSPPS)) + { + if (fdpps == -1) + { + fdpps = parse->fd; + if (!PARSE_DISABLE(parse)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_disable() FAILED", CL_UNIT(parse->unit)); + parse_shutdown(parse->unit); /* let our cleaning staff do the work */ + return 0; + } + } + else + { + syslog(LOG_NOTICE, "PARSE receiver #%d: parse_start: loopfilter PPS already active - no PPS via CIOGETEV", CL_UNIT(parse->unit)); + } + } +#endif + + /* + * wind up statistics timer + */ + parse->stattimer.peer = (struct peer *)parse; /* we know better, but what the heck */ + parse->stattimer.event_handler = cparse_statistics; + parse->stattimer.event_time = current_time + PARSESTATISTICS; + TIMER_ENQUEUE(timerqueue, &parse->stattimer); + + /* + * get out Copyright information once + */ + if (!notice) + { + syslog(LOG_INFO, "NTP PARSE support: Copyright (c) 1989-1993, Frank Kardel"); + notice = 1; + } + + /* + * print out configuration + */ + syslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" (device %s) added", + CL_UNIT(parse->unit), + parse->parse_type->cl_description, parsedev); + + syslog(LOG_INFO, "PARSE receiver #%d: Stratum %d, %sPPS support, trust time %s, precision %d", + CL_UNIT(parse->unit), + parse->peer->stratum, (parse->pollonly || !CL_PPS(parse->unit)) ? "no " : "", + l_mktime(parse->parse_type->cl_maxunsync), parse->peer->precision); + + syslog(LOG_INFO, "PARSE receiver #%d: rootdelay %s s, phaseadjust %s s, %s IO handling", + CL_UNIT(parse->unit), + ufptoa(parse->parse_type->cl_rootdelay, 6), + lfptoa(&parse->basedelay, 8), + parse->binding->bd_description); + + syslog(LOG_INFO, "PARSE receiver #%d: Format recognition: %s", CL_UNIT(parse->unit), + !(*parse->parse_type->cl_format) ? "<AUTOMATIC>" : parse->parse_type->cl_format); + +#ifdef PPSPPS + syslog(LOG_INFO, "PARSE receiver #%d: %sCD PPS support", + CL_UNIT(parse->unit), + (fdpps == parse->fd) ? "" : "NO "); +#endif + + return 1; +} + +/*-------------------------------------------------- + * parse_poll - called by the transmit procedure + */ +static void +parse_poll(unit, peer) + int unit; + struct peer *peer; +{ + register struct parseunit *parse; + + unit = CL_UNIT(unit); + + if (unit >= MAXUNITS) + { + syslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit invalid", + unit); + return; + } + + parse = parseunits[unit]; + + if (!parse->peer) + { + syslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit unused", + unit); + return; + } + + if (peer != parse->peer) + { + syslog(LOG_ERR, + "PARSE receiver #%d: poll: INTERNAL: peer incorrect", + unit); + return; + } + + /* + * Update clock stat counters + */ + parse->polls++; + + /* + * in PPS mode we just mark that we want the next sample + * for the clock filter + */ + if (!parse->pollonly) + { + if (parse->pollneeddata) + { + /* + * bad news - didn't get a response last time + */ + parse->noresponse++; + parse->lastmissed = current_time; + parse_event(parse, CEVNT_TIMEOUT); + + syslog(LOG_WARNING, "PARSE receiver #%d: no data from device within poll interval", CL_UNIT(parse->unit)); + } + parse->pollneeddata = 1; + if (parse->parse_type->cl_poll) + { + parse->parse_type->cl_poll(parse); + } + return; + } + + /* + * the following code is only executed only when polling is used + */ + + PARSE_POLL(parse); +} + +/*-------------------------------------------------- + * parse_leap - called when a leap second occurs + */ + +static void +parse_leap() +{ + /* + * PARSE encodes the LEAP correction direction. + * For timecodes that do not pass on the leap correction direction + * the default PARSEB_LEAPADD must be used. It may then be modified + * with a fudge flag (flag2). + */ +} + + +/*-------------------------------------------------- + * parse_control - set fudge factors, return statistics + */ +static void +parse_control(unit, in, out) + u_int unit; + struct refclockstat *in; + struct refclockstat *out; +{ + register struct parseunit *parse; + parsectl_t tmpctl; + unsigned LONG type; + static char outstatus[400]; /* status output buffer */ + + type = CL_TYPE(unit); + unit = CL_UNIT(unit); + + if (out) + { + out->lencode = 0; + out->lastcode = 0; + out->polls = out->noresponse = 0; + out->badformat = out->baddata = 0; + out->timereset = 0; + out->currentstatus = out->lastevent = CEVNT_NOMINAL; + out->kv_list = (struct ctl_var *)0; + } + + if (unit >= MAXUNITS) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (max %d)", + unit, MAXUNITS-1); + return; + } + + parse = parseunits[unit]; + + if (!parse || !parse->peer) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (UNIT INACTIVE)", + unit); + return; + } + + if (in) + { + if (in->haveflags & CLK_HAVETIME1) + parse->basedelay = in->fudgetime1; + + if (in->haveflags & CLK_HAVETIME2) + { + parse->ppsdelay = in->fudgetime2; + } + + if (in->haveflags & CLK_HAVEVAL1) + { + parse->peer->stratum = (u_char)(in->fudgeval1 & 0xf); + if (parse->peer->stratum <= 1) + memmove((char *)&parse->peer->refid, + parse->parse_type->cl_id, + 4); + else + parse->peer->refid = htonl(PARSEHSREFID); + } + + /* + * NOT USED - yet + * + if (in->haveflags & CLK_HAVEVAL2) + { + } + */ + if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4)) + { + parse->flags = (in->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4)) | + (parse->flags & ~PARSE_STAT_FLAGS); + } + + if (in->haveflags & (CLK_HAVEVAL2|CLK_HAVETIME2|CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4)) + { + parsectl_t tmpctl; + tmpctl.parsestatus.flags = parse->flags & PARSE_STAT_FLAGS; + + if (!PARSE_SETSTAT(parse, &tmpctl)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_setstat() FAILED", unit); + } + } + } + + if (out) + { + register unsigned LONG sum = 0; + register char *t, *tt; + register struct tm *tm; + register short utcoff; + register char sign; + register int i; + time_t tim; + + outstatus[0] = '\0'; + + out->haveflags = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3; + out->clockdesc = parse->parse_type->cl_description; + + out->fudgetime1 = parse->basedelay; + + out->fudgetime2 = parse->ppsdelay; + + out->fudgeval1 = (LONG)parse->peer->stratum; + + out->fudgeval2 = 0; + + out->flags = parse->flags & PARSE_STAT_FLAGS; + + out->type = REFCLK_PARSE; + + /* + * 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, 40, 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\"", prettydate(&parse->time.parse_ptime.fp)); + } + + /* + * all this for just finding out the +-xxxx part (there are always + * new and changing fields in the standards 8-(). + * + * but we do it for the human user... + */ + tim = parse->time.parse_time.fp.l_ui - JAN_1970; + tm = gmtime(&tim); + utcoff = tm->tm_hour * 60 + tm->tm_min; + tm = localtime(&tim); + utcoff = tm->tm_hour * 60 + tm->tm_min - utcoff + 12 * 60; + utcoff += 24 * 60; + utcoff %= 24 * 60; + utcoff -= 12 * 60; + if (utcoff < 0) + { + utcoff = -utcoff; + sign = '-'; + } + else + { + sign = '+'; + } + + 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 + { + strcpy(tt, prettydate(&parse->time.parse_time.fp)); + t = tt + strlen(tt); + + sprintf(t, " (%c%02d%02d)\"", sign, utcoff / 60, utcoff % 60); + } + + if (!PARSE_GETTIMECODE(parse, &tmpctl)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit); + } + else + { + tt = add_var(&out->kv_list, 128, 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), sizeof(outstatus)- strlen(outstatus) - 1, + tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1); + + parse->badformat += tmpctl.parsegettc.parse_badformat; + } + + tmpctl.parseformat.parse_format = tmpctl.parsegettc.parse_format; + + if (!PARSE_GETFMT(parse, &tmpctl)) + { + syslog (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 + */ + + tt = add_var(&out->kv_list, 200, RO|DEF); + strcpy(tt, "refclock_states=\""); + tt += strlen(tt); + + for (i = 0; i <= CEVNT_MAX; i++) + { + register unsigned LONG stime; + register unsigned LONG div = current_time - parse->timestarted; + register unsigned LONG percent; + + percent = stime = PARSE_STATETIME(parse, i); + + while (((unsigned LONG)(~0) / 10000) < percent) + { + percent /= 10; + div /= 10; + } + + if (div) + percent = (percent * 10000) / div; + else + percent = 10000; + + if (stime) + { + sprintf(tt, "%s%s%s: %s (%d.%02d%%)", + sum ? "; " : "", + (parse->status == i) ? "*" : "", + clockstatus(i), + l_mktime(stime), + percent / 100, percent % 100); + sum += stime; + tt += strlen(tt); + } + } + + 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=\"refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp\""); + + out->lencode = strlen(outstatus); + out->lastcode = outstatus; + out->timereset = parse->timestarted; + out->polls = parse->polls; + out->noresponse = parse->noresponse; + out->badformat = parse->badformat; + out->baddata = parse->baddata; + out->lastevent = parse->lastevent; + out->currentstatus = parse->status; + } +} + +/**=========================================================================== + ** processing routines + **/ + +/*-------------------------------------------------- + * event handling - note that nominal events will also be posted + */ +static void +parse_event(parse, event) + struct parseunit *parse; + int event; +{ + if (parse->status != (u_char) event) + { + parse->statetime[parse->status] += current_time - parse->lastchange; + parse->lastchange = current_time; + + parse->status = (u_char)event; + if (event != CEVNT_NOMINAL) + parse->lastevent = parse->status; + + report_event(EVNT_PEERCLOCK, parse->peer); + } +} + +/*-------------------------------------------------- + * process a PARSE time sample + */ +static void +parse_process(parse, parsetime) + struct parseunit *parse; + parsetime_t *parsetime; +{ + unsigned char leap; + struct timeval usecdisp; + l_fp off, rectime, reftime, dispersion; + + /* + * check for changes in conversion status + * (only one for each new status !) + */ + if (parse->laststatus != parsetime->parse_status) + { + char buffer[200]; + + syslog(LOG_WARNING, "PARSE receiver #%d: conversion status \"%s\"", + CL_UNIT(parse->unit), 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)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_process: parse_timecode() FAILED", CL_UNIT(parse->unit)); + } + else + { + syslog(LOG_WARNING, "PARSE receiver #%d: FAILED TIMECODE: \"%s\"", + CL_UNIT(parse->unit), mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1)); + parse->badformat += tmpctl.parsegettc.parse_badformat; + } + } + + parse->laststatus = parsetime->parse_status; + } + + /* + * 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: + break; /* well, still waiting - timeout is handled at higher levels */ + + case CVT_FAIL: + parse->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)) + { + syslog(LOG_ERR, "PARSE receiver #%d: parse_getfmt() FAILED", CL_UNIT(parse->unit)); + } + else + { + syslog(LOG_INFO, "PARSE receiver #%d: new packet format \"%s\"", + CL_UNIT(parse->unit), 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); + + syslog(LOG_INFO,"PARSE receiver #%d: STATE CHANGE: %s -> %s", + CL_UNIT(parse->unit), 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 U/A 31 the lost synchronisation ist 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); + if (parse->nosynctime) + { + /* + * repeated POWERUP/NOSYNC state - look whether + * the message should be repeated + */ + if (current_time - parse->nosynctime > PARSENOSYNCREPEAT) + { + syslog(LOG_ERR,"PARSE receiver #%d: *STILL* NOT SYNCHRONIZED (POWERUP or no PZF signal)", + CL_UNIT(parse->unit)); + parse->nosynctime = current_time; + } + } + else + { + syslog(LOG_ERR,"PARSE receiver #%d: NOT SYNCHRONIZED", + CL_UNIT(parse->unit)); + parse->nosynctime = current_time; + } + } + 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 U/A 31 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 + */ + parse->nosynctime = 0; /* current state is better than worst state */ + + if (PARSE_SYNC(parsetime->parse_state)) + { + /* + * currently completely synchronized - best possible state + */ + parse->lastsync = current_time; + /* + * log OK status + */ + parse_event(parse, CEVNT_NOMINAL); + } + else + { + /* + * we have had some problems receiving the time code + */ + parse_event(parse, CEVNT_PROP); + } + } + + if (PARSE_TIMECODE(parsetime->parse_state)) + { + l_fp offset; + + /* + * calculate time offset including systematic delays + * off = PARSE-timestamp + propagation delay - kernel time stamp + */ + offset = parse->basedelay; + + off = parsetime->parse_time.fp; + + reftime = off; + + L_ADD(&off, &offset); + rectime = off; /* this makes org time and xmt time somewhat artificial */ + + L_SUB(&off, &parsetime->parse_stime.fp); + + if ((parse->flags & PARSE_STAT_FILTER) && + (off.l_i > -60) && + (off.l_i < 60)) /* take usec error only if within +- 60 secs */ + { + struct timeval usecerror; + /* + * offset is already calculated + */ + usecerror.tv_sec = parsetime->parse_usecerror / 1000000; + usecerror.tv_usec = parsetime->parse_usecerror % 1000000; + + sTVTOTS(&usecerror, &off); + L_ADD(&off, &offset); + } + } + + if (PARSE_PPS(parsetime->parse_state) && CL_PPS(parse->unit)) + { + l_fp offset; + + /* + * we have a PPS signal - much better than the RS232 stuff (we hope) + */ + offset = parsetime->parse_ptime.fp; + + L_ADD(&offset, &parse->ppsdelay); + + 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)) + { + /* + * RS232 offsets within [-0.5..0.5[ - take PPS offsets + */ + + if (parse->parse_type->cl_flags & PARSE_F_PPSONSECOND) + { + reftime = off = offset; + rectime = offset; + /* + * 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 + */ + off = parsetime->parse_time.fp; + + rectime = reftime = off; /* take reference time - fake rectime */ + + L_SUB(&off, &offset); /* true offset */ + } + } + /* + * take RS232 offset when PPS when out of bounds + */ + } + else + { + /* + * Well, no time code to guide us - assume on second pulse + * and pray, that we are within [-0.5..0.5[ + */ + reftime = off = offset; + rectime = offset; + /* + * 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 ... + */ + return; + } + } + + +#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) || defined(PARSEPPS) + if (CL_PPS(parse->unit) && !parse->pollonly && PARSE_SYNC(parsetime->parse_state)) + { + /* + * only provide PPS information when clock + * is in sync + * thus PHASE and EPOCH are correct and PPS is not + * done via the CIOGETEV loopfilter mechanism + */ +#ifdef PPSPPS + if (fdpps != parse->fd) +#endif + (void) pps_sample(&off); + } +#endif /* PPS || PPSCLK || PPSPPS || PARSEPPS */ + + /* + * ready, unless the machine wants a sample + */ + if (!parse->pollonly && !parse->pollneeddata) + return; + + parse->pollneeddata = 0; + + if (PARSE_PPS(parsetime->parse_state)) + { + L_CLR(&dispersion); + } + else + { + /* + * convert usec dispersion into NTP TS world + */ + + usecdisp.tv_sec = parsetime->parse_usecdisp / 1000000; + usecdisp.tv_usec = parsetime->parse_usecdisp % 1000000; + + TVTOTS(&usecdisp, &dispersion); + } + + /* + * 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)) + { + 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). + */ + leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND; + } + else + if (PARSE_LEAPDEL(parsetime->parse_state)) + { + leap = LEAP_DELSECOND; + } + else + { + leap = LEAP_NOWARNING; + } + } + + refclock_receive(parse->peer, &off, 0, LFPTOFP(&dispersion), &reftime, &rectime, leap); +} + +/**=========================================================================== + ** clock polling support + **/ + +struct poll_timer +{ + struct event timer; /* we'd like to poll a a higher rate than 1/64s */ +}; + +typedef struct poll_timer poll_timer_t; + +/*-------------------------------------------------- + * direct poll routine + */ +static void +poll_dpoll(parse) + struct parseunit *parse; +{ + register int rtc; + register char *ps = ((poll_info_t *)parse->parse_type->cl_data)->string; + register int ct = ((poll_info_t *)parse->parse_type->cl_data)->count; + + rtc = write(parse->fd, ps, ct); + if (rtc < 0) + { + syslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd to clock: %m", CL_UNIT(parse->unit)); + } + else + if (rtc != ct) + { + syslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd incomplete (%d of %d bytes sent)", CL_UNIT(parse->unit), rtc, ct); + } +} + +/*-------------------------------------------------- + * periodic poll routine + */ +static void +poll_poll(parse) + struct parseunit *parse; +{ + register poll_timer_t *pt = (poll_timer_t *)parse->localdata; + + poll_dpoll(parse); + + if (pt != (poll_timer_t *)0) + { + pt->timer.event_time = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate; + TIMER_ENQUEUE(timerqueue, &pt->timer); + } +} + +/*-------------------------------------------------- + * init routine - setup timer + */ +static int +poll_init(parse) + struct parseunit *parse; +{ + register poll_timer_t *pt; + + if (((poll_info_t *)parse->parse_type->cl_data)->rate) + { + parse->localdata = (void *)malloc(sizeof(poll_timer_t)); + memset((char *)parse->localdata, 0, sizeof(poll_timer_t)); + + pt = (poll_timer_t *)parse->localdata; + + pt->timer.peer = (struct peer *)parse; /* well, only we know what it is */ + pt->timer.event_handler = poll_poll; + poll_poll(parse); + } + else + { + parse->localdata = (void *)0; + } + + return 0; +} + +/*-------------------------------------------------- + * end routine - clean up timer + */ +static void +poll_end(parse) + struct parseunit *parse; +{ + if (parse->localdata != (void *)0) + { + TIMER_DEQUEUE(&((poll_timer_t *)parse->localdata)->timer); + free((char *)parse->localdata); + parse->localdata = (void *)0; + } +} + +/**=========================================================================== + ** special code for special clocks + **/ + +/*-------------------------------------------------- + * trimble init routine - setup EOL and then do poll_init. + */ +static int +trimble_init(parse) + struct parseunit *parse; +{ +#ifdef HAVE_TERMIOS + struct termios tm; +#endif +#ifdef HAVE_SYSV_TTYS + struct termio tm; +#endif + /* + * configure terminal line for trimble receiver + */ + if (TTY_GETATTR(parse->fd, &tm) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: trimble_init: tcgetattr(fd, &tm): %m", CL_UNIT(parse->unit)); + return 0; + } + else + { + tm.c_cc[VEOL] = TRIMBLESV6_EOL; + + if (TTY_SETATTR(parse->fd, &tm) == -1) + { + syslog(LOG_ERR, "PARSE receiver #%d: trimble_init: tcsetattr(fd, &tm): %m", CL_UNIT(parse->unit)); + return 0; + } + } + return poll_init(parse); +} +#endif /* defined(REFCLOCK) && defined(PARSE) */ + +/* + * History: + * + * refclock_parse.c,v + * Revision 3.53 1994/03/25 13:07:39 kardel + * fixed offset calculation for large (>4 Min) offsets + * + * Revision 3.52 1994/03/03 09:58:00 kardel + * stick -kv in cvs is no fun + * + * Revision 3.49 1994/02/20 13:26:00 kardel + * rcs id cleanup + * + * Revision 3.48 1994/02/20 13:04:56 kardel + * parse add/delete second support + * + * Revision 3.47 1994/02/02 17:44:30 kardel + * rcs ids fixed + * + * Revision 3.45 1994/01/25 19:06:27 kardel + * 94/01/23 reconcilation + * + * Revision 3.44 1994/01/25 17:32:23 kardel + * settable extended variables + * + * Revision 3.43 1994/01/23 16:28:39 kardel + * HAVE_TERMIOS introduced + * + * Revision 3.42 1994/01/22 11:35:04 kardel + * added HAVE_TERMIOS + * + * Revision 3.41 1993/11/27 18:44:37 kardel + * can't trust GPS166 on unsync + * + * Revision 3.40 1993/11/21 18:03:36 kardel + * useless declaration deleted + * + * Revision 3.39 1993/11/21 15:30:15 kardel + * static funcitions may be declared only at outer level + * + * Revision 3.38 1993/11/15 21:26:49 kardel + * conditional define comments fixed + * + * Revision 3.37 1993/11/11 11:20:49 kardel + * declaration fixes + * + * Revision 3.36 1993/11/10 12:17:14 kardel + * #ifdef glitch + * + * Revision 3.35 1993/11/01 21:15:06 kardel + * comments updated + * + * Revision 3.34 1993/11/01 20:01:08 kardel + * parse Solaris support (initial version) + * + * Revision 3.33 1993/10/30 09:44:58 kardel + * conditional compilation flag cleanup + * + * Revision 3.32 1993/10/22 14:28:43 kardel + * Oct. 22nd 1993 reconcilation + * + * Revision 3.31 1993/10/10 21:19:10 kardel + * compilation cleanup - (minimal porting tests) + * + * Revision 3.30 1993/10/09 21:44:35 kardel + * syslog strings fixed + * + * Revision 3.29 1993/10/09 14:40:15 kardel + * default precision setting fixed + * + * Revision 3.28 1993/10/08 14:48:22 kardel + * Changed offset determination logic: + * Take the PPS offset if it is available and the time + * code offset is within [-0.5..0.5[, otherwise stick + * to the time code offset + * + * Revision 3.27 1993/10/08 00:53:17 kardel + * announce also simulated PPS via CIOGETEV in ntpq cl + * + * Revision 3.26 1993/10/07 23:29:35 kardel + * trimble fixes + * + * Revision 3.25 1993/10/06 21:13:35 kardel + * test reversed (CIOGETEV support) + * + * Revision 3.24 1993/10/03 20:18:26 kardel + * Well, values > 999999 in the usec field from uniqtime() timestamps + * can prove harmful. + * + * Revision 3.23 1993/10/03 19:49:54 kardel + * buftvtots where failing on uninitialized time stamps + * + * Revision 3.22 1993/10/03 19:11:09 kardel + * restructured I/O handling + * + * Revision 3.21 1993/09/29 11:30:18 kardel + * special init for trimble to set EOL + * + * Revision 3.20 1993/09/27 22:46:28 kardel + * preserve module stack if I_PUSH parse fails + * + * Revision 3.19 1993/09/27 21:10:11 kardel + * wrong structure member + * + * Revision 3.18 1993/09/27 13:05:06 kardel + * Trimble is true polling only + * + * Revision 3.17 1993/09/27 12:47:10 kardel + * poll string support generalized + * + * Revision 3.16 1993/09/26 23:40:56 kardel + * new parse driver logic + * + * Revision 3.15 1993/09/24 15:00:51 kardel + * Sep 23rd distribution... + * + * Revision 3.14 1993/09/22 18:21:15 kardel + * support ppsclock streams module (-DSTREAM -DPPSPPS -DPARSEPPS -UPARSESTREAM) + * + * Revision 3.13 1993/09/05 15:38:33 kardel + * not every cpp understands #error... + * + * Revision 3.12 1993/09/02 20:04:19 kardel + * TTY cleanup + * + * Revision 3.11 1993/09/01 21:48:47 kardel + * conditional cleanup + * + * Revision 3.10 1993/09/01 11:32:45 kardel + * assuming HAVE_POSIX_TTYS when STREAM defined + * + * Revision 3.9 1993/08/31 22:31:46 kardel + * SINIX-M SysVR4 integration + * + * Revision 3.8 1993/08/27 00:29:50 kardel + * compilation cleanup + * + * Revision 3.7 1993/08/24 22:27:30 kardel + * cleaned up AUTOCONF DCF77 mess 8-) - wasn't too bad + * + * Revision 3.6 1993/08/24 21:36:23 kardel + * casting and ifdefs + * + * Revision 3.5 1993/07/09 23:36:59 kardel + * HAVE_POSIX_TTYS used to produce errors 8-( - BSD driver support still lacking + * + * Revision 3.4 1993/07/09 12:42:29 kardel + * RAW DCF now officially released + * + * Revision 3.3 1993/07/09 11:50:37 kardel + * running GPS also on 960 to be able to switch GPS/DCF77 + * + * Revision 3.2 1993/07/09 11:37:34 kardel + * Initial restructured version + GPS support + * + * Revision 3.1 1993/07/06 10:01:07 kardel + * DCF77 driver goes generic... + * + */ diff --git a/usr.sbin/xntpd/xntpd/refclock_omega.c b/usr.sbin/xntpd/xntpd/refclock_omega.c index 73be84d3859b..3d4e4b91d920 100644 --- a/usr.sbin/xntpd/xntpd/refclock_omega.c +++ b/usr.sbin/xntpd/xntpd/refclock_omega.c @@ -83,12 +83,10 @@ #define OMEGAMAXDISPERSE (FP_SECOND/32) /* max allowed sample dispersion */ #define OMEGAPRECISION (-10) /* precision assumed (about 1 ms) */ #define OMEGAREFID "VLF\0" /* reference id */ -#define OMEGAHSREFID 0x7f7f0b0a /* 127.127.11.10 refid hi strata */ #define LENOMEGA 13 /* length of standard response */ #define GMT 0 /* hour offset from Greenwich */ #define NSTAMPS 9 /* samples collected when polled */ #define NSKEEP 5 /* samples to keep after discards */ -#define BMAX 50 /* timecode buffer length */ /* * The OM-DC puts out the start bit of the <CR> on the second, but @@ -167,7 +165,7 @@ struct omegaunit { u_char leap; /* leap indicators */ u_short msec; /* millisecond of second */ u_char quality; /* quality char from last timecode */ - U_LONG yearstart; /* start of current year */ + u_long yearstart; /* start of current year */ /* * Status tallies */ @@ -193,18 +191,19 @@ static l_fp fudgefactor1[MAXUNITS]; static l_fp fudgefactor2[MAXUNITS]; static u_char stratumtouse[MAXUNITS]; static u_char readonlyclockflag[MAXUNITS]; +static U_LONG refid[MAXUNITS]; /* * Function prototypes */ static void omega_init P((void)); -static int omega_start P((u_int, struct peer *)); -static void omega_shutdown P((int)); +static int omega_start P((int, struct peer *)); +static void omega_shutdown P((int, struct peer *)); static void omega_report_event P((struct omegaunit *, int)); static void omega_receive P((struct recvbuf *)); static char omega_process P((struct omegaunit *, l_fp *, u_fp *)); static void omega_poll P((int, struct peer *)); -static void omega_control P((u_int, struct refclockstat *, struct refclockstat *)); +static void omega_control P((int, struct refclockstat *, struct refclockstat *)); static void omega_buginfo P((int, struct refclockbug *)); static void omega_send P((struct omegaunit *, char *)); @@ -239,6 +238,7 @@ omega_init() fudgefactor2[i].l_uf = 0; stratumtouse[i] = 0; readonlyclockflag[i] = 0; + memcpy((char *)&refid[i], OMEGAREFID, 4); } } @@ -248,7 +248,7 @@ omega_init() */ static int omega_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { register struct omegaunit *omega; @@ -443,10 +443,7 @@ omega_start(unit, peer) peer->rootdelay = 0; peer->rootdispersion = 0; peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, OMEGAREFID, 4); - else - peer->refid = htonl(OMEGAHSREFID); + peer->refid = refid[unit]; unitinuse[unit] = 1; return 1; @@ -463,8 +460,9 @@ screwed: * omega_shutdown - shut down a OMEGA clock */ static void -omega_shutdown(unit) +omega_shutdown(unit, peer) int unit; + struct peer *peer; { register struct omegaunit *omega; @@ -898,7 +896,7 @@ omega_poll(unit, peer) */ static void omega_control(unit, in, out) - u_int unit; + int unit; struct refclockstat *in; struct refclockstat *out; { @@ -914,41 +912,30 @@ omega_control(unit, in, out) fudgefactor1[unit] = in->fudgetime1; if (in->haveflags & CLK_HAVETIME2) fudgefactor2[unit] = in->fudgetime2; - 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 - */ - omega = omegaunits[unit]; - peer = omega->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, - OMEGAREFID, 4); - else - peer->refid = htonl(OMEGAHSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG1) { + if (in->haveflags & CLK_HAVEVAL1) + stratumtouse[unit] = (u_char)(in->fudgeval1); + if (in->haveflags & CLK_HAVEVAL2) + refid[unit] = in->fudgeval2; + if (in->haveflags & CLK_HAVEFLAG1) readonlyclockflag[unit] = in->flags & CLK_FLAG1; + if (unitinuse[unit]) { + struct peer *peer; + + peer = omegaunits[unit]->peer; + peer->stratum = stratumtouse[unit]; + peer->refid = refid[unit]; } } if (out != 0) { out->type = REFCLK_OMEGA_TRUETIME; - out->haveflags - = CLK_HAVETIME1|CLK_HAVETIME2| - CLK_HAVEVAL1|CLK_HAVEVAL2| - CLK_HAVEFLAG1|CLK_HAVEFLAG2; + out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 | + CLK_HAVEVAL2| CLK_HAVEFLAG1; out->clockdesc = OMEGADESCRIPTION; out->fudgetime1 = fudgefactor1[unit]; out->fudgetime2 = fudgefactor2[unit]; out->fudgeval1 = (LONG)stratumtouse[unit]; - out->fudgeval2 = 0; + out->fudgeval2 = refid[unit]; out->flags = readonlyclockflag[unit]; if (unitinuse[unit]) { omega = omegaunits[unit]; @@ -962,13 +949,6 @@ omega_control(unit, in, out) out->baddata = omega->baddata; out->lastevent = omega->lastevent; out->currentstatus = omega->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; } } } diff --git a/usr.sbin/xntpd/xntpd/refclock_parse.c b/usr.sbin/xntpd/xntpd/refclock_parse.c index 0d95d18908a5..7ef54ab60ab9 100644 --- a/usr.sbin/xntpd/xntpd/refclock_parse.c +++ b/usr.sbin/xntpd/xntpd/refclock_parse.c @@ -49,7 +49,7 @@ * - Conrad DCF77 receiver module (DCF) * - FAU DCF77 NTP receiver (TimeBrick) (DCF) * - Meinberg GPS166 (GPS) - * - Trimble SV6 (GPS) + * - Trimble SV6 (TSIP and TAIP protocol) (GPS) * */ @@ -474,31 +474,67 @@ static poll_info_t wsdcf_pollinfo = { WS_POLLRATE, WS_POLLCMD, WS_CMDSIZE }; #define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)" /* - * Trimble SV6 GPS receiver + * Trimble SV6 GPS receivers (TAIP and TSIP protocols) */ +#define ETX 0x03 +#define DLE 0x10 + #define TRIM_POLLRATE 0 /* only true direct polling */ -#define TRIM_POLLCMD ">QTM<" -#define TRIM_CMDSIZE 5 - -static poll_info_t trimble_pollinfo = { TRIM_POLLRATE, TRIM_POLLCMD, TRIM_CMDSIZE }; -static int trimble_init P((struct parseunit *)); - -#define TRIMBLESV6_CFLAG (B4800|CS8|CREAD) -#define TRIMBLESV6_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON) -#define TRIMBLESV6_OFLAG (OPOST|ONLCR) -#define TRIMBLESV6_LFLAG (ICANON|ECHOK) -#define TRIMBLESV6_FLAGS (PARSE_F_PPSPPS|PARSE_F_PPSONSECOND) -#define TRIMBLESV6_POLL poll_dpoll -#define TRIMBLESV6_INIT trimble_init -#define TRIMBLESV6_END poll_end -#define TRIMBLESV6_DATA ((void *)(&trimble_pollinfo)) -#define TRIMBLESV6_ID GPS_ID -#define TRIMBLESV6_FORMAT NO_FORMAT -#define TRIMBLESV6_ROOTDELAY 0x0 -#define TRIMBLESV6_BASEDELAY 0x0 -#define TRIMBLESV6_DESCRIPTION "Trimble SV6 GPS receiver" -#define TRIMBLESV6_MAXUNSYNC 0 -#define TRIMBLESV6_EOL '<' + +#define TRIM_TAIPPOLLCMD ">QTM<" +#define TRIM_TAIPCMDSIZE 5 +static poll_info_t trimbletaip_pollinfo = { TRIM_POLLRATE, TRIM_TAIPPOLLCMD, TRIM_TAIPCMDSIZE }; +static int trimbletaip_init P((struct parseunit *)); + +/* 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 *)); + +#define TRIMBLETAIP_CFLAG (B4800|CS8|CREAD) +#define TRIMBLETAIP_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON) +#define TRIMBLETAIP_OFLAG (OPOST|ONLCR) +#define TRIMBLETAIP_LFLAG (ICANON|ECHOK) +#define TRIMBLETSIP_CFLAG (B9600|CS8|CLOCAL|CREAD|PARENB|PARODD) +#define TRIMBLETSIP_IFLAG (IGNBRK) +#define TRIMBLETSIP_OFLAG (0) +#define TRIMBLETSIP_LFLAG (0) + +#define TRIMBLETAIP_FLAGS (PARSE_F_PPSPPS|PARSE_F_PPSONSECOND) +#define TRIMBLETSIP_FLAGS (TRIMBLETAIP_FLAGS|PARSE_F_NOPOLLONLY) + +#define TRIMBLETAIP_POLL poll_dpoll +#define TRIMBLETSIP_POLL poll_dpoll + +#define TRIMBLETAIP_INIT trimbletaip_init +#define TRIMBLETSIP_INIT trimbletsip_init + +#define TRIMBLETAIP_END poll_end +#define TRIMBLETSIP_END poll_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 NO_FORMAT +#define TRIMBLETSIP_FORMAT "Trimble SV6/TSIP" + +#define TRIMBLETAIP_ROOTDELAY 0x0 +#define TRIMBLETSIP_ROOTDELAY 0x0 + +#define TRIMBLETAIP_BASEDELAY 0x0 +#define TRIMBLETSIP_BASEDELAY 0x51EB852 /* 20 ms as a l_uf - avg 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 '<' static struct clockinfo { @@ -674,23 +710,42 @@ static struct clockinfo GPS166_LFLAG }, { /* 127.127.8.32+<device> */ - TRIMBLESV6_FLAGS, - TRIMBLESV6_POLL, - TRIMBLESV6_INIT, - TRIMBLESV6_END, - TRIMBLESV6_DATA, - TRIMBLESV6_ROOTDELAY, - TRIMBLESV6_BASEDELAY, + TRIMBLETAIP_FLAGS, + TRIMBLETAIP_POLL, + TRIMBLETAIP_INIT, + TRIMBLETAIP_END, + TRIMBLETAIP_DATA, + TRIMBLETAIP_ROOTDELAY, + TRIMBLETAIP_BASEDELAY, + NO_PPSDELAY, + TRIMBLETAIP_ID, + TRIMBLETAIP_DESCRIPTION, + TRIMBLETAIP_FORMAT, + GPS_TYPE, + TRIMBLETAIP_MAXUNSYNC, + TRIMBLETAIP_CFLAG, + TRIMBLETAIP_IFLAG, + TRIMBLETAIP_OFLAG, + TRIMBLETAIP_LFLAG + }, + { /* 127.127.8.36+<device> */ + TRIMBLETSIP_FLAGS, + TRIMBLETSIP_POLL, + TRIMBLETSIP_INIT, + TRIMBLETSIP_END, + TRIMBLETSIP_DATA, + TRIMBLETSIP_ROOTDELAY, + TRIMBLETSIP_BASEDELAY, NO_PPSDELAY, - TRIMBLESV6_ID, - TRIMBLESV6_DESCRIPTION, - TRIMBLESV6_FORMAT, + TRIMBLETSIP_ID, + TRIMBLETSIP_DESCRIPTION, + TRIMBLETSIP_FORMAT, GPS_TYPE, - TRIMBLESV6_MAXUNSYNC, - TRIMBLESV6_CFLAG, - TRIMBLESV6_IFLAG, - TRIMBLESV6_OFLAG, - TRIMBLESV6_LFLAG + TRIMBLETSIP_MAXUNSYNC, + TRIMBLETSIP_CFLAG, + TRIMBLETSIP_IFLAG, + TRIMBLETSIP_OFLAG, + TRIMBLETSIP_LFLAG } }; @@ -3409,11 +3464,12 @@ poll_end(parse) ** special code for special clocks **/ + /*-------------------------------------------------- - * trimble init routine - setup EOL and then do poll_init. + * trimble TAIP init routine - setup EOL and then do poll_init. */ static int -trimble_init(parse) +trimbletaip_init(parse) struct parseunit *parse; { #ifdef HAVE_TERMIOS @@ -3427,21 +3483,221 @@ trimble_init(parse) */ if (TTY_GETATTR(parse->fd, &tm) == -1) { - syslog(LOG_ERR, "PARSE receiver #%d: trimble_init: tcgetattr(fd, &tm): %m", CL_UNIT(parse->unit)); + syslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcgetattr(fd, &tm): %m", CL_UNIT(parse->unit)); return 0; } else { - tm.c_cc[VEOL] = TRIMBLESV6_EOL; + tm.c_cc[VEOL] = TRIMBLETAIP_EOL; if (TTY_SETATTR(parse->fd, &tm) == -1) { - syslog(LOG_ERR, "PARSE receiver #%d: trimble_init: tcsetattr(fd, &tm): %m", CL_UNIT(parse->unit)); + syslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcsetattr(fd, &tm): %m", CL_UNIT(parse->unit)); return 0; } } return poll_init(parse); } + +/* + * 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 xntpd 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 ! + */ + +union { + u_char bd[8]; + int iv; + float fv; + double dv; +} uval; + +struct txbuf +{ + short idx; /* index to first unused byte */ + u_char *txt; /* pointer to actual data buffer */ +}; + +void +sendcmd(buf, c) + struct txbuf *buf; + u_char c; +{ + buf->txt[0] = DLE; + buf->txt[1] = c; + buf->idx = 2; +} + +void sendbyte(buf, b) + struct txbuf *buf; + u_char b; +{ + if (b == DLE) + buf->txt[buf->idx++] = DLE; + buf->txt[buf->idx++] = b; +} + +void +sendetx(buf, parse) + struct txbuf *buf; + struct parseunit *parse; +{ + buf->txt[buf->idx++] = DLE; + buf->txt[buf->idx++] = ETX; + + if (write(parse->fd, buf->txt, buf->idx) != buf->idx) + { + syslog(LOG_ERR, "PARSE receiver #%d: sendetx: failed to send cmd to clock: %m", CL_UNIT(parse->unit)); + } +} + +void +sendint(buf, a) + struct txbuf *buf; + int a; +{ + uval.iv = a; + sendbyte(buf, uval.bd[2]); + sendbyte(buf, uval.bd[3]); +} + +void +sendflt(buf, a) + struct txbuf *buf; + float a; +{ + int i; + + uval.fv = a; + for (i=0; i<=3; i++) + sendbyte(buf, uval.bd[i]); +} + +/*-------------------------------------------------- + * trimble TSIP init routine + */ +static int +trimbletsip_init(parse) + struct parseunit *parse; +{ + u_char buffer[256]; + struct txbuf buf; + + buf.txt = buffer; + + if (!poll_init(parse)) + { + sendcmd(&buf, 0x1f); /* request software versions */ + sendetx(&buf, parse); + + sendcmd(&buf, 0x2c); /* 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, 0x22); /* fix mode select */ + sendbyte(&buf, 0); /* automatic */ + sendetx(&buf, parse); + + sendcmd(&buf, 0x28); /* request system message */ + sendetx(&buf, parse); + + sendcmd(&buf, 0x8e); /* superpacket fix */ + sendbyte(&buf, 0x2); /* binary mode */ + sendetx(&buf, parse); + + sendcmd(&buf, 0x35); /* set I/O options */ + sendbyte(&buf, 0); /* no position output */ + sendbyte(&buf, 0); /* no velocity output */ + sendbyte(&buf, 7); /* UTC, compute on seconds, send only on request */ + sendbyte(&buf, 0); /* no raw measurements */ + sendetx(&buf, parse); + + sendcmd(&buf, 0x2f); /* request UTC correction data */ + sendetx(&buf, parse); + return 0; + } + else + return 1; +} + #endif /* defined(REFCLOCK) && defined(PARSE) */ /* diff --git a/usr.sbin/xntpd/xntpd/refclock_pst.c b/usr.sbin/xntpd/xntpd/refclock_pst.c index 4b8909afb801..29cbfb1201ec 100644 --- a/usr.sbin/xntpd/xntpd/refclock_pst.c +++ b/usr.sbin/xntpd/xntpd/refclock_pst.c @@ -1,7 +1,7 @@ /* - * refclock_pst - driver for the PSTI 1010/1020 WWV clock + * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers */ -#if defined(REFCLOCK) && (defined(PST) || defined(PSTCLK) || defined(PSTPPS)) +#if defined(REFCLOCK) && defined(PST) #include <stdio.h> #include <ctype.h> @@ -10,1795 +10,320 @@ #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" -#include "ntp_unixtime.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(PSTCLK) -#include <sys/clkdefs.h> -#endif /* PSTCLK */ -#endif /* STREAM */ - -#if defined (PSTPPS) -#include <sys/ppsclock.h> -#endif /* PSTPPS */ - #include "ntp_stdlib.h" /* - * This driver is in good measure due to David Schachter, who wrote - * the firmware for the PST clock. Not that he is to blame for - * any of this, but he kindly loaned me a clock to allow me to - * debug this. - * - * Postscript: - * - * The strategy in here is actually pretty good, especially if - * you try to support the clock on something lacking low order - * clock bits like a Sun, since all the business which is done - * before taking a time stamp tends to randomize the taking of - * the stamp with respect to the timer interrupt. It is, however, - * a big cpu hog, and in some ways is a bit of a waste since, as - * it turns out, the PST clock can give you no better than a - * millisecond precision and it doesn't pay to try to push it - * harder. - * - * In any event, like the first waffle off the iron, this one - * should probably be tossed. My current preference would be - * to retain the 12-a-minute schedule, but to use the QU command - * instead of the QD and QT, and to only send a QM command with - * the 12th poll of the minute to get the minutes-since-sync - * and the station. Need to get a clock which supports QU, - * however. - * - * End postscript - * - * This driver polls the clock using the QM, QT and QD commands. - * Ntpd actually uses QU instead of the last two, something I would - * like to have done as well since it gives you the day and time - * atom, but the firmware in the clock I had (X04.01.999) didn't know - * about this command. - * - * The QM command produces output like: - * - * O6B532352823C00270322 - * b c deeee - * - * We use (b) for the time zone, (c) to see whether time is available, - * (d) to tell whether we are sync'd to WWV or WWVH, and (e) to determine - * the number of minutes since the last signal was received. We - * don't trust the clock for more than about 20 minutes on its own. - * After this, we keep taking the time but mark the clock unsynchronized. + * 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. * - * The QT command returns something that looks like this: + * 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: * - * 18:57:50.263D + * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr> * - * Note that this particular sample is in 24 hour format, local time - * (daylight savings time even). We allow just about anything for - * this (sigh) since this leaves the clock owner free to set the - * display mode in whatever way he finds convenient for setting - * his watch. + * 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 QD command returns: + * 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. * - * 89/10/19/292 + * Fudge Factors * - * We actually only use the day-of-the-year here. We use the year - * only to determine whether the PST clock thinks the current year - * has 365 or 366 days in it. - * - * At the current writing, this code expects to be using a BSD-style - * terminal driver. It will compile code which uses the CLKLDISC - * line discipline if it thinks this is available, but use cooked - * mode otherwise. The cooked mode stuff may not have been tested. - */ - -/* - * Definitions - */ -#define MAXUNITS 4 /* maximum number of PST units permitted */ -#define PSTDEV "/dev/pst%d" /* device we open. %d is unit number */ -#define NPSTSAMPS 12 /* take 12 PST samples per minute */ - -/* - * Other constant stuff + * There are no special fudge factors other than the generic. */ -#define PSTPRECISION (-9) /* what the heck */ -#define WWVREFID "WWV\0" -#define WWVHREFID "WWVH" -#define PSTHSREFID 0x7f7f030a /* 127.127.3.10 refid for hi strata */ /* - * Parameters for the clock + * Interface definitions */ -#define SPEED232 B9600 -#define PSTMAGIC2 ('\r' | 0x80) /* HP-UX uses this also now */ -#ifdef CLKLDISC -#define PSTMAGIC1 '\r' -#define PSTEOL '\r' -#else -#define PSTEOL '\n' -#endif +#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 */ -/* - * Description of clock. We fill in whether it is a 1010 or 1020, - * and the firmware revision, using the QV command. - */ -#define PSTDESCLEN 64 -#define PSTDESCRIPTION "%s %s (%s) WWV/H Receiver" -#define PSTDEFDESC "PSTI/Traconex 10?0 (V??.??) WWV/H Receiver" +#define NSAMPLES 3 /* stages of median filter */ +#define LENPST 46 /* min timecode length */ /* - * Length of the PST time code. This must be the length of the output - * of the QM command, plus QT, plus QD, plus two spaces. We make it - * big just on principle. + * Imported from ntp_timer module */ -#define PSTCODELEN (128) +extern u_long current_time; /* current time (s) */ /* - * Minimum and maximum lengths - */ -#define PSTMINQVLEN (16) -#define PSTMAXQVLEN (24) - -#define PSTMINQMLEN (19) -#define PSTMAXQMLEN (32) - -#define PSTMINQDLEN (12) -#define PSTMAXQDLEN (12) - -#define PSTMINQTLEN (14) -#define PSTMAXQTLEN (14) - -/* - * It turns out that the QT command does *not* adjust for transmission - * delays. Since the QT command returns 15 characters at 9600 baud, - * the adjustment for this should be 15.6 ms. We'll default to this, - * but don't let this stop you from fiddling with the fudge factors - * to make things come out right - */ -#define PSTQTFUDGE 0x04000000 /* about 15.6 ms */ - -/* - * Default propagation delays. About right for Toronto - */ -#define DEFWWVPROP 0x01eb851f /* about 7.5 ms */ -#define DEFWWVHPROP 0x06c8b439 /* about 26.5 ms */ - -/* - * Maximum propagation delay we believe. 125 ms as an l_fp fraction - */ -#define PSTMAXPROP 0x20000000 - -/* - * Default minutes since an update. - */ -#define DEFMAXFREERUN (20) - -/* - * Hack to avoid excercising the multiplier. I have no pride. + * Imported from ntpd module */ -#define MULBY10(x) (((x)<<3) + ((x)<<1)) +extern int debug; /* global debug flag */ /* - * PST unit control structure. + * Unit control structure */ struct pstunit { - struct peer *peer; /* associated peer structure */ - struct event psttimer; /* timeout timer structure */ - struct refclockio pstio; /* given to the I/O handler */ - l_fp rectimes[NPSTSAMPS]; /* times we received this stuff */ - l_fp reftimes[NPSTSAMPS]; /* times of codes received */ - l_fp lastrec; /* last receive time */ - l_fp lastref; /* last reference time */ - char description[PSTDESCLEN]; /* description of clock */ - char lastcode[PSTCODELEN]; /* last code we received */ - u_char lencode; /* length of the last code */ - u_char nextsample; /* the next offset expected */ - u_char unit; /* unit number for this guy */ - u_char state; /* what we're waiting for */ - s_char station; /* WWV or WWVH? */ - u_char flags; /* flag byte */ - u_char status; /* clock status */ - u_char lastevent; /* last clock event */ - u_char timezone; /* hour offset to time zone */ - u_char errors; /* number of errors detected */ - u_char year; /* year reported by clock */ - u_char month; /* month, from clock */ - u_char monthday; /* day, from clock */ - u_char hour; /* hour of day */ - u_char minute; /* minute of day */ - u_char second; /* second of day */ - u_char leap; /* leap indicators */ - s_char tzoffset; /* time zone offset */ - u_char reason; /* reason for failure */ - u_short millisecond; /* millisecond of day */ - u_short yearday; /* day of the year */ - u_short timesincesync; /* time since radio got sample */ - U_LONG yearstart; /* NTP time at year start */ - U_LONG lastupdate; /* last time data received */ - U_LONG polls; /* number of polls */ - U_LONG noreply; /* number of time outs */ - U_LONG badformat; /* number of bad format responses */ - U_LONG baddata; /* number of invalid time codes */ - U_LONG timestarted; /* time we started this */ -}; + int pollcnt; /* poll message counter */ -/* - * States we might be in - */ -#define STATE_IDLE 0 /* not doing anything in particular */ -#define STATE_QV 1 /* trying to get version */ -#define STATE_QM 2 /* sent QM */ -#define STATE_QD 3 /* sent QD */ -#define STATE_QT 4 /* send QT */ - -/* - * Status flags - */ -#define PST_LEAPYEAR 0x1 /* pst clock thinks it is a leap year */ -#define PST_SIGFAULT 0x2 /* signal fault */ -#define PST_HARDERR 0x4 /* hardware error */ -#define PST_NOTIME 0x8 /* no time available */ -#define PST_WWVH 0x10 /* synchronized to WWVH */ -#define PST_DOQV 0x20 /* get version, reinit delays */ -#define PST_DORESET 0x40 /* reset the clock */ - -/* - * The PST often encodes stuff by adding an ASCII '0' to it. The - * largest range of values encoded this way is 0 through 31, or '0' - * through 'O'. These macroes manipulate these values. - */ -#define ISVALIDPST(c) ((c) >= '0' && (c) <= 'O') -#define PSTTOBIN(c) ((int)(c) - '0') -#define BINTOPST(c) ((char)((c) + '0')) - -/* - * Status bits. Look at the QM command - */ -#define SIGFAULT 0x1 -#define HARDFAULT 0x2 -#define OUTOFSPEC 0x4 -#define TIMEAVAILABLE 0x8 - -/* - * Module reason codes - */ -#define QVREASON 20 -#define QMREASON 40 -#define QDREASON 60 -#define QTREASON 80 - -/* - * Station i.d. characters in QM output - */ -#define WWV_CHAR 'C' -#define WWVH_CHAR 'H' - -/* - * We allow a few errors, but if we get more than 12 seconds behind - * the schedule we start from sample 0 again. 4 seconds is the minimum - * time between time out routine executions. - */ -#define PSTMAXDELAY 12 -#define PSTMINTIMEOUT 4 - -/* - * The PST polling schedule. We poll 12 times per 64 seconds (far too - * many, but what the heck). The polls are scheduled to finish in this - * time with the assumption that the timer is good for no better than - * 4 second resolution. If we get too far behind (due to bad samples - * or no responses) we start over. - */ -struct pstsched { - u_short nextinterval; - u_short tooold; + u_char tcswitch; /* timecode switch */ + char *lastptr; /* pointer to timecode data */ }; -static struct pstsched psttab[NPSTSAMPS] = { - { 4, PSTMAXDELAY+1 }, - { 4, PSTMAXDELAY+1+4 }, - { 8, PSTMAXDELAY+1+4+4 }, - { 4, PSTMAXDELAY+1+4+4+8 }, - { 8, PSTMAXDELAY+1+4+4+8+4 }, - { 4, PSTMAXDELAY+1+4+4+8+4+8 }, - { 4, PSTMAXDELAY+1+4+4+8+4+8+4 }, - { 8, PSTMAXDELAY+1+4+4+8+4+8+4+4 }, - { 4, PSTMAXDELAY+1+4+4+8+4+8+4+4+8 }, - { 8, PSTMAXDELAY+1+4+4+8+4+8+4+4+8+4 }, - { 4, PSTMAXDELAY+1+4+4+8+4+8+4+4+8+4+8 }, - { 4, PSTMAXDELAY+1+4+4+8+4+8+4+4+8+4+8+4 } -}; - - -/* - * Data space for the unit structures. Note that we allocate these on - * the fly, but never give them back. - */ -static struct pstunit *pstunits[MAXUNITS]; -static u_char unitinuse[MAXUNITS]; - -/* - * Structure to keep processed propagation data in. - */ -struct pst_propagate { - U_LONG remainder; /* left over submillisecond remainder */ - char msbchar; /* character for high order bits */ - char lsbchar; /* character for low order bits */ -}; - - -/* - * Keep the fudge factors separately so they can be set even - * when no clock is configured. - */ -static l_fp wwv_prop_delay[MAXUNITS]; -static l_fp wwvh_prop_delay[MAXUNITS]; -static struct pst_propagate wwv_prop_data[MAXUNITS]; -static struct pst_propagate wwvh_prop_data[MAXUNITS]; -static u_char stratumtouse[MAXUNITS]; -static u_char sloppyclock[MAXUNITS]; -static u_short freerun[MAXUNITS]; - -/* - * Pointer to the default description - */ -static char *pstdefdesc = PSTDEFDESC; - -/* - * macro for writing to the clock, printing an error if we fail - */ -#define pst_send(pst, str, len) \ - if (write((pst)->pstio.fd, (str), (len)) < 0) \ - pst_write_error((pst)) - -/* - * macro for resetting the clock structure to zero - */ -#define pst_reset(pst) \ - do { \ - pst->nextsample = 0; \ - pst->station = 0; \ - pst->leap = 0; \ - } while (0) - -/* - * macro for event reporting - */ -#define pst_event(pst, evnt_code) \ - do { \ - if ((pst)->status != (u_char)(evnt_code)) \ - pst_do_event((pst), (evnt_code)); \ - } while (0) - -/* - * Imported from the timer module - */ -extern U_LONG current_time; -extern struct event timerqueue[]; - -/* - * Imported from ntp_loopfilter module - */ -extern int fdpps; /* pps file descriptor */ - -/* - * Imported from ntpd module - */ -extern int debug; /* global debug flag */ - /* * Function prototypes */ -static void pst_init P((void)); -static int pst_start P((u_int, struct peer *)); -static void pst_shutdown P((int)); +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_process P((struct pstunit *)); -static void pst_control P((u_int, struct refclockstat *, struct refclockstat *)); -static void pst_buginfo P((int, struct refclockbug *)); -static void pst_write_error P((struct pstunit *)); -static void pst_timeout P((struct peer *)); -static int pst_QV_process P((struct pstunit *, struct recvbuf *)); -static int pst_QM_process P((struct pstunit *, struct recvbuf *)); -static int pst_QD_process P((struct pstunit *, struct recvbuf *)); -static int pst_QT_process P((struct pstunit *, struct recvbuf *, l_fp *, l_fp *)); -static void pst_do_event P((struct pstunit *, int)); -static void pst_compute_delay P((U_LONG, struct pst_propagate *)); +static void pst_poll P((int, struct peer *)); /* * Transfer vector */ struct refclock refclock_pst = { - pst_start, pst_shutdown, noentry, - pst_control, pst_init, pst_buginfo, NOFLAGS + 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_init - initialize internal PST driver data - */ -static void -pst_init() -{ - register int i; - - /* - * Just zero the data arrays - */ - memset((char *)pstunits, 0, sizeof pstunits); - memset((char *)unitinuse, 0, sizeof unitinuse); - - /* - * Initialize fudge factors to default. - */ - for (i = 0; i < MAXUNITS; i++) { - wwv_prop_delay[i].l_ui = 0; - wwv_prop_delay[i].l_uf = DEFWWVPROP; - pst_compute_delay(DEFWWVPROP, &wwv_prop_data[i]); - wwvh_prop_delay[i].l_ui = 0; - wwvh_prop_delay[i].l_uf = DEFWWVHPROP; - pst_compute_delay(DEFWWVHPROP, &wwvh_prop_data[i]); - stratumtouse[i] = 0; - sloppyclock[i] = 0; - freerun[i] = DEFMAXFREERUN; - } -} - /* - * pst_start - open the PST device and initialize data for processing + * pst_start - open the devices and initialize data for processing */ static int pst_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { - register struct pstunit *pst; - register int i; - int fd232; - char pstdev[20]; - - /* - * Check configuration info - */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "pst_start: unit %d invalid", unit); - return 0; - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "pst_start: unit %d in use", unit); - return 0; - } + register struct pstunit *up; + struct refclockproc *pp; + int fd; + char device[20]; /* - * Open serial port - */ - (void) sprintf(pstdev, PSTDEV, unit); - fd232 = open(pstdev, O_RDWR, 0777); - if (fd232 == -1) { - syslog(LOG_ERR, "pst_start: open of %s: %m", pstdev); - return 0; - } - -#if defined(HAVE_SYSV_TTYS) - /* - * System V serial line parameters (termio interface) - * - */ - { struct termio ttyb; - if (ioctl(fd232, TCGETA, &ttyb) < 0) { - syslog(LOG_ERR, - "pst_start: ioctl(%s, TCGETA): %m", pstdev); - 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) { - syslog(LOG_ERR, - "pst_start: ioctl(%s, TCSETA): %m", pstdev); - goto screwed; - } - } -#endif /* HAVE_SYSV_TTYS */ -#if defined(HAVE_TERMIOS) - /* - * POSIX serial line parameters (termios interface) - * - * The PSTCLK option provides timestamping at the driver level. - * It requires the tty_clk streams module. - * - * The PSTPPS 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 SunOS 4.1.1 or - * later. + * Open serial port. Use CLK line discipline, if available. */ - { struct termios ttyb, *ttyp; + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) + return (0); - ttyp = &ttyb; - if (tcgetattr(fd232, ttyp) < 0) { - syslog(LOG_ERR, - "pst_start: tcgetattr(%s): %m", pstdev); - 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) { - syslog(LOG_ERR, - "pst_start: tcsetattr(%s): %m", pstdev); - goto screwed; - } - if (tcflush(fd232, TCIOFLUSH) < 0) { - syslog(LOG_ERR, - "pst_start: tcflush(%s): %m", pstdev); - goto screwed; - } - } -#endif /* HAVE_TERMIOS */ -#ifdef STREAM -#if defined(PSTCLK) - if (ioctl(fd232, I_PUSH, "clk") < 0) - syslog(LOG_ERR, - "pst_start: ioctl(%s, I_PUSH, clk): %m", pstdev); - if (ioctl(fd232, CLK_SETSTR, "\n") < 0) - syslog(LOG_ERR, - "pst_start: ioctl(%s, CLK_SETSTR): %m", pstdev); -#endif /* PSTCLK */ -#if defined(PSTPPS) - if (ioctl(fd232, I_PUSH, "ppsclock") < 0) - syslog(LOG_ERR, - "pst_start: ioctl(%s, I_PUSH, ppsclock): %m", pstdev); - else - fdpps = fd232; -#endif /* PSTPPS */ -#endif /* STREAM */ -#if defined(HAVE_BSD_TTYS) /* - * 4.3bsd serial line parameters (sgttyb interface) - * - * The PSTCLK option provides timestamping at the driver level. - * It requires the tty_clk line discipline and 4.3bsd or later. + * Allocate and initialize unit structure */ - { struct sgttyb ttyb; -#if defined(PSTCLK) - int ldisc = CLKLDISC; -#endif /* PSTCLK */ - - if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { - syslog(LOG_ERR, - "pst_start: ioctl(%s, TIOCGETP): %m", pstdev); - goto screwed; - } - ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; -#if defined(PSTCLK) - 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 /* PSTCLK */ - if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { - syslog(LOG_ERR, - "pst_start: ioctl(%s, TIOCSETP): %m", pstdev); - goto screwed; + if (!(up = (struct pstunit *) + emalloc(sizeof(struct pstunit)))) { + (void) close(fd); + return (0); } -#if defined(PSTCLK) - if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { - syslog(LOG_ERR, - "pst_start: ioctl(%s, TIOCSETD): %m",pstdev); - goto screwed; - } -#endif /* PSTCLK */ - } -#endif /* HAVE_BSD_TTYS */ - - /* - * Allocate unit structure - */ - if (pstunits[unit] != 0) { - pst = pstunits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < MAXUNITS; i++) { - if (!unitinuse[i] && pstunits[i] != 0) - break; - } - if (i < MAXUNITS) { - /* - * Reclaim this one - */ - pst = pstunits[i]; - pstunits[i] = 0; - } else { - pst = (struct pstunit *)emalloc(sizeof(struct pstunit)); - } + 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); } - memset((char *)pst, 0, sizeof(struct pstunit)); - pstunits[unit] = pst; + pp->unitptr = (caddr_t)up; /* - * Set up the structure + * Initialize miscellaneous variables */ - pst->peer = peer; - pst->unit = (u_char)unit; - pst->state = STATE_IDLE; - pst->flags |= PST_DOQV; - pst->timestarted = current_time; - (void) strcpy(pst->description, pstdefdesc); - - pst->psttimer.peer = (struct peer *)pst; - pst->psttimer.event_handler = pst_timeout; - - pst->pstio.clock_recv = pst_receive; - pst->pstio.srcclock = (caddr_t)pst; - pst->pstio.datalen = 0; - pst->pstio.fd = fd232; - if (!io_addclock(&pst->pstio)) { - goto screwed; - } - - /* - * All done. Initialize a few random peer variables, then - * start the timer and return success. - */ - peer->precision = PSTPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = 0; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, WWVREFID, 4); - else - peer->refid = htonl(PSTHSREFID); - pst->psttimer.event_time = current_time + PSTMINTIMEOUT; - TIMER_ENQUEUE(timerqueue, &pst->psttimer); - unitinuse[unit] = 1; - return 1; - - /* - * Something broke; abandon ship. - */ -screwed: - (void) close(fd232); - return (0); + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, WWVREFID, 4); + up->pollcnt = 2; + return (1); } + /* - * pst_shutdown - shut down a PST clock + * pst_shutdown - shut down the clock */ static void -pst_shutdown(unit) +pst_shutdown(unit, peer) int unit; + struct peer *peer; { - register struct pstunit *pst; + register struct pstunit *up; + struct refclockproc *pp; - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "pst_shutdown: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "pst_shutdown: unit %d not in use", unit); - return; - } - - /* - * Tell the I/O module to turn us off, and dequeue timer - * if any. We're history. - */ - pst = pstunits[unit]; - TIMER_DEQUEUE(&pst->psttimer); - io_closeclock(&pst->pstio); - unitinuse[unit] = 0; + pp = peer->procptr; + up = (struct pstunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); } /* - * pst_write_error - complain about writes to the clock + * pst_receive - receive data from the serial interface */ static void -pst_write_error(pst) - struct pstunit *pst; -{ - /* - * This will fill syslog is something is really wrong. Should - * throttle it back. - */ - syslog(LOG_ERR, "pst_write_error: unit %d: %m", pst->unit); -} - - -/* - * pst_timeout - process a timeout event - */ -static void -pst_timeout(fakepeer) - struct peer *fakepeer; -{ - register struct pstunit *pst; - U_LONG poll; - - /* - * The timeout routine always initiates a chain of - * query-responses from the clock, by sending either - * a QV command (if we need to (re)set the propagation - * delays into the clock), a QM command or an SRY - * command (after a leap second). The pst_receive() - * routine should complete the set of queries on its own - * LONG before the next time out is due, so if we see any - * state in here other than idle it means the clock hasn't - * responded. - */ - pst = (struct pstunit *)fakepeer; - switch(pst->state) { - case STATE_IDLE: - poll = (U_LONG)psttab[pst->nextsample].nextinterval; - break; /* all is well */ - - case STATE_QV: - pst->flags |= PST_DOQV; /* no response, do QV again */ - /*FALLSTHROUGH*/ - - case STATE_QM: - case STATE_QD: - case STATE_QT: - pst->noreply++; /* mark the lack of response */ - poll = PSTMINTIMEOUT; /* minimum time poll */ - break; - - default: - syslog(LOG_ERR, "pst_timeout: unit %d invalid state %d", - pst->unit, pst->state); - poll = PSTMINTIMEOUT; /* minimum time poll */ - break; - } - - if (pst->flags & PST_DORESET) { - /* - * Do a reset. At the next interrupt, start with - * a QV command to set in the delays. - */ - pst->flags &= ~PST_DORESET; - pst->flags |= PST_DOQV; - pst->state = STATE_IDLE; - pst_send(pst, "\003SRY", 4); - } else if (pst->flags & PST_DOQV) { - pst->polls++; - pst->flags &= ~PST_DOQV; - pst->state = STATE_QV; - pst_send(pst, "\003QV", 3); - } else { - pst->polls++; - pst->state = STATE_QM; - pst_send(pst, "\003QM", 3); - } - - pst->psttimer.event_time += poll; - TIMER_ENQUEUE(timerqueue, &pst->psttimer); -} - - -/* - * pst_QV_process - decode the results of a QV poll and insert fudge - * factors into the clock. - */ -static int -pst_QV_process(pst, rbufp) - register struct pstunit *pst; - struct recvbuf *rbufp; -{ - register char *cp; - register char *bp; - register int len; - char *model; - char *company; - char buf[20]; - static char wwvdelay[6] = { 'S', 'C', '\0', 'S', 'E', '\0' }; - static char wwvhdelay[6] = { 'S', 'H', '\0', 'S', 'G', '\0' }; - - /* - * The output of the QV command looks like: - * - * PSTI ITS V04.01.000\r - * - * or - * - * TRAC ITS V04.01.000\r - * - * or - * - * TRACONEX TS V05.02.001\r - * - * The minimum length of the string is about 16 characters. - * The maximum length is sort of unbounded, but we get suspicious - * if it is more than 34. - */ - len = rbufp->recv_length; - if (len > PSTMAXQVLEN + 10) - len = PSTMAXQVLEN + 10; - - bp = rbufp->recv_buffer; - cp = pst->lastcode; - while (len-- > 0) { - *cp = (*bp++) & 0x7f; /* strip parity */ - if (!isprint(*cp)) - break; - cp++; - } - pst->lencode = (u_char)(cp - pst->lastcode); - - /* - * Okay, got all printable characters from the string - * copied. We expect to have been terminated by the - * EOL character. If not, forget it. If the length - * is insane, forget it. - */ - - if (*cp != PSTEOL - || pst->lencode < PSTMINQVLEN || pst->lencode > PSTMAXQVLEN) { - pst->reason = QVREASON + 1; - return 0; - } - - /* - * Now, format check what we can. Dump it at the least - * sign of trouble. - */ - cp = pst->lastcode; - model = NULL; - if (*cp++ != 'P' || *cp++ != 'S' || *cp++ != 'T' - || *cp++ != 'I' || *cp++ != ' ') { - cp = pst->lastcode; - if (*cp++ != 'T' || *cp++ != 'R' || *cp++ != 'A' - || *cp++ != 'C' || *cp++ != ' ') { - cp = pst->lastcode; - if (*cp++ != 'T' || *cp++ != 'R' || *cp++ != 'A' - || *cp++ != 'C' || *cp++ != 'O' || *cp++ != 'N' - || *cp++ != 'E' || *cp++ != 'X' || *cp != ' ') { - pst->reason = QVREASON + 2; - return 0; - } - company = "Traconex"; - model = "1030"; - } - company = "Traconex"; - } else { - company = "Precision Standard Time"; - } - - if (*cp == 'M') - model = "1010"; - else if (*cp == 'I') - model = "1020"; - else if (model == NULL) { - pst->reason = QVREASON + 3; - return 0; - } - cp++; - - if (*cp++ != 'T' || *cp++ != 'S' || *cp++ != ' ') { - pst->reason = QVREASON + 4; - return 0; - } - if (*cp != 'X' && *cp != 'V') { - pst->reason = QVREASON + 5; - return 0; - } - - /* - * Next is the version. Copy it into the buffer. - */ - bp = buf; - *bp++ = *cp++; - while (isdigit(*cp) || *cp == '.') - *bp++ = *cp++; - *bp++ = '\0'; - - /* - * Final bit of fluff is to set the description - */ - (void) sprintf(pst->description, PSTDESCRIPTION, company, model, buf); - - /* - * Now the serious stuff. Since we are now sure that the - * clock is there, we can be fairly sure that the delay - * setting commands will take. Send them. - */ - wwvdelay[2] = wwv_prop_data[pst->unit].msbchar; - wwvdelay[5] = wwv_prop_data[pst->unit].lsbchar; - pst_send(pst, wwvdelay, 6); - - /* - * Same thing for WWVH - */ - wwvhdelay[2] = wwvh_prop_data[pst->unit].msbchar; - wwvhdelay[5] = wwvh_prop_data[pst->unit].lsbchar; - pst_send(pst, wwvhdelay, 6); - - /* - * Should be okay. Return positive response. - */ - return 1; -} - - -/* - * pst_QM_process - process the output of a QM command - */ -static int -pst_QM_process(pst, rbufp) - register struct pstunit *pst; - struct recvbuf *rbufp; -{ - register char *cp; - register char *bp; - register int n; - - /* - * The output of the QM command looks like: - * - * O6B532352823C00270322 - * - * The minimum length of the string is 19 characters. - * The maximum length is sort of unbounded, but we get suspicious - * if it is more than 42. - */ - n = rbufp->recv_length; - if (n > PSTMAXQMLEN + 10) - n = PSTMAXQMLEN + 10; - - bp = rbufp->recv_buffer; - cp = pst->lastcode; - while (n-- > 0) { - *cp = (*bp++) & 0x7f; /* strip parity */ - if (!isprint(*cp)) - break; - cp++; - } - pst->lencode = (u_char)(cp - pst->lastcode); - - /* - * Okay, got all printable characters from the string - * copied. We expect to have been terminated by the - * EOL character. If not, forget it. If the length - * is insane, forget it. - */ - if (*cp != PSTEOL - || pst->lencode < PSTMINQMLEN || pst->lencode > PSTMAXQMLEN) { - pst->reason = QMREASON + 1; - return 0; - } - - /* - * Ensure that the first PSTMINQMLEN characters are valid with - * respect to the way the clock encodes binary data. - */ - cp = pst->lastcode; - n = pst->lencode; - while (n-- > 0) { - if (!ISVALIDPST(*cp)) { - pst->reason = QMREASON + 2; - return 0; - } - cp++; - } - - /* - * Collect information we are interested in. - */ - cp = pst->lastcode; - pst->timezone = PSTTOBIN(cp[3]); - if (pst->timezone > 23) { - pst->reason = QMREASON + 3; - return 0; - } - - pst->flags &= - ~(PST_LEAPYEAR|PST_SIGFAULT|PST_HARDERR|PST_NOTIME|PST_WWVH); - n = PSTTOBIN(cp[4]); - if (n > 15) { - pst->reason = QMREASON + 4; - return 0; - } - if (((n + 2) & 0x3) == 0) - pst->flags |= PST_LEAPYEAR; - - n = PSTTOBIN(cp[9]); - if (n > 15) { - pst->reason = QMREASON + 5; - return 0; - } - if (n & SIGFAULT) - pst->flags |= PST_SIGFAULT; - if (n & HARDFAULT) - pst->flags |= PST_HARDERR; - if (!(n & TIMEAVAILABLE)) - pst->flags |= PST_NOTIME; - - if (cp[12] == 'H') { - pst->flags |= PST_WWVH; - } else if (cp[12] == 'C') { - pst->flags &= ~PST_WWVH; - } else { - pst->reason = QMREASON + 6; - return 0; - } - - if (wwv_prop_data[pst->unit].msbchar != cp[5] || - wwv_prop_data[pst->unit].lsbchar != cp[6] || - wwvh_prop_data[pst->unit].msbchar != cp[7] || - wwvh_prop_data[pst->unit].lsbchar != cp[8]) - pst->flags |= PST_DOQV; - - bp = cp + 13; - pst->timesincesync = 0; - while (bp < (cp + 17)) { - if (!isdigit(*bp)) { - pst->reason = QMREASON + 6; - return 0; - } - pst->timesincesync = MULBY10(pst->timesincesync) - + PSTTOBIN(*bp); - bp++; - } - - /* - * That's about all we can do. Return success. - */ - return 1; -} - - -/* - * pst_QD_process - process the output of a QD command - */ -static int -pst_QD_process(pst, rbufp) - register struct pstunit *pst; - struct recvbuf *rbufp; -{ - register char *cp; - register char *bp; - register int n; - char *cpstart; - int len; - - /* - * The output of the QM command looks like: - * - * 88/05/17/138\r - * - * The minimum length of the string is 12 characters as is - * the maximum length. - */ - n = rbufp->recv_length; - if (n > PSTMAXQDLEN + 10) - n = PSTMAXQDLEN + 10; - - bp = rbufp->recv_buffer; - cp = &pst->lastcode[pst->lencode]; - *cp++ = ' '; - cpstart = cp; - while (n-- > 0) { - *cp = (*bp++) & 0x7f; /* strip parity */ - if (!isprint(*cp)) - break; - cp++; - } - len = (cp - cpstart); - pst->lencode = (u_char)(cp - pst->lastcode); - - /* - * Okay, got all printable characters from the string - * copied. We expect to have been terminated by the - * EOL character. If not, forget it. If the length - * is insane, forget it. - */ - if (*cp != PSTEOL || - len < PSTMINQDLEN || len > PSTMAXQDLEN) { - pst->reason = QDREASON + 1; - return 0; - } - - /* - * Ensure that the characters are formatted validly. They - * are either digits or '/'s. - */ - cp = cpstart; - if (!isdigit(cp[0]) || !isdigit(cp[1]) || cp[2] != '/' || - !isdigit(cp[3]) || !isdigit(cp[4]) || cp[5] != '/' || - !isdigit(cp[6]) || !isdigit(cp[7]) || cp[8] != '/' || - !isdigit(cp[9]) || !isdigit(cp[10]) || !isdigit(cp[11])) { - pst->reason = QDREASON + 2; - return 0; - } - - /* - * Decode into year, month, day and year day - */ - pst->year = MULBY10(PSTTOBIN(cp[0])) + PSTTOBIN(cp[1]); - pst->month = MULBY10(PSTTOBIN(cp[3])) + PSTTOBIN(cp[4]); - pst->monthday = MULBY10(PSTTOBIN(cp[6])) + PSTTOBIN(cp[7]); - pst->yearday = MULBY10(PSTTOBIN(cp[9])) + PSTTOBIN(cp[10]); - pst->yearday = MULBY10(pst->yearday) + PSTTOBIN(cp[11]); - - /* - * Format check these. - */ - if (pst->month > 12 || pst->monthday > 31 || pst->yearday > 366) { - pst->reason = QDREASON + 3; - return 0; - } - if (!(pst->flags & PST_LEAPYEAR) && pst->yearday > 365) { - pst->reason = QDREASON + 4; - return 0; - } - - /* - * Done all we can. - */ - return 1; -} - - -/* - * pst_QT_process - process the output of a QT command, return the times - */ -static int -pst_QT_process(pst, rbufp, tsclk, tsrec) - register struct pstunit *pst; +pst_receive(rbufp) struct recvbuf *rbufp; - l_fp *tsclk; - l_fp *tsrec; { - register char *cp; - register char *bp; - register int n; - char *cpstart; - int len; - int hour; - int minute; - int second; - int msec; - int tzoff; + 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 */ /* - * The output of the QT command looks like: - * - * A09:57:50.263D - * - * The minimum length of the string is 14 characters as is - * the maximum length. + * Initialize pointers and read the timecode and timestamp */ - n = rbufp->recv_length; - if (n > PSTMAXQTLEN + 10) - n = PSTMAXQTLEN + 10; - - bp = rbufp->recv_buffer; - cp = &pst->lastcode[pst->lencode]; - *cp++ = ' '; - cpstart = cp; - while (n-- > 0) { - *cp = (*bp++) & 0x7f; /* strip parity */ - if (!isprint(*cp)) - break; - cp++; - } - len = (cp - cpstart); - pst->lencode = (u_char)(cp - pst->lastcode); + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct pstunit *)pp->unitptr; + up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->lastcode + + BMAX - 2 - up->lastptr, &trtmp); + *up->lastptr++ = ' '; + *up->lastptr = '\0'; /* - * Okay, got all printable characters from the string - * copied. We expect to have been terminated by the - * EOL character. If not, forget it. If the length - * is insane, forget it. - */ - if (*cp != PSTEOL || - len < PSTMINQTLEN || len > PSTMAXQTLEN) { - pst->reason = QTREASON + 1; - return 0; - } - *cp = '\0'; -#ifdef PSTCLK - /* - * Receive time stamp should be in buffer after the code. - * Make sure we have enough characters in there. - */ - if (&rbufp->recv_buffer[rbufp->recv_length] - bp < 8) { - pst->reason = QTREASON + 2; - return 0; - } - if (!buftvtots(bp, tsrec)) { - pst->reason = QTREASON + 3; - return 0; - } -#else - /* - * Use the timestamp collected with the input. + * Note we get a buffer and timestamp for each <cr>, but only + * the first timestamp is retained. */ - *tsrec = rbufp->recv_time; + if (!up->tcswitch) + pp->lastrec = trtmp; + up->tcswitch++; + pp->lencode = up->lastptr - pp->lastcode; + if (up->tcswitch < 3) + return; + up->pollcnt = 2; + record_clock_stats(&peer->srcadr, pp->lastcode); +#ifdef DEBUG + if (debug) + printf("pst: timecode %d %s\n", pp->lencode, + pp->lastcode); #endif /* - * Ensure that the characters are formatted validly. Mostly - * digits, but the occasional `:' and `.'. - */ - cp = cpstart; - if (!isdigit(cp[1]) || !isdigit(cp[2]) || cp[3] != ':' || - !isdigit(cp[4]) || !isdigit(cp[5]) || cp[6] != ':' || - !isdigit(cp[7]) || !isdigit(cp[8]) || cp[9] != '.' || - !isdigit(cp[10]) || !isdigit(cp[11]) || !isdigit(cp[12])) { - pst->reason = QTREASON + 4; - return 0; - } - - /* - * Extract the hour, minute, second and millisecond - */ - hour = MULBY10(PSTTOBIN(cp[1])) + PSTTOBIN(cp[2]); - minute = MULBY10(PSTTOBIN(cp[4])) + PSTTOBIN(cp[5]); - second = MULBY10(PSTTOBIN(cp[7])) + PSTTOBIN(cp[8]); - msec = MULBY10(PSTTOBIN(cp[10])) + PSTTOBIN(cp[11]); - msec = MULBY10(msec) + PSTTOBIN(cp[12]); - - if (minute > 59 || second > 59) { - pst->reason = QTREASON + 5; - return 0; - } - - /* - * Trouble here. Adjust the hours for AM/PM, if this is - * on, and for daylight saving time. - */ - if (*cp == 'A') { - if (hour > 12 || hour == 0) { - pst->reason = QTREASON + 5; - return 0; - } - if (hour == 12) - hour = 0; - } else if (*cp == 'P') { - if (hour > 12 || hour == 0) - return 0; - if (hour < 12) - hour += 12; - } else if (*cp != ' ') { - pst->reason = QTREASON + 6; - return 0; - } - - if (cp[13] == 'D') - tzoff = -1; - else if (cp[13] == ' ') - tzoff = 0; - else { - pst->reason = QTREASON + 7; - return 0; - } - - /* - * Adjust for the timezone. The PST manual is screwy here. - * it says the timezone is an integer in the range 0 to 23, - * but this doesn't allow us to tell the difference between - * +12 and -12. Assume the 12 hour timezone is west of - * GMT. - */ - if (pst->timezone <= 12) - tzoff += pst->timezone; - else - tzoff -= (24 - pst->timezone); - - - /* - * Record for posterity - */ - pst->hour = (u_char)hour; - pst->minute = (u_char)minute; - pst->second = (u_char)second; - pst->millisecond = (u_short)msec; - pst->tzoffset = (s_char)tzoff; - - /* - * All that to get the day-hour-minute-second. Turn this - * into the seconds part of a time stamp. Also use the - * milliseconds part directly as the fractional part. - */ - MSUTOTSF(msec, tsclk->l_uf); - if (!clocktime((int)pst->yearday, hour, minute, second, tzoff, - tsrec->l_ui, &pst->yearstart, &tsclk->l_ui)) { - pst->reason = QTREASON + 8; - return 0; - } - - /* - * Add in the fudge - */ - if (pst->flags & PST_WWVH) - L_ADDUF(tsclk, wwvh_prop_data[pst->unit].remainder); - else - L_ADDUF(tsclk, wwv_prop_data[pst->unit].remainder); - - /* - * Glad that's over with - */ - return 1; -} - - -/* - * pst_do_event - update our status and report any changes - */ -static void -pst_do_event(pst, evnt_code) - register struct pstunit *pst; - int evnt_code; -{ - if (pst->status != (u_char)evnt_code) { - pst->status = (u_char)evnt_code; - if (evnt_code != CEVNT_NOMINAL) - pst->lastevent = (u_char)evnt_code; - /* - * Should trap this, but the trap code isn't up to - * it yet. - */ - } -} - - - -/* - * pst_process - process the data collected to produce an offset estimate - */ -static void -pst_process(pst) - register struct pstunit *pst; -{ - register int i; - register int n; - register U_LONG tmp_ui; - register U_LONG tmp_uf; - register U_LONG date_ui; - register U_LONG date_uf; - u_fp dispersion; - l_fp off[NPSTSAMPS]; - - /* - * Compute offsets from the raw data. Sort them into - * ascending order. + * 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. */ - for (i = 0; i < NPSTSAMPS; i++) { - tmp_ui = pst->reftimes[i].l_ui; - tmp_uf = pst->reftimes[i].l_uf; - M_SUB(tmp_ui, tmp_uf, pst->rectimes[i].l_ui, - pst->rectimes[i].l_uf); - for (n = i; n > 0; n--) { - if (M_ISGEQ(tmp_ui, tmp_uf, off[n-1].l_ui, - off[n-1].l_uf)) - break; - off[n] = off[n-1]; - } - off[n].l_ui = tmp_ui; - off[n].l_uf = tmp_uf; + if (pp->lencode < LENPST) { + refclock_report(peer, CEVNT_BADREPLY); + return; } /* - * Reject the furthest from the median until 8 samples left + * Timecode format: + * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx" */ - i = 0; - n = NPSTSAMPS; - while ((n - i) > 8) { - tmp_ui = off[n-1].l_ui; - tmp_uf = off[n-1].l_uf; - date_ui = off[(n+i)/2].l_ui; - date_uf = off[(n+i)/2].l_uf; - M_SUB(tmp_ui, tmp_uf, date_ui, date_uf); - M_SUB(date_ui, date_uf, off[i].l_ui, off[i].l_uf); - if (M_ISHIS(date_ui, date_uf, tmp_ui, tmp_uf)) { - /* - * reject low end - */ - i++; - } else { - /* - * reject high end - */ - n--; - } + if (sscanf(pp->lastcode, "%c%2d:%2d:%2d.%3d%c %9s%3d%13s%4ld", + &mchar, &pp->hour, &pp->minute, &pp->second, + &pp->msec, &daychar, junque, &pp->day, + info, <emp) != 10) { + refclock_report(peer, CEVNT_BADREPLY); + return; } /* - * Compute the dispersion based on the difference between the - * extremes of the remaining offsets. + * 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. */ - tmp_ui = off[n-1].l_ui; - tmp_uf = off[n-1].l_uf; - M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf); - dispersion = MFPTOFP(tmp_ui, tmp_uf); - - /* - * Now compute the offset estimate. If the sloppy clock - * flag is set, average the remainder, otherwise pick the - * median. - */ - if (sloppyclock[pst->unit]) { - tmp_ui = tmp_uf = 0; - while (i < n) { - M_ADD(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf); - i++; - } - M_RSHIFT(tmp_ui, tmp_uf); - M_RSHIFT(tmp_ui, tmp_uf); - M_RSHIFT(tmp_ui, tmp_uf); - i = 0; - off[0].l_ui = tmp_ui; - off[0].l_uf = tmp_uf; + if (info[9] != '8') { + pp->leap = LEAP_NOTINSYNC; } else { - i = (n+i)/2; - } - - /* - * Add the default PST QT delay into this. - */ - L_ADDUF(&off[i], PSTQTFUDGE); - - /* - * Set the reference ID to the appropriate station - */ - if (stratumtouse[pst->unit] <= 1) { - if (pst->station >= 0) - memmove((char *)&pst->peer->refid, WWVREFID, 4); + pp->leap = 0; + pp->lasttime = current_time - ltemp; + if (info[12] == 'H') + memcpy((char *)&pp->refid, WWVHREFID, 4); else - memmove((char *)&pst->peer->refid, WWVHREFID, 4); + memcpy((char *)&pp->refid, WWVREFID, 4); + if (peer->stratum <= 1) + peer->refid = pp->refid; } /* - * Give the data to the reference clock support code - */ - record_clock_stats(&(pst->peer->srcadr), pst->lastcode); - refclock_receive(pst->peer, &off[i], 0, dispersion, &pst->reftimes[NPSTSAMPS-1], - &pst->rectimes[NPSTSAMPS-1], pst->leap); - - /* - * If the don't-sync flag isn't on, we're nominal. - */ - if (pst->leap == 0) - pst_event(pst, CEVNT_NOMINAL); - pst_reset(pst); -} - - - -/* - * pst_receive - receive data from a PST clock, call the appropriate - * routine to process it, and advance the state. - */ -static void -pst_receive(rbufp) - struct recvbuf *rbufp; -{ - register struct pstunit *pst; - register U_LONG tmp; - - pst = (struct pstunit *)rbufp->recv_srcclock; - - /* - * Process based on the current state. - */ - switch(pst->state) { - case STATE_IDLE: - return; /* Ignore the input */ - - case STATE_QV: - if (!pst_QV_process(pst, rbufp)) { - /* - * Set the state to idle, but request another - * QV poll. - */ - pst->badformat++; - pst_event(pst, CEVNT_BADREPLY); - pst->state = STATE_IDLE; - pst->flags |= PST_DOQV; - } else { - /* - * This went okay. Advance the state to - * QM and send the request. - */ - pst->state = STATE_QM; - pst_send(pst, "QM", 2); - } - return; - - case STATE_QM: - if (!pst_QM_process(pst, rbufp)) { - /* - * Idle us and note the error - */ - pst->badformat++; - pst_event(pst, CEVNT_BADREPLY); - pst->state = STATE_IDLE; - return; - } - if (pst->flags & PST_NOTIME) { - /* - * Here we aren't getting any time because the - * clock is still searching. Don't bother - * looking for anything. Remove any leap - * second hold, however, since this should - * ensure the clock is sensible. - */ - pst_event(pst, CEVNT_FAULT); - pst->state = STATE_IDLE; - if (pst->nextsample > 0) - pst_reset(pst); /* Make sure rate low */ - return; - } - - /* - * Next is QD. Do it. - */ - pst->state = STATE_QD; - pst_send(pst, "QD", 2); - return; - - case STATE_QD: - if (!pst_QD_process(pst, rbufp)) { - /* - * Idle us and note the error - */ - pst->badformat++; - pst_event(pst, CEVNT_BADDATE); - pst->state = STATE_IDLE; - } else { - /* - * Last step is QT. - */ - pst->state = STATE_QT; - pst_send(pst, "QT", 2); - } - return; - - case STATE_QT: - pst->state = STATE_IDLE; - if (!pst_QT_process(pst, rbufp, &pst->lastref, &pst->lastrec)) { - /* - * Note the error - */ - pst->baddata++; - pst_event(pst, CEVNT_BADTIME); - return; - } - break; - - default: - syslog(LOG_ERR, - "pst_receive: unit %d invalid state %d", - pst->unit, pst->state); + * 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, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); return; } - - - /* - * You may not have noticed this, but the only way we end up - * out here is if we've completed polling and have a couple of - * valid time stamps. First see if we should reset the - * structure. - */ - if (pst->nextsample > 0) { - tmp = pst->lastrec.l_ui - pst->rectimes[0].l_ui; - if (tmp > (U_LONG)psttab[pst->nextsample].tooold) - pst_reset(pst); - } - - pst->rectimes[pst->nextsample] = pst->lastrec; - pst->reftimes[pst->nextsample] = pst->lastref; - pst->nextsample++; - if (pst->flags & PST_WWVH) - pst->station--; - else - pst->station++; - - if (pst->flags & (PST_SIGFAULT|PST_HARDERR)) { - pst_event(pst, CEVNT_FAULT); - pst->leap = LEAP_NOTINSYNC; - } else if (pst->timesincesync > freerun[pst->unit]) { - pst_event(pst, CEVNT_PROP); - pst->leap = LEAP_NOTINSYNC; - } - - if (pst->nextsample >= NPSTSAMPS) - pst_process(pst); + trtmp = pp->lastrec; + trtmp.l_ui -= ltemp; + refclock_receive(peer, &pp->offset, 0, pp->dispersion, &trtmp, + &pp->lastrec, pp->leap); } /* - * pst_compute_delay - compute appropriate things to tell clock about delays + * pst_poll - called by the transmit procedure */ static void -pst_compute_delay(prop_delay, prop_data) - U_LONG prop_delay; - struct pst_propagate *prop_data; +pst_poll(unit, peer) + int unit; + struct peer *peer; { - register int code; - register U_LONG tsf; + register struct pstunit *up; + struct refclockproc *pp; /* - * Convert (truncate) the delay to milliseconds. Save the - * characters needed to send this to the clock and compute - * the remainder to be added in later. + * 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. */ - code = tsftomsu(prop_delay, 0); - MSUTOTSF(code, tsf); - prop_data->remainder = prop_delay - tsf; - if (prop_data->remainder & 0x80000000) - prop_data->remainder = 0; - prop_data->msbchar = BINTOPST((code >> 2) & 0x1f); - prop_data->lsbchar = BINTOPST(code & 0x3); -} - - -/* - * pst_control - set fudge factors, return statistics - */ -static void -pst_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - register struct pstunit *pst; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "pst_control: unit %d invalid", unit); - return; - } - - if (in != 0) { - int doqv = 0; - - if (in->haveflags & CLK_HAVETIME1) - if (in->fudgetime1.l_ui == 0 - && in->fudgetime1.l_uf <= PSTMAXPROP) { - wwv_prop_delay[unit] = in->fudgetime1; - doqv = 1; - pst_compute_delay(wwv_prop_delay[unit].l_uf, - &wwv_prop_data[unit]); - } - if (in->haveflags & CLK_HAVETIME2) - if (in->fudgetime2.l_ui == 0 - && in->fudgetime2.l_uf <= PSTMAXPROP) { - wwvh_prop_delay[unit] = in->fudgetime2; - doqv = 1; - pst_compute_delay(wwvh_prop_delay[unit].l_uf, - &wwvh_prop_data[unit]); - } - if (in->haveflags & CLK_HAVEVAL1) { - stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf); - } - if (in->haveflags & CLK_HAVEVAL2) { - if (in->fudgeval2 > 0 && in->fudgeval2 < 9990) - freerun[unit] = (u_short)in->fudgeval2; - } - if (in->haveflags & CLK_HAVEFLAG1) { - sloppyclock[unit] = in->flags & CLK_FLAG1; - } - if (unitinuse[unit]) { - /* - * Should actually reselect clock, but - * will wait for the next timecode - */ - if (in->haveflags & CLK_HAVEVAL1) { - pstunits[unit]->peer->stratum - = stratumtouse[unit]; - if (stratumtouse[unit] > 1) - pstunits[unit]->peer->refid - = htonl(PSTHSREFID); - } - - if ((in->haveflags & CLK_HAVEFLAG3) && - (in->flags & CLK_FLAG3)) { - pstunits[unit]->flags |= PST_DORESET; - } else if (doqv || ((in->haveflags & CLK_HAVEFLAG2) && - (in->flags & CLK_FLAG2))) { - pstunits[unit]->flags |= PST_DOQV; - } - } - } - - if (out != 0) { - out->type = REFCLK_WWV_PST; - out->flags = 0; - out->haveflags - = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1| - CLK_HAVEVAL2|CLK_HAVEFLAG1; - out->fudgetime1 = wwv_prop_delay[unit]; - out->fudgetime2 = wwvh_prop_delay[unit]; - out->fudgeval1 = (LONG)stratumtouse[unit]; - out->fudgeval2 = (LONG)freerun[unit]; - out->flags = sloppyclock[unit]; - if (unitinuse[unit]) { - pst = pstunits[unit]; - out->clockdesc = pst->description; - out->lencode = pst->lencode; - out->lastcode = pst->lastcode; - out->timereset = current_time - pst->timestarted; - out->polls = pst->polls; - out->noresponse = pst->noreply; - out->badformat = pst->badformat; - out->baddata = pst->baddata; - out->lastevent = pst->lastevent; - out->currentstatus = pst->status; - } else { - out->clockdesc = pstdefdesc; - out->lencode = 0; - out->lastcode = ""; - out->polls = out->noresponse = 0; - out->badformat = out->baddata = 0; - out->timereset = 0; - out->currentstatus = out->lastevent = CEVNT_NOMINAL; - } - } + pp = peer->procptr; + up = (struct pstunit *)pp->unitptr; + if (up->pollcnt == 0) + refclock_report(peer, CEVNT_TIMEOUT); + else + up->pollcnt--; + up->tcswitch = 0; + up->lastptr = pp->lastcode; + if (write(pp->io.fd, "QTQDQMT", 6) != 6) { + refclock_report(peer, CEVNT_FAULT); + } else + pp->polls++; } - -/* - * pst_buginfo - return clock dependent debugging info - */ -static void -pst_buginfo(unit, bug) - int unit; - register struct refclockbug *bug; -{ - register struct pstunit *pst; - register int i; - - bug->nvalues = bug->ntimes = 0; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "pst_buginfo: unit %d invalid", unit); - return; - } - - if (!unitinuse[unit]) - return; - pst = pstunits[unit]; - - bug->nvalues = 14; - bug->svalues = (1<<10); - bug->values[0] = (U_LONG)pst->nextsample; - bug->values[1] = (U_LONG)pst->state; - bug->values[2] = (U_LONG)pst->reason; - bug->values[3] = (U_LONG)pst->flags; - bug->values[4] = (U_LONG)pst->yearday; - bug->values[5] = (U_LONG)pst->hour; - bug->values[6] = (U_LONG)pst->minute; - bug->values[7] = (U_LONG)pst->second; - bug->values[8] = (U_LONG)pst->millisecond; - bug->values[9] = (U_LONG)pst->timezone; - bug->values[10] = (U_LONG)((LONG)pst->tzoffset); - bug->values[11] = (U_LONG)pst->timesincesync; - bug->values[12] = pst->yearstart; - bug->ntimes = ((NPSTSAMPS*2)+2) > NCLKBUGTIMES ? NCLKBUGTIMES : - ((NPSTSAMPS*2)+2); - bug->stimes = 0; - for (i = 0; i < (bug->ntimes-2)/2; i++) { - bug->times[2*i] = pst->rectimes[i]; - bug->times[(2*i) + 1] = pst->reftimes[i]; - } - bug->times[bug->ntimes - 2] = pst->lastrec; - bug->times[bug->ntimes - 1] = pst->lastref; -} #endif diff --git a/usr.sbin/xntpd/xntpd/refclock_tpro.c b/usr.sbin/xntpd/xntpd/refclock_tpro.c index 54d9f3cb5287..388103c7c1c6 100644 --- a/usr.sbin/xntpd/xntpd/refclock_tpro.c +++ b/usr.sbin/xntpd/xntpd/refclock_tpro.c @@ -1,7 +1,8 @@ /* - * refclock_tpro - clock driver for the KSI/Odetics TPRO-S IRIG-B reader + * refclock_tpro - clock driver for the KSI/Odetics TPRO-S IRIG-B reader */ -#if defined(REFCLOCK) && defined(TPRO) +#if defined(REFCLOCK) && defined(TPRO) && defined(sun) + #include <stdio.h> #include <ctype.h> #include <sys/time.h> @@ -20,31 +21,19 @@ */ /* - * Definitions + * TPRO interface definitions */ -#define MAXUNITS 1 /* max number of TPRO units */ -#define TPROFD "/dev/tpro%d" /* name of driver device */ -#define BMAX 50 /* timecode buffer length */ +#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 */ -/* - * TPRO interface parameters. The "IRIG" can be changed to "GPS" for the - * TPRO-GPS. - */ -#define TPROPRECISION (-20) /* precision assumed (1 us) */ -#define TPROREFID "IRIG" /* reference id */ -#define TPRODESCRIPTION "KSI/Odetics TPRO-S IRIG-B Reader" /* who we are */ -#define TPROHSREFID 0x7f7f0c0a /* 127.127.12.10 refid hi strata */ -#define GMT 0 /* hour offset from Greenwich */ - -/* - * Hack to avoid excercising the multiplier. I have no pride. - */ -#define MULBY10(x) (((x)<<3) + ((x)<<1)) +#define NSAMPLES 3 /* stages of median filter */ /* * Imported from ntp_timer module */ -extern U_LONG current_time; /* current time (s) */ +extern u_long current_time; /* current time (s) */ /* * Imported from ntpd module @@ -52,250 +41,106 @@ extern U_LONG current_time; /* current time (s) */ extern int debug; /* global debug flag */ /* - * TPRO unit control structure. + * Unit control structure */ struct tprounit { - struct peer *peer; /* associated peer structure */ - struct refclockio io; /* given to the I/O handler */ - struct tproval tprodata; /* data returned from tpro read */ - l_fp lastrec; /* last local time */ - l_fp lastref; /* last timecode time */ - char lastcode[BMAX]; /* last timecode received */ - u_char lencode; /* length of last timecode */ - U_LONG lasttime; /* last time clock heard from */ - u_char unit; /* unit number for this guy */ - u_char status; /* clock status */ - u_char lastevent; /* last clock event */ - u_char year; /* year of eternity */ - u_short day; /* day of year */ - u_char hour; /* hour of day */ - u_char minute; /* minute of hour */ - u_char second; /* seconds of minute */ - U_LONG usec; /* microsecond of second */ - U_LONG yearstart; /* start of current year */ - u_char 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 */ + struct tproval tprodata; /* data returned from tpro read */ }; /* - * Data space for the unit structures. Note that we allocate these on - * the fly, but never give them back. - */ -static struct tprounit *tprounits[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 tpro_init P(()); -static int tpro_start P((u_int, struct peer *)); -static void tpro_shutdown P((int)); -static void tpro_report_event P((struct tprounit *, int)); -static void tpro_receive P((struct recvbuf *)); +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 *)); -static void tpro_control P((u_int, struct refclockstat *, struct refclockstat *)); -static void tpro_buginfo P((int, struct refclockbug *)); /* * Transfer vector */ -struct refclock refclock_tpro = { - tpro_start, tpro_shutdown, tpro_poll, - tpro_control, tpro_init, tpro_buginfo, NOFLAGS +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_init - initialize internal tpro driver data - */ -static void -tpro_init() -{ - register int i; - /* - * Just zero the data arrays - */ - memset((char *)tprounits, 0, sizeof tprounits); - memset((char *)unitinuse, 0, 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; - } -} /* * tpro_start - open the TPRO device and initialize data for processing */ static int tpro_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { - register struct tprounit *tpro; - register int i; - char tprodev[20]; - int fd_tpro; + register struct tprounit *up; + struct refclockproc *pp; + char device[20]; + int fd; /* - * Check configuration info. + * Open TPRO device */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "tpro_start: unit %d invalid", unit); - return (0); - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "tpro_start: unit %d in use", unit); + (void)sprintf(device, DEVICE, unit); + fd = open(device, O_RDONLY | O_NDELAY, 0777); + if (fd == -1) { + syslog(LOG_ERR, "tpro_start: open of %s: %m", device); return (0); } /* - * Open TPRO device + * Allocate and initialize unit structure */ - (void) sprintf(tprodev, TPROFD, unit); - fd_tpro = open(tprodev, O_RDWR, 0777); - if (fd_tpro == -1) { - syslog(LOG_ERR, "tpro_start: open of %s: %m", tprodev); + if (!(up = (struct tprounit *) + emalloc(sizeof(struct tprounit)))) { + (void) close(fd); return (0); } - - /* - * Allocate unit structure - */ - if (tprounits[unit] != 0) { - tpro = tprounits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < MAXUNITS; i++) { - if (!unitinuse[i] && tprounits[i] != 0) - break; - } - if (i < MAXUNITS) { - /* - * Reclaim this one - */ - tpro = tprounits[i]; - tprounits[i] = 0; - } else { - tpro = (struct tprounit *) - emalloc(sizeof(struct tprounit)); - } + 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; + if (!io_addclock(&pp->io)) { + (void) close(fd); + free(up); + return (0); } - memset((char *)tpro, 0, sizeof(struct tprounit)); - tprounits[unit] = tpro; - - /* - * Set up the structures - */ - tpro->peer = peer; - tpro->unit = (u_char)unit; - tpro->timestarted = current_time; - - tpro->io.clock_recv = tpro_receive; - tpro->io.srcclock = (caddr_t)tpro; - tpro->io.datalen = 0; - tpro->io.fd = fd_tpro; + pp->unitptr = (caddr_t)up; /* - * All done. Initialize a few random peer variables, then - * return success. Note that root delay and root dispersion are - * always zero for this clock. + * Initialize miscellaneous peer variables */ - peer->precision = TPROPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = 0; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, TPROREFID, 4); - else - peer->refid = htonl(TPROHSREFID); - unitinuse[unit] = 1; + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); return (1); } /* - * tpro_shutdown - shut down a TPRO clock + * tpro_shutdown - shut down the clock */ static void -tpro_shutdown(unit) +tpro_shutdown(unit, peer) int unit; -{ - register struct tprounit *tpro; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "tpro_shutdown: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "tpro_shutdown: unit %d not in use", unit); - return; - } - - /* - * Tell the I/O module to turn us off. We're history. - */ - tpro = tprounits[unit]; - io_closeclock(&tpro->io); - unitinuse[unit] = 0; -} - -/* - * tpro_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 -tpro_report_event(tpro, code) - struct tprounit *tpro; - int code; -{ struct peer *peer; +{ + register struct tprounit *up; + struct refclockproc *pp; - peer = tpro->peer; - if (tpro->status != (u_char)code) { - tpro->status = (u_char)code; - if (code != CEVNT_NOMINAL) - tpro->lastevent = (u_char)code; - syslog(LOG_INFO, - "clock %s event %x", ntoa(&peer->srcadr), code); - } + pp = peer->procptr; + up = (struct tprounit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); } /* - * tpro_receive - receive data from the TPRO 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 -tpro_receive(rbufp) - struct recvbuf *rbufp; -{ -} - -/* * tpro_poll - called by the transmit procedure */ static void @@ -303,196 +148,80 @@ tpro_poll(unit, peer) int unit; struct peer *peer; { - struct tprounit *tpro; - struct tproval *tptr; - l_fp tstmp; + register struct tprounit *up; + struct refclockproc *pp; + struct tproval *tp; - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "tpro_poll: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "tpro_poll: unit %d not in use", unit); - return; - } - tpro = tprounits[unit]; - tpro->polls++; + /* + * 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; - tptr = &tpro->tprodata; - if (read(tpro->io.fd, (char *)tptr, sizeof(struct tproval)) < 0) { - tpro_report_event(tpro, CEVNT_BADREPLY); + tp = &up->tprodata; + if (read(pp->io.fd, (char *)tp, sizeof(struct tproval)) < 0) { + refclock_report(peer, CEVNT_FAULT); return; } - gettstamp(&tpro->lastrec); - tpro->lasttime = current_time; + gettstamp(&pp->lastrec); + pp->lasttime = current_time; + pp->polls++; /* - * Get TPRO time and convert to timestamp format. Note: we + * 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. + * since the year may be suspect. All format error checking is + * done by the sprintf() and sscanf() routines. */ - sprintf(tpro->lastcode, + if (sprintf(pp->lastcode, "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x", - tptr->day100, tptr->day10, tptr->day1, tptr->hour10, tptr->hour1, - tptr->min10, tptr->min1, tptr->sec10, tptr->sec1, - tptr->ms100, tptr->ms10, tptr->ms1, tptr->usec100, tptr->usec10, - tptr->usec1, tptr->status); - record_clock_stats(&(tpro->peer->srcadr), tpro->lastcode); - tpro->lencode = strlen(tpro->lastcode); + 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)) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } - tpro->day = MULBY10(MULBY10(tptr->day100) + tptr->day10) + tptr->day1; - tpro->hour = MULBY10(tptr->hour10) + tptr->hour1; - tpro->minute = MULBY10(tptr->min10) + tptr->min1; - tpro->second = MULBY10(tptr->sec10) + tptr->sec1; - tpro->usec = MULBY10(MULBY10(tptr->ms100) + tptr->ms10) + tptr->ms1; - tpro->usec = tpro->usec * 10 + tptr->usec100; - tpro->usec = tpro->usec * 10 + tptr->usec10; - tpro->usec = tpro->usec * 10 + tptr->usec1; #ifdef DEBUG if (debug) - printf("tpro: %3d %02d:%02d:%02d.%06ld %1x\n", - tpro->day, tpro->hour, tpro->minute, tpro->second, - tpro->usec, tptr->status); + printf("tpro: time %s timecode %d %s\n", + ulfptoa(&pp->lastrec, 6), pp->lencode, + pp->lastcode); #endif - if (tptr->status != 0xff) { - tpro_report_event(tpro, CEVNT_BADREPLY); + record_clock_stats(&peer->srcadr, pp->lastcode); + if (sscanf(pp->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 != 0xff) { + pp->leap = LEAP_NOTINSYNC; + refclock_report(peer, CEVNT_FAULT); return; + } else { + pp->leap = 0; + pp->lasttime = current_time; } /* - * 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(tpro->day, tpro->hour, tpro->minute, - tpro->second, GMT, tpro->lastrec.l_ui, - &tpro->yearstart, &tpro->lastref.l_ui)) { - tpro->baddata++; - tpro_report_event(tpro, CEVNT_BADTIME); - return; - } - TVUTOTSF(tpro->usec, tpro->lastref.l_uf); - tstmp = tpro->lastref; - L_SUB(&tstmp, &tpro->lastrec); - tpro->coderecv++; - L_ADD(&tstmp, &(fudgefactor[tpro->unit])); - refclock_receive(tpro->peer, &tstmp, GMT, 0, - &tpro->lastrec, &tpro->lastrec, tpro->leap); -} - -/* - * tpro_control - set fudge factors, return statistics - */ -static void -tpro_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - register struct tprounit *tpro; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "tpro_control: unit %d invalid)", unit); + * 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, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); 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 - */ - tpro = tprounits[unit]; - peer = tpro->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, - TPROREFID, 4); - else - peer->refid = htonl(TPROHSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG1) { - sloppyclockflag[unit] = in->flags & CLK_FLAG1; - } - } - - if (out != 0) { - out->type = REFCLK_IRIG_TPRO; - out->haveflags - = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1; - out->clockdesc = TPRODESCRIPTION; - 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]) { - tpro = tprounits[unit]; - out->lencode = tpro->lencode; - out->lastcode = tpro->lastcode; - out->timereset = current_time - tpro->timestarted; - out->polls = tpro->polls; - out->noresponse = tpro->noreply; - out->badformat = tpro->badformat; - out->baddata = tpro->baddata; - out->lastevent = tpro->lastevent; - out->currentstatus = tpro->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; - } - } + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->leap); } -/* - * tpro_buginfo - return clock dependent debugging info - */ -static void -tpro_buginfo(unit, bug) - int unit; - register struct refclockbug *bug; -{ - register struct tprounit *tpro; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "tpro_buginfo: unit %d invalid)", unit); - return; - } - - if (!unitinuse[unit]) - return; - tpro = tprounits[unit]; - - bug->nvalues = 11; - bug->ntimes = 5; - if (tpro->lasttime != 0) - bug->values[0] = current_time - tpro->lasttime; - else - bug->values[0] = 0; - bug->values[2] = (U_LONG)tpro->year; - bug->values[3] = (U_LONG)tpro->day; - bug->values[4] = (U_LONG)tpro->hour; - bug->values[5] = (U_LONG)tpro->minute; - bug->values[6] = (U_LONG)tpro->second; - bug->values[7] = (U_LONG)tpro->usec; - bug->values[9] = tpro->yearstart; - bug->stimes = 0x1c; - bug->times[0] = tpro->lastref; - bug->times[1] = tpro->lastrec; -} #endif diff --git a/usr.sbin/xntpd/xntpd/refclock_trak.c b/usr.sbin/xntpd/xntpd/refclock_trak.c index f2b3eb11fc9d..d10d75234fbd 100644 --- a/usr.sbin/xntpd/xntpd/refclock_trak.c +++ b/usr.sbin/xntpd/xntpd/refclock_trak.c @@ -1,11 +1,10 @@ /* - * refclock_trak.c - clock driver for the TRAK 8810 GPS STATION CLOCK - * Tsuruoka Tomoaki Oct 30, 1993 - * tsuruoka@nc.fukuoka-u.ac.jp - * Faculty of Engineering, - * Fukuoka University, Fukuoka, JAPAN + * refclock_trak - clock driver for the TRAK 8820 GPS Station Clock + * + * Thanks to Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp> for the + * previous version from which this one was developed. */ -#if defined(REFCLOCK) && (defined(TRAK) || defined(TRAKCLK) || defined(TRAKPPS)) +#if defined(REFCLOCK) && defined(TRAK) #include <stdio.h> #include <ctype.h> @@ -14,494 +13,201 @@ #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" -#include "ntp_unixtime.h" - -static void gps_send(); - -#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(STREAM) -#include <termios.h> -#include <stropts.h> -#if defined(TRAKCLK) -#include <sys/clkdefs.h> -#endif /* TRAKCLK */ -#endif /* STREAM */ - -#if defined (TRAKPPS) -#include <sys/ppsclock.h> -#endif /* TRAKPPS */ - #include "ntp_stdlib.h" /* - * This driver supports the TRAK 8810 GPS Receiver with - * Buffered RS-232-C Interface Module. + * This driver supports the TRAK 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. * - * Most of codes are copied from refclock_as2201.c, Thanks a lot. + * 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. * - * The program expects the radio responses once per seccond - * ( by "rqts,u" command or panel control ) - * of the form "*RQTS U,ddd:hh:mm:ss.0,Q\r\n for UTC" where - * ddd= day of year - * hh= hours - * mm= minutes - * ss= seconds - * Q= Quality byte. Q=0 Phase error > 20 us - * Q=6 Pahse error < 20 us - * > 10 us - * Q=5 Pahse error < 10 us - * > 1 us - * Q=4 Pahse error < 1 us - * > 100 ns - * Q=3 Pahse error < 100 ns - * > 10 ns - * Q=2 Pahse error < 10 ns - * (note that my clock almost stable at 1 us per 10 hours) + * 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. * - * Request leap second status - if needed. - * send: rqls\n - * reply: RQLS yy,mm,dd - * where: yy is year - * mm is month - * dd is day of month.baud - * Note: Default data is all zeros - * i.e. RQLS 00,00,00 - */ - -/* - * Definitions + * 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. */ -#define MAXUNITS 4 /* max number of GPS units */ -#define GPS232 "/dev/gps%d" /* name of radio device */ -#define SPEED232 B9600 /* uart speed (9600 bps) */ /* - * Radio interface parameters + * Interface definitions */ -#define GPSPRECISION (-20) /* precision assumed (about 1 us) */ -#define GPSREFID "GPS" /* reference id */ -#define GPSDESCRIPTION "TRAK 8810 GPS station clock" /* who we are */ -#define GPSHSREFID 0x7f7f020a /* 127.127.2.10 refid hi strata */ -#define GMT 0 /* hour offset from Greenwich */ -#define NCODES 3 /* stages of median filter */ -#define LENTOC 25 /* *RQTS U,ddd:hh:mm:ss.0,Q datecode length */ -#define BMAX 100 /* timecode buffer length */ -#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */ +#define DEVICE "/dev/trak%d" /* device name and unit */ +#define SPEED232 B9600 /* uart speed (9600 baud) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ +#define REFID "TRAK" /* reference ID */ +#define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */ -/* - * Hack to avoid excercising the multiplier. I have no pride. - */ -#define MULBY10(x) (((x)<<3) + ((x)<<1)) +#define NSAMPLES 3 /* stages of median filter */ +#define LENTRAK 24 /* timecode length */ +#define C_CTO "RQTS\r" /* start continuous time output */ /* * Imported from ntp_timer module */ -extern U_LONG current_time; /* current time (s) */ - -/* - * Imported from ntp_loopfilter module - */ -extern int fdpps; /* pps file descriptor */ +extern u_long current_time; /* current time (s) */ /* * Imported from ntpd module */ -extern int debug; /* global debug flag */ +extern int debug; /* global debug flag */ /* - * GPS unit control structure. + * Unit control structure */ -struct gpsunit { - struct peer *peer; /* associated peer structure */ - struct refclockio io; /* given to the I/O handler */ - l_fp lastrec; /* last data receive time */ - l_fp lastref; /* last timecode time */ - l_fp offset[NCODES]; /* recent sample offsets */ - char lastcode[BMAX]; /* last timecode received */ - u_short polled; /* when polled, means a last sample */ - u_char lencode; /* length of last received ASCII string */ - U_LONG lasttime; /* last time clock heard from */ -#ifdef TRAKPPS - U_LONG lastev; /* last ppsclock second */ -#endif /* TRAKPPS */ - 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 year; /* year of eternity */ - u_short day; /* day of year */ - u_char hour; /* hour of day */ - u_char minute; /* minute of hour */ - u_char second; /* seconds of minute */ - u_short msec; /* milliseconds of second */ - u_char leap; /* leap indicators */ - U_LONG yearstart; /* start of current year */ - /* - * 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 */ -}; - +struct wwvbunit { + int pollcnt; /* poll message counter */ -/* - * Data space for the unit structures. Note that we allocate these on - * the fly, but never give them back. - */ -static struct gpsunit *gpsunits[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]; + u_char tcswitch; /* timecode switch */ + char qualchar; /* quality indicator */ +}; /* * Function prototypes */ -static void trak_init P(()); -static int trak_start P((u_int, struct peer *)); -static void trak_shutdown P((int)); -static void trak_report_event P((struct gpsunit *, int)); +static int trak_start P((int, struct peer *)); +static void trak_shutdown P((int, struct peer *)); static void trak_receive P((struct recvbuf *)); -static char trak_process P((struct gpsunit *, l_fp *, u_fp *)); -static void trak_poll P((int unit, struct peer *)); -static void trak_control P((u_int, struct refclockstat *, struct refclockstat *)); -static void trak_buginfo P((int, struct refclockbug *)); +static void trak_poll P((int, struct peer *)); /* * Transfer vector */ -struct refclock refclock_trak = { - trak_start, trak_shutdown, trak_poll, - trak_control, trak_init, trak_buginfo, NOFLAGS +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_init - initialize internal gps driver data - */ -static void -trak_init() -{ - register int i; - /* - * Just zero the data arrays - */ - memset((char *)gpsunits, 0, sizeof gpsunits); - memset((char *)unitinuse, 0, 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; - } -} - /* - * trak_start - open the GPS devices and initialize data for processing + * trak_start - open the devices and initialize data for processing */ static int trak_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { - register struct gpsunit *gps; - register int i; - int fd232; - char trakdev[20]; -#ifdef TRAKPPS - struct ppsclockev ev; -#endif /* TRAKPPS */ + register struct wwvbunit *up; + struct refclockproc *pp; + int fd; + char device[20]; /* - * Check configuration info + * Open serial port. The LDISC_ACTS line discipline inserts a + * timestamp following the "*" on-time character of the + * timecode. */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "trak_start: unit %d invalid", unit); - return (0); - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "trak_start: unit %d in use", unit); + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) return (0); - } /* - * Open serial port + * Allocate and initialize unit structure */ - (void) sprintf(trakdev, GPS232, unit); - fd232 = open(trakdev, O_RDWR, 0777); - if (fd232 == -1) { - syslog(LOG_ERR, "trak_start: open of %s: %m", trakdev); + if (!(up = (struct wwvbunit *) + emalloc(sizeof(struct wwvbunit)))) { + (void) close(fd); return (0); } - -#if defined(HAVE_SYSV_TTYS) - /* - * System V serial line parameters (termio interface) - * - */ - { struct termio ttyb; - if (ioctl(fd232, TCGETA, &ttyb) < 0) { - syslog(LOG_ERR, - "trak_start: ioctl(%s, TCGETA): %m", trakdev); - 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) { - syslog(LOG_ERR, - "trak_start: ioctl(%s, TCSETA): %m", trakdev); - goto screwed; - } - } -#endif /* HAVE_SYSV_TTYS */ -#if defined(STREAM) - /* - * POSIX/STREAMS serial line parameters (termios interface) - * - * The TRAKCLK option provides timestamping at the driver level. - * It requires the tty_clk streams module. - * - * The TRAKPPS 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 SunOS 4.1.1 or - * later. - */ - { struct termios ttyb, *ttyp; - - ttyp = &ttyb; - if (tcgetattr(fd232, ttyp) < 0) { - syslog(LOG_ERR, - "trak_start: tcgetattr(%s): %m", trakdev); - goto screwed; - } - ttyp->c_iflag = IGNBRK|IGNPAR; - 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) { - syslog(LOG_ERR, - "trak_start: tcsetattr(%s): %m", trakdev); - goto screwed; - } - if (tcflush(fd232, TCIOFLUSH) < 0) { - syslog(LOG_ERR, - "trak_start: tcflush(%s): %m", trakdev); - goto screwed; - } -#if defined(TRAKCLK) - if (ioctl(fd232, I_PUSH, "clk") < 0) - syslog(LOG_ERR, - "trak_start: ioctl(%s, I_PUSH, clk): %m", trakdev); - if (ioctl(fd232, CLK_SETSTR, "*") < 0) - syslog(LOG_ERR, - "trak_start: ioctl(%s, CLK_SETSTR): %m", trakdev); -#endif /* TRAKCLK */ -#if defined(TRAKPPS) - if (ioctl(fd232, I_PUSH, "ppsclock") < 0) - syslog(LOG_ERR, - "trak_start: ioctl(%s, I_PUSH, ppsclock): %m", trakdev); - else - fdpps = fd232; -#endif /* TRAKPPS */ - } -#endif /* STREAM */ -#if defined(HAVE_BSD_TTYS) - /* - * 4.3bsd serial line parameters (sgttyb interface) - * - * The TRAKCLK option provides timestamping at the driver level. - * It requires the tty_clk line discipline and 4.3bsd or later. - */ - { struct sgttyb ttyb; -#if defined(TRAKCLK) - int ldisc = CLKLDISC; -#endif /* TRAKCLK */ - - if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { - syslog(LOG_ERR, - "trak_start: ioctl(%s, TIOCGETP): %m", trakdev); - goto screwed; - } - ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; -#if defined(TRAKCLK) - 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 /* TRAKCLK */ - if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { - syslog(LOG_ERR, - "trak_start: ioctl(%s, TIOCSETP): %m", trakdev); - goto screwed; - } -#if defined(TRAKCLK) - if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { - syslog(LOG_ERR, - "trak_start: ioctl(%s, TIOCSETD): %m",trakdev); - goto screwed; - } -#endif /* TRAKCLK */ - } -#endif /* HAVE_BSD_TTYS */ - - /* - * Allocate unit structure - */ - if (gpsunits[unit] != 0) { - gps = gpsunits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < MAXUNITS; i++) { - if (!unitinuse[i] && gpsunits[i] != 0) - break; - } - if (i < MAXUNITS) { - /* - * Reclaim this one - */ - gps = gpsunits[i]; - gpsunits[i] = 0; - } else { - gps = (struct gpsunit *) - emalloc(sizeof(struct gpsunit)); - } + memset((char *)up, 0, sizeof(struct wwvbunit)); + 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); } - bzero((char *)gps, sizeof(struct gpsunit)); - gpsunits[unit] = gps; + pp->unitptr = (caddr_t)up; /* - * Set up the structures + * Initialize miscellaneous variables */ - gps->peer = peer; - gps->unit = (u_char)unit; - gps->timestarted = current_time; - - gps->io.clock_recv = trak_receive; - gps->io.srcclock = (caddr_t)gps; - gps->io.datalen = 0; - gps->io.fd = fd232; -#ifdef TRAKPPS - if (ioctl(fd232, CIOGETEV, (caddr_t)&ev) < 0) { - syslog(LOG_ERR, - "trak_start: ioctl(%s, CIOGETEV): %m", trakdev); - goto screwed; - } else - gps->lastev = ev.tv.tv_sec; -#endif /* TRAKPPS */ - if (!io_addclock(&gps->io)) { - goto screwed; - } + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; /* - * All done. Initialize a few random peer variables, then - * return success. Note that root delay and root dispersion are - * always zero for this clock. - */ - peer->precision = GPSPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = 0; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - bcopy(GPSREFID, (char *)&peer->refid, 4); - else - peer->refid = htonl(GPSHSREFID); - unitinuse[unit] = 1; - /* - * request to give time code + * Start continuous time output. If something breaks, fold the + * tent and go home. */ - { - void gps_send(); - gps_send(gps,"\rRQTS,U\r"); - gps_send(gps,"SEL 00\r"); + 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); - - /* - * Something broke; abandon ship. - */ -screwed: - (void) close(fd232); - return (0); -} - -/* - * trak_shutdown - shut down a GPS clock - */ -static void -trak_shutdown(unit) - int unit; -{ - register struct gpsunit *gps; - void gps_send(); - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "trak_shutdown: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "trak_shutdown: unit %d not in use", unit); - return; - } - gps = gpsunits[unit]; - /* - * request not to give time code any more - */ - gps_send(gps,"RQTX\r"); - /* - * Tell the I/O module to turn us off. We're history. - */ - io_closeclock(&gps->io); - - unitinuse[unit] = 0; } /* - * trak_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. + * trak_shutdown - shut down the clock */ static void -trak_report_event(gps, code) - struct gpsunit *gps; - int code; -{ +trak_shutdown(unit, peer) + int unit; struct peer *peer; +{ + register struct wwvbunit *up; + struct refclockproc *pp; - peer = gps->peer; - if (gps->status != (u_char)code) { - gps->status = (u_char)code; - if (code != CEVNT_NOMINAL) - gps->lastevent = (u_char)code; - syslog(LOG_INFO, - "clock %s event %x\n", ntoa(&peer->srcadr), code); - } + pp = peer->procptr; + up = (struct wwvbunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); } @@ -512,495 +218,122 @@ static void trak_receive(rbufp) struct recvbuf *rbufp; { - register int i,cmdtype; - register struct gpsunit *gps; - -#if defined(TRAKPPS) - struct ppsclockev ev; + register struct wwvbunit *up; + struct refclockproc *pp; + struct peer *peer; l_fp trtmp; -#endif /* TRAKPPS */ - register u_char *dpt; - register u_char *cp; - register u_char *dpend; - l_fp tstmp; - u_fp dispersion; + char *dpt, *dpend; + char qchar; /* - * Get the clock this applies to and pointers to the data. - * Edit the timecode to remove control chars and trashbits. + * Initialize pointers and read the timecode and timestamp. We + * then chuck out everything, including runts, except one + * message each poll interval. */ - gps = (struct gpsunit *)rbufp->recv_srcclock; - dpt = (u_char *)&rbufp->recv_space; - dpend = dpt + rbufp->recv_length; - cp = (u_char *)gps->lastcode; - - while (dpt < dpend) { -#ifdef TRAKCLK /* prior to TRAKPPS due to timestamp */ - if ((*cp = 0x7f & *dpt++) != '*' ) cp++; - else if (*cp == '*' ) { /* caught magic character */ - if ( dpend - dpt < 8) { - /* short timestamp */ - if(debug) puts("gps: short timestamp."); - return; - } - if (!buftvtots(dpt,&gps->lastrec)) { - /* screwy timestamp */ - if(debug) puts("gps: screwy timestamp."); - return; - } - dpt += 8; + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct wwvbunit *)pp->unitptr; + pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, + &pp->lastrec); + if (up->tcswitch || pp->lencode < 9) + return; + up->tcswitch = 1; + + /* + * 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->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++; } -#else -#ifdef TRAKPPS - if ((*cp = 0x7f & *dpt++) >= ' ') cp++; -#else - /* both are not specified */ -#endif /* TRAKPPS */ -#endif /* TRAKCLK */ } - *cp = '\0'; - gps->lencode = cp - (u_char *)gps->lastcode; - if (gps->lencode == 0) return; - + up->pollcnt = 2; + record_clock_stats(&peer->srcadr, pp->lastcode); #ifdef DEBUG if (debug) - printf("gps: timecode %d %s\n", - gps->lencode, gps->lastcode); + printf("trak: timecode %d %s\n", pp->lencode, + pp->lastcode); #endif /* - * We check the timecode format and decode its contents. The - * timecode has format *........RQTS U,ddd:hh:mm:ss.0,Q\r\n). - * 012345678901234567890123 + * 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. */ -#define RQTS 0 -#define RQLS 1 - cp = (u_char *)gps->lastcode; - gps->leap = 0; - cmdtype=0; - if(strncmp(cp,"*RQTS",5)==0) { - cmdtype=RQTS; - cp += 8; - } - else if(strncmp(cp,"RQTS",4)==0) { - cmdtype=RQTS; - cp += 7; - } - else if(strncmp(cp,"RQLS",4)==0) { - cmdtype=RQLS; - cp += 5; - } - else - return; - - switch( cmdtype ) { - case RQTS: - /* - * Check time code format of TRAK 8810 - */ - if( !isdigit(cp[0]) || - !isdigit(cp[1]) || - !isdigit(cp[2]) || - cp[3] != ':' || - !isdigit(cp[4]) || - !isdigit(cp[5]) || - cp[6] != ':' || - !isdigit(cp[7]) || - !isdigit(cp[8]) || - cp[9] != ':' || - !isdigit(cp[10])|| - !isdigit(cp[11])) { - gps->badformat++; - trak_report_event(gps, CEVNT_BADREPLY); - return; - } - break; - case RQLS: - /* - * reply for leap second request - */ - if (cp[0] !='0' || cp[1] != '0' ) gps->leap = LEAP_ADDSECOND; - return; - default: + if (pp->lencode < LENTRAK) { + refclock_report(peer, CEVNT_BADREPLY); return; - } /* - * Convert date and check values. - */ - gps->day = cp[0] - '0'; - gps->day = MULBY10(gps->day) + cp[1] - '0'; - gps->day = MULBY10(gps->day) + cp[2] - '0'; - if (gps->day < 1 || gps->day > 366) { - gps->baddata++; - trak_report_event(gps, CEVNT_BADDATE); - return; - } - /* - * Convert time and check values. - */ - gps->hour = MULBY10(cp[4] - '0') + cp[5] - '0'; - gps->minute = MULBY10(cp[7] - '0') + cp[8] - '0'; - gps->second = MULBY10(cp[10] - '0') + cp[11] - '0'; - gps->msec = 0; - if (gps->hour > 23 || gps->minute > 59 || gps->second > 59) { - gps->baddata++; - trak_report_event(gps, CEVNT_BADTIME); + * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q" + */ + if (sscanf(pp->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; } - if (!gps->polled) return; - /* - * Test for synchronization Check for quality byte. + * Decode quality and leap characters. If unsynchronized, set + * the leap bits accordingly and exit. */ -/* - switch( cp[15] ) { - case '0': - if(gps->peer->stratum == stratumtouse[gps->unit]) { - gps->peer->stratum = 10 ; - bzero(&gps->peer->refid,4); - } - break; - default: - if(gps->peer->stratum != stratumtouse[gps->unit]) { - gps->peer->stratum = stratumtouse[gps->unit] ; - bcopy(GPSREFID,&gps->peer->refid,4); - } - break; + if (qchar == '0') + pp->leap = LEAP_NOTINSYNC; + else { + pp->leap = 0; + pp->lasttime = current_time; } -*/ - if( cp[15] == '0') /* TRAK derailed from tracking satellites */ - { - gps->leap = LEAP_NOTINSYNC; - gps->noreply++; - trak_report_event(gps, CEVNT_TIMEOUT); - } - else - { - gps->lasttime = current_time; - if( gps->lastevent == CEVNT_TIMEOUT ) { - gps->status = CEVNT_NOMINAL; - trak_report_event(gps, CEVNT_NOMINAL); - } - } - - /* - * Now, compute the reference time value. Use the heavy - * machinery for the second, which presumably is the one which - * occured at the last pps pulse and which was captured by the - * loop_filter module. All we have to do here is present a - * reasonable facsimile of the time at that pulse so the clock- - * filter and selection machinery declares us truechimer. The - * precision offset within the second is really tuned by the - * loop_filter module. Note that this code does not yet know how - * to do the years and relies on the clock-calendar chip for - * sanity. - */ - -#if defined(TRAKPPS) - - /* - * timestamp must be greater than previous one. - */ - if (ioctl(fdpps, CIOGETEV, (caddr_t)&ev) >= 0) { - ev.tv.tv_sec += (U_LONG)JAN_1970; - TVTOTS(&ev.tv,&gps->lastrec); - if (gps->lastev < ev.tv.tv_sec) { - gps->lastev = ev.tv.tv_sec; - } else { /* in case of 1-pps missing */ - gps->lastev = ev.tv.tv_sec; - return; - } - } - else - return; /* failed to get timestamp */ -#endif /* TRAKPPS */ - - if (!clocktime(gps->day, gps->hour, gps->minute, - gps->second, GMT, gps->lastrec.l_ui, - &gps->yearstart, &gps->lastref.l_ui)) { - gps->baddata++; - trak_report_event(gps, CEVNT_BADTIME); -#ifdef DEBUG - if(debug) printf("gps: bad date \n"); -#endif - return; - } - MSUTOTSF(gps->msec, gps->lastref.l_uf); - tstmp = gps->lastref; - - L_SUB(&tstmp, &gps->lastrec); - L_ADD(&tstmp, &(fudgefactor[gps->unit])); - i = ((int)(gps->coderecv)) % NCODES; - gps->offset[i] = tstmp; - gps->coderecv++; -#if DEBUG - if (debug) - printf("gps: times %s %s %s\n", - ulfptoa(&gps->lastref, 6), ulfptoa(&gps->lastrec, 6), - lfptoa(&tstmp, 6)); -#endif -/* if( tstmp.l_ui != 0 ) return; something wrong */ /* - * Process the samples in the median filter, add the fudge - * factor and pass the offset and dispersion along. We use - * lastref 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. + * 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 (gps->coderecv < NCODES) - return; - if (!trak_process(gps, &tstmp, &dispersion)) { - gps->baddata++; - trak_report_event(gps, CEVNT_BADTIME); + if (!refclock_process(pp, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); return; } - refclock_receive(gps->peer, &tstmp, GMT, dispersion, - &gps->lastrec, &gps->lastrec, gps->leap); - /* - * after all, clear polled flag - */ - gps->polled = 0; + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &pp->lastrec, &pp->lastrec, pp->leap); } -/* - * ================================================================== - * gps_send(gps,cmd) Sends a command to the GPS receiver. - * as gps_send(gps,"rqts,u\r"); - * ================================================================== - */ -static void -gps_send(gps,cmd) - struct gpsunit *gps; - char *cmd; -{ - if (write(gps->io.fd, cmd, strlen(cmd)) == -1) { - syslog(LOG_ERR, "gps_send: unit %d: %m", gps->unit); - trak_report_event(gps,CEVNT_FAULT); - } else { - gps->polls++; - } -} - -/* - * trak_process - process a pile of samples from the clock - * - * This routine uses a three-stage median filter to calculate offset and - * dispersion and reduce jitter. The dispersion is calculated as the - * span of the filter (max - min). - */ -static char -trak_process(gps, offset, dispersion) - struct gpsunit *gps; - l_fp *offset; - u_fp *dispersion; -{ - register int i, j; - register U_LONG tmp_ui, tmp_uf; - int not_median1 = -1; /* XXX correct? */ - int not_median2 = -1; /* XXX correct? */ - int median; - u_fp disp_tmp, disp_tmp2; - - /* - * This code implements a three-stage median filter. First, we - * check if the samples are within 125 ms of each other. If not, - * dump the sample set. We take the median of the three offsets - * and use that as the sample offset. There probably is not much - * to be gained by a longer filter, since the clock filter in - * ntp_proto should do its thing. - */ - disp_tmp2 = 0; - for (i = 0; i < NCODES-1; i++) { - for (j = i+1; j < NCODES; j++) { - tmp_ui = gps->offset[i].l_ui; - tmp_uf = gps->offset[i].l_uf; - M_SUB(tmp_ui, tmp_uf, gps->offset[j].l_ui, - gps->offset[j].l_uf); - if (M_ISNEG(tmp_ui, tmp_uf)) { - M_NEG(tmp_ui, tmp_uf); - } - if (tmp_ui != 0 || tmp_uf > CODEDIFF) { - return (0); - } - disp_tmp = MFPTOFP(0, tmp_uf); - if (disp_tmp > disp_tmp2) { - disp_tmp2 = disp_tmp; - not_median1 = i; - not_median2 = j; - } - } - } - if (gps->lasttime == 0) - disp_tmp2 = NTP_MAXDISPERSE; - else - disp_tmp2 = current_time - gps->lasttime; - if (not_median1 == 0) { - if (not_median2 == 1) - median = 2; - else - median = 1; - } else { - median = 0; - } - *offset = gps->offset[median]; - *dispersion = disp_tmp2; - return (1); -} /* * trak_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 trak_poll(unit, peer) int unit; struct peer *peer; { - struct gpsunit *gps; + register struct wwvbunit *up; + struct refclockproc *pp; - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "trak_poll: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "trak_poll: unit %d not in use", unit); - return; - } - gps = gpsunits[unit]; - if ((current_time - gps->lasttime) > 150) - trak_report_event(gpsunits[unit], CEVNT_TIMEOUT); /* - * usually trak_receive can get a timestamp every second + * We don't really do anything here, except arm the receiving + * side to capture a sample and check for timeouts. */ -#if !defined(TRAKPPS) && !defined(TRAKCLK) - gettstamp(&gps->lastrec); -#endif - gps->polls++; - /* - * may be polled every 16 seconds (minpoll 4) - */ - gps->polled = 1; -} - -/* - * trak_control - set fudge factors, return statistics - */ -static void -trak_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - register struct gpsunit *gps; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "trak_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 - */ - gps = gpsunits[unit]; - peer = gps->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - bcopy(GPSREFID, (char *)&peer->refid, - 4); - else - peer->refid = htonl(GPSHSREFID); - } - } - } - - if (out != 0) { - out->type = REFCLK_GPS_TRAK; - out->haveflags - = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2; - out->clockdesc = GPSDESCRIPTION; - 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]) { - gps = gpsunits[unit]; - out->lencode = gps->lencode; /* LENTOC */; - out->lastcode = gps->lastcode; - out->timereset = current_time - gps->timestarted; - out->polls = gps->polls; - out->noresponse = gps->noreply; - out->badformat = gps->badformat; - out->baddata = gps->baddata; - out->lastevent = gps->lastevent; - out->currentstatus = gps->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; - } - } + pp = peer->procptr; + up = (struct wwvbunit *)pp->unitptr; + if (up->pollcnt == 0) + refclock_report(peer, CEVNT_TIMEOUT); + else + up->pollcnt--; + up->tcswitch = 0; + pp->polls++; } -/* - * trak_buginfo - return clock dependent debugging info - */ -static void -trak_buginfo(unit, bug) - int unit; - register struct refclockbug *bug; -{ - register struct gpsunit *gps; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "trak_buginfo: unit %d invalid", unit); - return; - } - - if (!unitinuse[unit]) - return; - gps = gpsunits[unit]; - - bug->nvalues = 10; - bug->ntimes = 5; - if (gps->lasttime != 0) - bug->values[0] = current_time - gps->lasttime; - else - bug->values[0] = 0; - bug->values[1] = (U_LONG)gps->reason; - bug->values[2] = (U_LONG)gps->year; - bug->values[3] = (U_LONG)gps->day; - bug->values[4] = (U_LONG)gps->hour; - bug->values[5] = (U_LONG)gps->minute; - bug->values[6] = (U_LONG)gps->second; - bug->values[7] = (U_LONG)gps->msec; - bug->values[8] = gps->noreply; - bug->values[9] = gps->yearstart; - bug->stimes = 0x1c; - bug->times[0] = gps->lastref; - bug->times[1] = gps->lastrec; - bug->times[2] = gps->offset[0]; - bug->times[3] = gps->offset[1]; - bug->times[4] = gps->offset[2]; -} #endif diff --git a/usr.sbin/xntpd/xntpd/refclock_wwvb.c b/usr.sbin/xntpd/xntpd/refclock_wwvb.c index 9b2563a797db..ea0c82e28ec1 100644 --- a/usr.sbin/xntpd/xntpd/refclock_wwvb.c +++ b/usr.sbin/xntpd/xntpd/refclock_wwvb.c @@ -1,7 +1,7 @@ /* - * refclock_wwvb - clock driver for the Spectracom WWVB receivers + * refclock_wwvb - clock driver for Spectracom WWVB receivers */ -#if defined(REFCLOCK) && (defined(WWVB) || defined(WWVBCLK) || defined(WWVBPPS)) +#if defined(REFCLOCK) && defined(WWVB) #include <stdio.h> #include <ctype.h> @@ -10,62 +10,63 @@ #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" -#include "ntp_unixtime.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 Spectracom Model 8170 and Netclock/2 WWVB - * Synchronized Clock under Unix and on a Gizmo board. There are two - * formats used by these clocks. Format 0 (zero), which is available - * with both the Netclock/2 and 8170, is in the following format: + * Synchronized Clock. This clock has proven a reliable source of time, + * except in some cases of high ambient conductive RF interference. The + * claimed accuracy of the clock is 100 usec 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. + * + * The DIPswitches on this clock should be set to 24-hour display, AUTO + * DST off, time zone 0 (UTC), data format 0 or 2 (see below) and baud + * rate 9600. If this clock is to used as the source for the IRIG Audio + * Decoder (refclock_irig.c in this distribution), set the DIPswitches + * for AM IRIG output and IRIG format 1 (IRIG B with signature control). + * + * 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 and specially modified 8170. + * + * 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) * - * <cr><lf>I<sp><sp>ddd<sp>hh:mm:ss<sp><sp>TZ=nn<cr><lf> + * 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 ddd, hh, mm and ss fields show the day of year, hours, minutes - * and seconds, respectively. The nn field shows the local hour offset - * relative to UTC and should always be set to 00. The I is normally - * <sp> when the clock is synchronized and '?' when it isn't (it could - * also be a '*' if we set the time manually, but this is forbidden. + * Format 2 (24 ASCII printing characters): * - * Format 2 (two), which is available only with the Netclock/2 and - * specially modified 8170, is in the following format: + * <cr><lf>iqyy ddd hh:mm:ss.fff ld * - * <cr><lf>IQyy<sp>ddd<sp>hh:mm:ss.mmm<sp>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 ddd, hh and ss fields and I are as in format 0. The yy field - * shows the year and mmm the milliseconds, respectively. The Q is - * normally <sp> when the time error is less than 1 ms and and a + * 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 - * <sp>, but is set to 'L' early in the month of an upcoming UTC - * leap second and reset to <sp> 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 supposed to be synchronized to the on-time second. + * 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. A three-stage @@ -74,485 +75,168 @@ * jitter of the radio itself, which is a known problem with the older * radios. * - * This driver supports the 1-pps signal provided by the radio and - * connected via a level converted described in the gadget directory. - * The signal is captured using a separate, dedicated, serial port and - * the tty_clk line discipline/streams modules described in the kernel - * directory. For the highest precision, the signal is captured using - * the carrier-detect line of the same serial port using the ppsclock - * streams module described in the ppsclock directory. - * - * Bugs: + * Fudge Factors * - * The year indication so carefully provided in format 2 is not used. + * This driver can retrieve a table of quality data maintained + * internally by the Netclock/2 receiver. 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. */ /* - * Definitions + * Interface definitions */ -#define MAXUNITS 4 /* max number of WWVB units */ -#define WWVB232 "/dev/wwvb%d" /* name of radio device */ +#define DEVICE "/dev/wwvb%d" /* device name and unit */ #define SPEED232 B9600 /* uart speed (9600 baud) */ +#define PRECISION (-10) /* precision assumed (about 1 ms) */ +#define REFID "WWVB" /* reference ID */ +#define DESCRIPTION "Spectracom WWVB Receiver" /* WRU */ -/* - * Radio interface parameters - */ -#define WWVBPRECISION (-13) /* precision assumed (about 100 us) */ -#define WWVBREFID "WWVB" /* reference id */ -#define WWVBDESCRIPTION "Spectracom WWVB Receiver" /* who we are */ -#define WWVBHSREFID 0x7f7f040a /* 127.127.4.10 refid hi strata */ -#define GMT 0 /* hour offset from Greenwich */ -#define NCODES 3 /* stages of median filter */ +#define NSAMPLES 3 /* stages of median filter */ #define LENWWVB0 22 /* format 0 timecode length */ #define LENWWVB2 24 /* format 2 timecode length */ -#define FMTWWVBU 0 /* unknown format timecode id */ -#define FMTWWVB0 1 /* format 0 timecode id */ -#define FMTWWVB2 2 /* format 2 timecode id */ -#define BMAX 80 /* timecode buffer length */ -#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */ #define MONLIN 15 /* number of monitoring lines */ -/* - * Hack to avoid excercising the multiplier. I have no pride. - */ -#define MULBY10(x) (((x)<<3) + ((x)<<1)) /* * Imported from ntp_timer module */ -extern U_LONG current_time; /* current time (s) */ - -/* - * Imported from ntp_loopfilter module - */ -extern int fdpps; /* pps file descriptor */ +extern u_long current_time; /* current time (s) */ /* * Imported from ntpd module */ -extern int debug; /* global debug flag */ +extern int debug; /* global debug flag */ /* * WWVB unit control structure */ struct wwvbunit { - struct peer *peer; /* associated peer structure */ - struct refclockio io; /* given to the I/O handler */ - l_fp lastrec; /* last receive time */ - l_fp lastref; /* last timecode time */ - l_fp offset[NCODES]; /* recent sample offsets */ - char lastcode[BMAX]; /* last timecode received */ - u_char format; /* timecode format */ - u_char tcswitch; /* timecode switch */ - u_char pollcnt; /* poll message counter */ - u_char lencode; /* length of last timecode */ - U_LONG lasttime; /* last time clock heard from */ - 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 year; /* year of eternity */ - u_short day; /* day of year */ - u_char hour; /* hour of day */ - u_char minute; /* minute of hour */ - u_char second; /* seconds of minute */ - u_char leap; /* leap indicators */ - u_short msec; /* millisecond of second */ - u_char quality; /* quality char from format 2 */ - U_LONG yearstart; /* start of current year */ - u_char lasthour; /* last hour (for monitor) */ - u_char linect; /* count of ignored lines (for monitor */ + int pollcnt; /* poll message counter */ - /* - * 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 */ + 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 */ }; /* - * Data space for the unit structures. Note that we allocate these on - * the fly, but never give them back. - */ -static struct wwvbunit *wwvbunits[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 wwvb_init P((void)); -static int wwvb_start P((u_int, struct peer *)); -static void wwvb_shutdown P((int)); -static void wwvb_report_event P((struct wwvbunit *, int)); +static int wwvb_start P((int, struct peer *)); +static void wwvb_shutdown P((int, struct peer *)); static void wwvb_receive P((struct recvbuf *)); -static char wwvb_process P((struct wwvbunit *, l_fp *, u_fp *)); static void wwvb_poll P((int, struct peer *)); -static void wwvb_control P((u_int, struct refclockstat *, struct refclockstat *)); -static void wwvb_buginfo P((int, struct refclockbug *)); /* * Transfer vector */ struct refclock refclock_wwvb = { - wwvb_start, wwvb_shutdown, wwvb_poll, - wwvb_control, wwvb_init, wwvb_buginfo, NOFLAGS + 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_init - initialize internal wwvb driver data - */ -static void -wwvb_init() -{ - register int i; - /* - * Just zero the data arrays - */ - memset((char *)wwvbunits, 0, sizeof wwvbunits); - memset((char *)unitinuse, 0, 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; - } -} - /* - * wwvb_start - open the WWVB devices and initialize data for processing + * wwvb_start - open the devices and initialize data for processing */ static int wwvb_start(unit, peer) - u_int unit; + int unit; struct peer *peer; { - register struct wwvbunit *wwvb; - register int i; - int fd232; - char wwvbdev[20]; + register struct wwvbunit *up; + struct refclockproc *pp; + int fd; + char device[20]; /* - * Check configuration info + * Open serial port. Use CLK line discipline, if available. */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "wwvb_start: unit %d invalid", unit); + (void)sprintf(device, DEVICE, unit); + if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) return (0); - } - if (unitinuse[unit]) { - syslog(LOG_ERR, "wwvb_start: unit %d in use", unit); - return (0); - } /* - * Open serial port + * Allocate and initialize unit structure */ - (void) sprintf(wwvbdev, WWVB232, unit); - fd232 = open(wwvbdev, O_RDWR, 0777); - if (fd232 == -1) { - syslog(LOG_ERR, "wwvb_start: open of %s: %m", wwvbdev); + if (!(up = (struct wwvbunit *) + emalloc(sizeof(struct wwvbunit)))) { + (void) close(fd); return (0); } - -#if defined(HAVE_SYSV_TTYS) - /* - * System V serial line parameters (termio interface) - * - */ - { struct termio ttyb; - if (ioctl(fd232, TCGETA, &ttyb) < 0) { - syslog(LOG_ERR, - "wwvb_start: ioctl(%s, TCGETA): %m", wwvbdev); - 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) { - syslog(LOG_ERR, - "wwvb_start: ioctl(%s, TCSETA): %m", wwvbdev); - goto screwed; - } - } -#endif /* HAVE_SYSV_TTYS */ -#if defined(HAVE_TERMIOS) - /* - * POSIX serial line parameters (termios interface) - * - * The WWVBCLK option provides timestamping at the driver level. - * It requires the tty_clk streams module. - * - * The WWVBPPS 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 SunOS 4.1.1 or - * later. - */ - { struct termios ttyb, *ttyp; - - ttyp = &ttyb; - if (tcgetattr(fd232, ttyp) < 0) { - syslog(LOG_ERR, - "wwvb_start: tcgetattr(%s): %m", wwvbdev); - 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) { - syslog(LOG_ERR, - "wwvb_start: tcsetattr(%s): %m", wwvbdev); - goto screwed; - } - if (tcflush(fd232, TCIOFLUSH) < 0) { - syslog(LOG_ERR, - "wwvb_start: tcflush(%s): %m", wwvbdev); - goto screwed; - } - } -#endif /* HAVE_TERMIOS */ -#ifdef STREAM -#if defined(WWVBCLK) - if (ioctl(fd232, I_PUSH, "clk") < 0) - syslog(LOG_ERR, - "wwvb_start: ioctl(%s, I_PUSH, clk): %m", wwvbdev); - if (ioctl(fd232, CLK_SETSTR, "\n") < 0) - syslog(LOG_ERR, - "wwvb_start: ioctl(%s, CLK_SETSTR): %m", wwvbdev); -#endif /* WWVBCLK */ -#if defined(WWVBPPS) - if (ioctl(fd232, I_PUSH, "ppsclock") < 0) - syslog(LOG_ERR, - "wwvb_start: ioctl(%s, I_PUSH, ppsclock): %m", wwvbdev); - else - fdpps = fd232; -#endif /* WWVBPPS */ -#endif /* STREAM */ -#if defined(HAVE_BSD_TTYS) - /* - * 4.3bsd serial line parameters (sgttyb interface) - * - * The WWVBCLK option provides timestamping at the driver level. - * It requires the tty_clk line discipline and 4.3bsd or later. - */ - { struct sgttyb ttyb; -#if defined(WWVBCLK) - int ldisc = CLKLDISC; -#endif /* WWVBCLK */ - - if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { - syslog(LOG_ERR, - "wwvb_start: ioctl(%s, TIOCGETP): %m", wwvbdev); - goto screwed; - } - ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; -#if defined(WWVBCLK) - 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 /* WWVBCLK */ - if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { - syslog(LOG_ERR, - "wwvb_start: ioctl(%s, TIOCSETP): %m", wwvbdev); - goto screwed; - } -#if defined(WWVBCLK) - if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { - syslog(LOG_ERR, - "wwvb_start: ioctl(%s, TIOCSETD): %m",wwvbdev); - goto screwed; - } -#endif /* WWVBCLK */ - } -#endif /* HAVE_BSD_TTYS */ - - /* - * Allocate unit structure - */ - if (wwvbunits[unit] != 0) { - wwvb = wwvbunits[unit]; /* The one we want is okay */ - } else { - for (i = 0; i < MAXUNITS; i++) { - if (!unitinuse[i] && wwvbunits[i] != 0) - break; - } - if (i < MAXUNITS) { - /* - * Reclaim this one - */ - wwvb = wwvbunits[i]; - wwvbunits[i] = 0; - } else { - wwvb = (struct wwvbunit *) - emalloc(sizeof(struct wwvbunit)); - } + memset((char *)up, 0, sizeof(struct wwvbunit)); + pp = peer->procptr; + 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); } - memset((char *)wwvb, 0, sizeof(struct wwvbunit)); - wwvbunits[unit] = wwvb; + pp->unitptr = (caddr_t)up; /* - * Set up the structures + * Initialize miscellaneous variables */ - wwvb->peer = peer; - wwvb->unit = (u_char)unit; - wwvb->timestarted = current_time; - wwvb->pollcnt = 2; - - wwvb->io.clock_recv = wwvb_receive; - wwvb->io.srcclock = (caddr_t)wwvb; - wwvb->io.datalen = 0; - wwvb->io.fd = fd232; - if (!io_addclock(&wwvb->io)) - goto screwed; - - /* - * All done. Initialize a few random peer variables, then - * return success. Note that root delay and root dispersion are - * always zero for this clock. - */ - peer->precision = WWVBPRECISION; - peer->rootdelay = 0; - peer->rootdispersion = 0; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, WWVBREFID, 4); - else - peer->refid = htonl(WWVBHSREFID); - unitinuse[unit] = 1; + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; return (1); - - /* - * Something broke; abandon ship. - */ -screwed: - (void) close(fd232); - return (0); -} - -/* - * wwvb_shutdown - shut down a WWVB clock - */ -static void -wwvb_shutdown(unit) - int unit; -{ - register struct wwvbunit *wwvb; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "wwvb_shutdown: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "wwvb_shutdown: unit %d not in use", unit); - return; - } - - /* - * Tell the I/O module to turn us off. We're history. - */ - wwvb = wwvbunits[unit]; - io_closeclock(&wwvb->io); - unitinuse[unit] = 0; } /* - * wwvb_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. + * wwvb_shutdown - shut down the clock */ static void -wwvb_report_event(wwvb, code) - struct wwvbunit *wwvb; - int code; -{ +wwvb_shutdown(unit, peer) + int unit; struct peer *peer; +{ + register struct wwvbunit *up; + struct refclockproc *pp; - peer = wwvb->peer; - if (wwvb->status != (u_char)code) { - wwvb->status = (u_char)code; - if (code != CEVNT_NOMINAL) - wwvb->lastevent = (u_char)code; - syslog(LOG_INFO, - "clock %s event %x", ntoa(&peer->srcadr), code); - } + pp = peer->procptr; + up = (struct wwvbunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); } /* - * wwvb_receive - receive data from the serial interface on a Spectracom - * clock + * wwvb_receive - receive data from the serial interface */ static void wwvb_receive(rbufp) struct recvbuf *rbufp; { - register int i; - register struct wwvbunit *wwvb; - register char *dpt, *cp, *dp; - int dpend; - l_fp tstmp, trtmp; - u_fp dispersion; + register struct wwvbunit *up; + struct refclockproc *pp; + struct peer *peer; + l_fp trtmp; + u_long ltemp; + int temp; + char syncchar; /* synchronization indicator */ + char qualchar; /* quality indicator */ + char leapchar; /* leap indicator */ /* - * Get the clock this applies to and a pointers to the data. - * Check for the presence of a timestamp left by the tty_clock - * line discipline/streams module and, if present, use that - * instead of the timestamp captured by the i/o routines. + * Initialize pointers and read the timecode and timestamp */ - wwvb = (struct wwvbunit *)rbufp->recv_srcclock; - dpt = (char *)&rbufp->recv_space; - dpend = rbufp->recv_length; - if (dpend > BMAX - 1) - dpend = BMAX - 1; - wwvb-> pollcnt = 2; - trtmp = rbufp->recv_time; - if (dpend >= 9) { - dp = dpt + dpend - 9; - if (*dp == '\n' || *dp == '\r') { - dpend -= 8; - if (!buftvtots(dp + 1, &tstmp)) { -#ifdef DEBUG - if (debug) - printf("wwvb_receive: invalid timestamp"); -#endif - } else { -#ifdef DEBUG - if (debug) { - L_SUB(&trtmp, &tstmp); - printf("wwvb: delta %s", - lfptoa(&trtmp, 6)); - gettstamp(&trtmp); - L_SUB(&trtmp, &tstmp); - printf(" SIGIO %s\n", - lfptoa(&trtmp, 6)); - } -#endif - trtmp = tstmp; - } - } - } + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct wwvbunit *)pp->unitptr; + temp = refclock_gtlin(rbufp, pp->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 @@ -563,38 +247,24 @@ wwvb_receive(rbufp) * you have a pps gadget and don't have to have the year, format * 0 provides the lowest jitter. */ - if (dpend == 1) { - if (wwvb->tcswitch == 0) { - wwvb->tcswitch = 1; - wwvb->lastrec = trtmp; + if (temp == 0) { + if (up->tcswitch == 0) { + up->tcswitch = 1; + up->laststamp = trtmp; } else - wwvb->tcswitch = 0; + up->tcswitch = 0; return; } - tstmp = wwvb->lastrec; - wwvb->lastrec = trtmp; - wwvb->tcswitch = 1; - - /* - * Edit timecode to remove control chars. Note the receive - * timestamp is determined at the first <cr>; however, we don't - * get the timecode for that timestamp until the next <cr>. We - * assume that, if we happen to come up during a timestamp, or - * other awkward time, the format and data checks will cause the - * driver to resynchronize after maybe a few false starts. - */ - if (dpend <= 0) - return; - cp = dp = wwvb->lastcode; - for (i = 0; i < dpend; i++) - if ((*dp = 0x7f & *dpt++) >= ' ') dp++; - *dp = '\0'; - wwvb->lencode = dp - cp; - record_clock_stats(&(wwvb->peer->srcadr), wwvb->lastcode); + pp->lencode = temp; + pp->lastrec = up->laststamp; + up->laststamp = trtmp; + up->tcswitch = 1; + up->pollcnt = 2; + record_clock_stats(&peer->srcadr, pp->lastcode); #ifdef DEBUG if (debug) - printf("wwvb: timecode %d %d %s\n", - wwvb->linect, wwvb->lencode, wwvb->lastcode); + printf("wwvb: timecode %d %s\n", pp->lencode, + pp->lastcode); #endif /* @@ -602,279 +272,113 @@ wwvb_receive(rbufp) * 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; if the converted decimal values are out of range, we - * declare bad data and exit. + * exit. */ - cp = wwvb->lastcode; - wwvb->leap = 0; - wwvb->format = FMTWWVBU; - if ((cp[0] == ' ' || cp[0] == '?') && wwvb->lencode == LENWWVB0) { + switch (pp->lencode) { + + case LENWWVB0: /* - * Check timecode format 0 + * Timecode format 0: "I ddd hh:mm:ss TZ=nn" */ - if (cp[1] != ' ' || /* <sp> separator */ - cp[2] != ' ' || /* <sp> separator */ - !isdigit(cp[3]) || /* day of year */ - !isdigit(cp[4]) || - !isdigit(cp[5]) || - cp[6] != ' ' || /* <sp> separator */ - !isdigit(cp[7]) || /* hours */ - !isdigit(cp[8]) || - cp[9] != ':' || /* : separator */ - !isdigit(cp[10]) || /* minutes */ - !isdigit(cp[11]) || - cp[12] != ':' || /* : separator */ - !isdigit(cp[13]) || /* seconds */ - !isdigit(cp[14])) { - wwvb->badformat++; - wwvb_report_event(wwvb, CEVNT_BADREPLY); - return; - } - else - wwvb->format = FMTWWVB0; + qualchar = leapchar = ' '; + if (sscanf(pp->lastcode, "%c %3d %2d:%2d:%2d", + &syncchar, &pp->day, &pp->hour, &pp->minute, + &pp->second) == 5) + break; - /* - * Convert format 0 and check values - */ - wwvb->year = 0; /* fake */ - wwvb->day = cp[3] - '0'; - wwvb->day = MULBY10(wwvb->day) + cp[4] - '0'; - wwvb->day = MULBY10(wwvb->day) + cp[5] - '0'; - wwvb->hour = MULBY10(cp[7] - '0') + cp[8] - '0'; - wwvb->minute = MULBY10(cp[10] - '0') + cp[11] - '0'; - wwvb->second = MULBY10(cp[13] - '0') + cp[14] - '0'; - wwvb->msec = 0; - if (cp[0] != ' ') - wwvb->leap = LEAP_NOTINSYNC; - else - wwvb->lasttime = current_time; - if (wwvb->day < 1 || wwvb->day > 366) { - wwvb->baddata++; - wwvb_report_event(wwvb, CEVNT_BADDATE); - return; - } - if (wwvb->hour > 23 || wwvb->minute > 59 - || wwvb->second > 59) { - wwvb->baddata++; - wwvb_report_event(wwvb, CEVNT_BADTIME); - return; - } - } else if ((cp[0] == ' ' || cp[0] == '?') && wwvb->lencode == LENWWVB2) { + case LENWWVB2: /* - * Check timecode format 2 + * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */ - if (!isdigit(cp[2]) || /* year of century */ - !isdigit(cp[3]) || - cp[4] != ' ' || /* <sp> separator */ - !isdigit(cp[5]) || /* day of year */ - !isdigit(cp[6]) || - !isdigit(cp[7]) || - cp[8] != ' ' || /* <sp> separator */ - !isdigit(cp[9]) || /* hour */ - !isdigit(cp[10]) || - cp[11] != ':' || /* : separator */ - !isdigit(cp[12]) || /* minute */ - !isdigit(cp[13]) || - cp[14] != ':' || /* : separator */ - !isdigit(cp[15]) || /* second */ - !isdigit(cp[16]) || - cp[17] != '.' || /* . separator */ - !isdigit(cp[18]) || /* millisecond */ - !isdigit(cp[19]) || - !isdigit(cp[20]) || - cp[21] != ' ') { /* <sp> separator */ - wwvb->badformat++; - wwvb_report_event(wwvb, CEVNT_BADREPLY); - return; - } - else - wwvb->format = FMTWWVB2; + if (sscanf(pp->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; - /* - * Convert format 2 and check values - */ - wwvb->year = MULBY10(cp[2] - '0') + cp[3] - '0'; - wwvb->day = cp[5] - '0'; - wwvb->day = MULBY10(wwvb->day) + cp[6] - '0'; - wwvb->day = MULBY10(wwvb->day) + cp[7] - '0'; - wwvb->hour = MULBY10(cp[9] - '0') + cp[10] - '0'; - wwvb->minute = MULBY10(cp[12] - '0') + cp[13] - '0'; - wwvb->second = MULBY10(cp[15] - '0') + cp[16] - '0'; - wwvb->msec = cp[18] - '0'; - wwvb->msec = MULBY10(wwvb->msec) + cp[19] - '0'; - wwvb->msec = MULBY10(wwvb->msec) + cp[20] - '0'; - wwvb->quality = cp[1]; - if (cp[0] != ' ') - wwvb->leap = LEAP_NOTINSYNC; + default: - /* - * This nonsense adjusts the last time the clock was - * heard from depending on the quality indicator. Once - * the clock has been heard, the dispersion depends only - * on when the clock was last heard. The first time the - * clock is heard, the time last heard is faked based on - * the quality indicator. The magic numbers (in seconds) - * are from the clock specifications. - */ - if (wwvb->lasttime != 0) { - if (cp[1] == ' ') - wwvb->lasttime = current_time; - } else { - switch (cp[1]) { - case ' ': - wwvb->lasttime = current_time; - break; - case 'A': - wwvb->lasttime = current_time - 800; - break; - case 'B': - wwvb->lasttime = current_time - 5300; - break; - case 'C': - wwvb->lasttime = current_time - 25300; - break; - /* Don't believe anything else */ - } - } - if (cp[22] == 'L') - wwvb->leap = LEAP_ADDSECOND; - if (wwvb->day < 1 || wwvb->day > 366) { - wwvb->baddata++; - wwvb_report_event(wwvb, CEVNT_BADDATE); - return; - } - if (wwvb->hour > 23 || wwvb->minute > 59 - || wwvb->second > 59) { - wwvb->baddata++; - wwvb_report_event(wwvb, CEVNT_BADTIME); - return; - } - } else { - if (wwvb->linect > 0) - wwvb->linect--; - else { - wwvb->badformat++; - wwvb_report_event(wwvb, CEVNT_BADREPLY); - } + if (up->linect > 0) + up->linect--; + else + refclock_report(peer, CEVNT_BADREPLY); return; } - if (sloppyclockflag[wwvb->unit] & CLK_FLAG4 && - wwvb->hour < wwvb->lasthour) - wwvb->linect = MONLIN; - wwvb->lasthour = wwvb->hour; /* - * 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. + * 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 when the + * clock was last heard. The first time the clock is heard, the + * time last heard is faked based on the quality indicator. The + * magic numbers (in seconds) are from the clock specifications. */ - if (!clocktime(wwvb->day, wwvb->hour, wwvb->minute, - wwvb->second, GMT, tstmp.l_ui, - &wwvb->yearstart, &wwvb->lastref.l_ui)) { - wwvb->baddata++; - wwvb_report_event(wwvb, CEVNT_BADTIME); + switch (qualchar) { + + case ' ': + ltemp = 0; + break; + + case 'A': + ltemp = 800; + break; + + case 'B': + ltemp = 5300; + break; + + case 'C': + ltemp = 25300; + break; + + case 'D': + ltemp = NTP_MAXAGE; + break; + + default: + refclock_report(peer, CEVNT_BADREPLY); return; } - MSUTOTSF(wwvb->msec, wwvb->lastref.l_uf); - i = ((int)(wwvb->coderecv)) % NCODES; - wwvb->offset[i] = wwvb->lastref; - L_SUB(&wwvb->offset[i], &tstmp); - if (wwvb->coderecv == 0) - for (i = 1; i < NCODES; i++) - wwvb->offset[i] = wwvb->offset[0]; - wwvb->coderecv++; + if (syncchar != ' ') + pp->leap = LEAP_NOTINSYNC; + else { + if (leapchar == 'L') + pp->leap = LEAP_ADDSECOND; + else + pp->leap = 0; + pp->lasttime = current_time - ltemp; + } + + /* + * 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 < + up->lasthour) + up->linect = MONLIN; + up->lasthour = pp->hour; /* - * Process the samples in the median filter, add the fudge - * factor and pass the offset and dispersion along. 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. + * 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 (!wwvb_process(wwvb, &tstmp, &dispersion)) { - wwvb->baddata++; - wwvb_report_event(wwvb, CEVNT_BADTIME); + if (!refclock_process(pp, NSAMPLES, NSAMPLES)) { + refclock_report(peer, CEVNT_BADTIME); return; } - L_ADD(&tstmp, &(fudgefactor[wwvb->unit])); - refclock_receive(wwvb->peer, &tstmp, GMT, dispersion, - &wwvb->lastrec, &wwvb->lastrec, wwvb->leap); + trtmp = pp->lastrec; + trtmp.l_ui -= ltemp; + refclock_receive(peer, &pp->offset, 0, pp->dispersion, + &trtmp, &pp->lastrec, pp->leap); } -/* - * wwvb_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 char -wwvb_process(wwvb, offset, dispersion) - struct wwvbunit *wwvb; - l_fp *offset; - u_fp *dispersion; -{ - register int i, j; - register U_LONG tmp_ui, tmp_uf; - int not_median1 = -1; /* XXX correct? */ - int not_median2 = -1; /* XXX correct? */ - int median; - u_fp disp_tmp, disp_tmp2; - - /* - * This code implements a three-stage median filter. First, we - * check if the samples are within 125 ms of each other. If not, - * dump the sample set. We take the median of the three offsets - * and use that as the sample offset. There probably is not much - * to be gained by a longer filter, since the clock filter in - * ntp_proto should do its thing. - */ - disp_tmp2 = 0; - for (i = 0; i < NCODES-1; i++) { - for (j = i+1; j < NCODES; j++) { - tmp_ui = wwvb->offset[i].l_ui; - tmp_uf = wwvb->offset[i].l_uf; - M_SUB(tmp_ui, tmp_uf, wwvb->offset[j].l_ui, - wwvb->offset[j].l_uf); - if (M_ISNEG(tmp_ui, tmp_uf)) { - M_NEG(tmp_ui, tmp_uf); - } - if (tmp_ui != 0 || tmp_uf > CODEDIFF) { - return (0); - } - disp_tmp = MFPTOFP(0, tmp_uf); - if (disp_tmp > disp_tmp2) { - disp_tmp2 = disp_tmp; - not_median1 = i; - not_median2 = j; - } - } - } - if (wwvb->lasttime == 0) - disp_tmp2 = NTP_MAXDISPERSE; - else if (wwvb->quality != ' ') - disp_tmp2 = current_time - wwvb->lasttime; - if (not_median1 == 0) { - if (not_median2 == 1) - median = 2; - else - median = 1; - } else { - median = 0; - } - *offset = wwvb->offset[median]; - *dispersion = disp_tmp2; - return (1); -} /* * wwvb_poll - called by the transmit procedure @@ -884,162 +388,33 @@ wwvb_poll(unit, peer) int unit; struct peer *peer; { - struct wwvbunit *wwvb; + register struct wwvbunit *up; + struct refclockproc *pp; char poll; /* - * Time to request a time code. The Spectracom clock responds - * to a "T" sent to it by returning a time code as stated in the - * comments in the header. 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. + * 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 nothing is heard + * from the clock for two polls, declare a timeout and keep + * going. */ - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "wwvb_poll: unit %d invalid", unit); - return; - } - if (!unitinuse[unit]) { - syslog(LOG_ERR, "wwvb_poll: unit %d not in use", unit); - return; - } - wwvb = wwvbunits[unit]; - if (wwvb->pollcnt > 0) { - wwvb->pollcnt--; - if (wwvb->pollcnt == 0) - wwvb_report_event(wwvbunits[unit], CEVNT_TIMEOUT); - } - if (wwvb->pollcnt == 0) - wwvb->noreply++; - if (wwvb->linect > 0) + pp = peer->procptr; + up = (struct wwvbunit *)pp->unitptr; + if (up->pollcnt == 0) + refclock_report(peer, CEVNT_TIMEOUT); + else + up->pollcnt--; + if (up->linect > 0) poll = 'R'; else poll = 'T'; - if (write(wwvb->io.fd, &poll, 1) != 1) { - syslog(LOG_ERR, "wwvb_poll: unit %d: %m", wwvb->unit); - wwvb_report_event(wwvb, CEVNT_FAULT); - } else { - wwvb->polls++; - } -} - -/* - * wwvb_control - set fudge factors, return statistics - */ -static void -wwvb_control(unit, in, out) - u_int unit; - struct refclockstat *in; - struct refclockstat *out; -{ - register struct wwvbunit *wwvb; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "wwvb_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 - */ - wwvb = wwvbunits[unit]; - peer = wwvb->peer; - peer->stratum = stratumtouse[unit]; - if (stratumtouse[unit] <= 1) - memmove((char *)&peer->refid, - WWVBREFID, 4); - else - peer->refid = htonl(WWVBHSREFID); - } - } - if (in->haveflags & CLK_HAVEFLAG4) { - sloppyclockflag[unit] = in->flags & CLK_FLAG4; - } - } - - if (out != 0) { - out->type = REFCLK_WWVB_SPECTRACOM; - out->haveflags - = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG4; - out->clockdesc = WWVBDESCRIPTION; - 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]) { - wwvb = wwvbunits[unit]; - out->lencode = wwvb->lencode; - out->lastcode = wwvb->lastcode; - out->timereset = current_time - wwvb->timestarted; - out->polls = wwvb->polls; - out->noresponse = wwvb->noreply; - out->badformat = wwvb->badformat; - out->baddata = wwvb->baddata; - out->lastevent = wwvb->lastevent; - out->currentstatus = wwvb->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; - } - } + if (write(pp->io.fd, &poll, 1) != 1) { + refclock_report(peer, CEVNT_FAULT); + } else + pp->polls++; } -/* - * wwvb_buginfo - return clock dependent debugging info - */ -static void -wwvb_buginfo(unit, bug) - int unit; - register struct refclockbug *bug; -{ - register struct wwvbunit *wwvb; - - if (unit >= MAXUNITS) { - syslog(LOG_ERR, "wwvb_buginfo: unit %d invalid", unit); - return; - } - - if (!unitinuse[unit]) - return; - wwvb = wwvbunits[unit]; - - bug->nvalues = 11; - bug->ntimes = 5; - if (wwvb->lasttime != 0) - bug->values[0] = current_time - wwvb->lasttime; - else - bug->values[0] = 0; - bug->values[1] = (U_LONG)wwvb->reason; - bug->values[2] = (U_LONG)wwvb->year; - bug->values[3] = (U_LONG)wwvb->day; - bug->values[4] = (U_LONG)wwvb->hour; - bug->values[5] = (U_LONG)wwvb->minute; - bug->values[6] = (U_LONG)wwvb->second; - bug->values[7] = (U_LONG)wwvb->msec; - bug->values[8] = wwvb->noreply; - bug->values[9] = wwvb->yearstart; - bug->values[10] = wwvb->quality; - bug->stimes = 0x1c; - bug->times[0] = wwvb->lastref; - bug->times[1] = wwvb->lastrec; - bug->times[2] = wwvb->offset[0]; - bug->times[3] = wwvb->offset[1]; - bug->times[4] = wwvb->offset[2]; -} #endif |