154359Sroberto/*
254359Sroberto * refclock_tpro - clock driver for the KSI/Odetics TPRO-S IRIG-B reader
354359Sroberto */
454359Sroberto
554359Sroberto#ifdef HAVE_CONFIG_H
654359Sroberto#include <config.h>
754359Sroberto#endif
854359Sroberto
954359Sroberto#if defined(REFCLOCK) && defined(CLOCK_TPRO)
1054359Sroberto
1154359Sroberto#include "ntpd.h"
1254359Sroberto#include "ntp_io.h"
1354359Sroberto#include "ntp_refclock.h"
1454359Sroberto#include "ntp_unixtime.h"
1554359Sroberto#include "sys/tpro.h"
1654359Sroberto#include "ntp_stdlib.h"
1754359Sroberto
1882498Sroberto#include <stdio.h>
1982498Sroberto#include <ctype.h>
2082498Sroberto
2154359Sroberto/*
2254359Sroberto * This driver supports the KSI/Odetecs TPRO-S IRIG-B reader and TPRO-
2354359Sroberto * SAT GPS receiver for the Sun Microsystems SBus. It requires that the
2454359Sroberto * tpro.o device driver be installed and loaded.
2554359Sroberto */
2654359Sroberto
2754359Sroberto/*
2854359Sroberto * TPRO interface definitions
2954359Sroberto */
3054359Sroberto#define	DEVICE		 "/dev/tpro%d" /* device name and unit */
3154359Sroberto#define	PRECISION	(-20)	/* precision assumed (1 us) */
3254359Sroberto#define	REFID		"IRIG"	/* reference ID */
3354359Sroberto#define	DESCRIPTION	"KSI/Odetics TPRO/S IRIG Interface" /* WRU */
3454359Sroberto
3554359Sroberto/*
3654359Sroberto * Unit control structure
3754359Sroberto */
3854359Srobertostruct tprounit {
3954359Sroberto	struct	tproval tprodata; /* data returned from tpro read */
4054359Sroberto};
4154359Sroberto
4254359Sroberto/*
4354359Sroberto * Function prototypes
4454359Sroberto */
45280849Scystatic	int	tpro_start	(int, struct peer *);
46280849Scystatic	void	tpro_shutdown	(int, struct peer *);
47280849Scystatic	void	tpro_poll	(int unit, struct peer *);
4854359Sroberto
4954359Sroberto/*
5054359Sroberto * Transfer vector
5154359Sroberto */
5254359Srobertostruct	refclock refclock_tpro = {
5354359Sroberto	tpro_start,		/* start up driver */
5454359Sroberto	tpro_shutdown,		/* shut down driver */
5554359Sroberto	tpro_poll,		/* transmit poll message */
5654359Sroberto	noentry,		/* not used (old tpro_control) */
5754359Sroberto	noentry,		/* initialize driver (not used) */
5854359Sroberto	noentry,		/* not used (old tpro_buginfo) */
5954359Sroberto	NOFLAGS			/* not used */
6054359Sroberto};
6154359Sroberto
6254359Sroberto
6354359Sroberto/*
6454359Sroberto * tpro_start - open the TPRO device and initialize data for processing
6554359Sroberto */
6654359Srobertostatic int
6754359Srobertotpro_start(
6854359Sroberto	int unit,
6954359Sroberto	struct peer *peer
7054359Sroberto	)
7154359Sroberto{
7254359Sroberto	register struct tprounit *up;
7354359Sroberto	struct refclockproc *pp;
7454359Sroberto	char device[20];
7554359Sroberto	int fd;
7654359Sroberto
7754359Sroberto	/*
7854359Sroberto	 * Open TPRO device
7954359Sroberto	 */
80280849Scy	snprintf(device, sizeof(device), DEVICE, unit);
8154359Sroberto	fd = open(device, O_RDONLY | O_NDELAY, 0777);
8254359Sroberto	if (fd == -1) {
8354359Sroberto		msyslog(LOG_ERR, "tpro_start: open of %s: %m", device);
8454359Sroberto		return (0);
8554359Sroberto	}
8654359Sroberto
8754359Sroberto	/*
8854359Sroberto	 * Allocate and initialize unit structure
8954359Sroberto	 */
90280849Scy	up = emalloc_zero(sizeof(*up));
9154359Sroberto	pp = peer->procptr;
9254359Sroberto	pp->io.clock_recv = noentry;
93280849Scy	pp->io.srcclock = peer;
9454359Sroberto	pp->io.datalen = 0;
9554359Sroberto	pp->io.fd = fd;
96280849Scy	pp->unitptr = up;
9754359Sroberto
9854359Sroberto	/*
9954359Sroberto	 * Initialize miscellaneous peer variables
10054359Sroberto	 */
10154359Sroberto	peer->precision = PRECISION;
10254359Sroberto	pp->clockdesc = DESCRIPTION;
10354359Sroberto	memcpy((char *)&pp->refid, REFID, 4);
10454359Sroberto	return (1);
10554359Sroberto}
10654359Sroberto
10754359Sroberto
10854359Sroberto/*
10954359Sroberto * tpro_shutdown - shut down the clock
11054359Sroberto */
11154359Srobertostatic void
11254359Srobertotpro_shutdown(
11354359Sroberto	int unit,
11454359Sroberto	struct peer *peer
11554359Sroberto	)
11654359Sroberto{
11754359Sroberto	register struct tprounit *up;
11854359Sroberto	struct refclockproc *pp;
11954359Sroberto
12054359Sroberto	pp = peer->procptr;
121280849Scy	up = pp->unitptr;
12254359Sroberto	io_closeclock(&pp->io);
123280849Scy	if (NULL != up)
124280849Scy		free(up);
12554359Sroberto}
12654359Sroberto
12754359Sroberto
12854359Sroberto/*
12954359Sroberto * tpro_poll - called by the transmit procedure
13054359Sroberto */
13154359Srobertostatic void
13254359Srobertotpro_poll(
13354359Sroberto	int unit,
13454359Sroberto	struct peer *peer
13554359Sroberto	)
13654359Sroberto{
13754359Sroberto	register struct tprounit *up;
13854359Sroberto	struct refclockproc *pp;
13954359Sroberto	struct tproval *tp;
14054359Sroberto
14154359Sroberto	/*
14254359Sroberto	 * This is the main routine. It snatches the time from the TPRO
14354359Sroberto	 * board and tacks on a local timestamp.
14454359Sroberto	 */
14554359Sroberto	pp = peer->procptr;
146280849Scy	up = pp->unitptr;
14754359Sroberto
14854359Sroberto	tp = &up->tprodata;
14954359Sroberto	if (read(pp->io.fd, (char *)tp, sizeof(struct tproval)) < 0) {
15054359Sroberto		refclock_report(peer, CEVNT_FAULT);
15154359Sroberto		return;
15254359Sroberto	}
15354359Sroberto	get_systime(&pp->lastrec);
15454359Sroberto	pp->polls++;
15554359Sroberto
15654359Sroberto	/*
15754359Sroberto	 * We get down to business, check the timecode format and decode
15854359Sroberto	 * its contents. If the timecode has invalid length or is not in
15954359Sroberto	 * proper format, we declare bad format and exit. Note: we
16054359Sroberto	 * can't use the sec/usec conversion produced by the driver,
16154359Sroberto	 * since the year may be suspect. All format error checking is
162280849Scy	 * done by the snprintf() and sscanf() routines.
163132451Sroberto	 *
164132451Sroberto	 * Note that the refclockproc usec member has now become nsec.
165132451Sroberto	 * We could either multiply the read-in usec value by 1000 or
166132451Sroberto	 * we could pad the written string appropriately and read the
167132451Sroberto	 * resulting value in already scaled.
16854359Sroberto	 */
169280849Scy	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
170280849Scy		 "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
171280849Scy		 tp->day100, tp->day10, tp->day1, tp->hour10, tp->hour1,
172280849Scy		 tp->min10, tp->min1, tp->sec10, tp->sec1, tp->ms100,
173280849Scy		 tp->ms10, tp->ms1, tp->usec100, tp->usec10, tp->usec1,
174280849Scy		 tp->status);
175280849Scy	pp->lencode = strlen(pp->a_lastcode);
17654359Sroberto#ifdef DEBUG
17754359Sroberto	if (debug)
17854359Sroberto		printf("tpro: time %s timecode %d %s\n",
17954359Sroberto		   ulfptoa(&pp->lastrec, 6), pp->lencode,
18054359Sroberto		   pp->a_lastcode);
18154359Sroberto#endif
18254359Sroberto	if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld", &pp->day,
183132451Sroberto	    &pp->hour, &pp->minute, &pp->second, &pp->nsec)
18454359Sroberto	    != 5) {
18554359Sroberto		refclock_report(peer, CEVNT_BADTIME);
18654359Sroberto		return;
18754359Sroberto	}
188132451Sroberto	pp->nsec *= 1000;	/* Convert usec to nsec */
18954359Sroberto	if (!tp->status & 0x3)
19054359Sroberto		pp->leap = LEAP_NOTINSYNC;
19154359Sroberto	else
19254359Sroberto		pp->leap = LEAP_NOWARNING;
19354359Sroberto	if (!refclock_process(pp)) {
19454359Sroberto		refclock_report(peer, CEVNT_BADTIME);
19554359Sroberto		return;
19654359Sroberto	}
19754359Sroberto	if (pp->coderecv == pp->codeproc) {
19854359Sroberto		refclock_report(peer, CEVNT_TIMEOUT);
19954359Sroberto		return;
20054359Sroberto	}
201132451Sroberto	pp->lastref = pp->lastrec;
20254359Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
20354359Sroberto	refclock_receive(peer);
20454359Sroberto}
20554359Sroberto
20654359Sroberto#else
20754359Srobertoint refclock_tpro_bs;
20854359Sroberto#endif /* REFCLOCK */
209