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