diff options
Diffstat (limited to 'usr.sbin/xntpd/xntpd/refclock_new/refclock_parse.c')
-rw-r--r-- | usr.sbin/xntpd/xntpd/refclock_new/refclock_parse.c | 3617 |
1 files changed, 3617 insertions, 0 deletions
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 0000000..1b950a9 --- /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... + * + */ |