1c0b746e5SOllivier Robert /* 2c0b746e5SOllivier Robert * refclock_tpro - clock driver for the KSI/Odetics TPRO-S IRIG-B reader 3c0b746e5SOllivier Robert */ 4c0b746e5SOllivier Robert 5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 6c0b746e5SOllivier Robert #include <config.h> 7c0b746e5SOllivier Robert #endif 8c0b746e5SOllivier Robert 9c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_TPRO) 10c0b746e5SOllivier Robert 11c0b746e5SOllivier Robert #include "ntpd.h" 12c0b746e5SOllivier Robert #include "ntp_io.h" 13c0b746e5SOllivier Robert #include "ntp_refclock.h" 14c0b746e5SOllivier Robert #include "ntp_unixtime.h" 15c0b746e5SOllivier Robert #include "sys/tpro.h" 16c0b746e5SOllivier Robert #include "ntp_stdlib.h" 17c0b746e5SOllivier Robert 18224ba2bdSOllivier Robert #include <stdio.h> 19224ba2bdSOllivier Robert #include <ctype.h> 20224ba2bdSOllivier Robert 21c0b746e5SOllivier Robert /* 22c0b746e5SOllivier Robert * This driver supports the KSI/Odetecs TPRO-S IRIG-B reader and TPRO- 23c0b746e5SOllivier Robert * SAT GPS receiver for the Sun Microsystems SBus. It requires that the 24c0b746e5SOllivier Robert * tpro.o device driver be installed and loaded. 25c0b746e5SOllivier Robert */ 26c0b746e5SOllivier Robert 27c0b746e5SOllivier Robert /* 28c0b746e5SOllivier Robert * TPRO interface definitions 29c0b746e5SOllivier Robert */ 30c0b746e5SOllivier Robert #define DEVICE "/dev/tpro%d" /* device name and unit */ 31c0b746e5SOllivier Robert #define PRECISION (-20) /* precision assumed (1 us) */ 32c0b746e5SOllivier Robert #define REFID "IRIG" /* reference ID */ 33c0b746e5SOllivier Robert #define DESCRIPTION "KSI/Odetics TPRO/S IRIG Interface" /* WRU */ 34c0b746e5SOllivier Robert 35c0b746e5SOllivier Robert /* 36c0b746e5SOllivier Robert * Unit control structure 37c0b746e5SOllivier Robert */ 38c0b746e5SOllivier Robert struct tprounit { 39c0b746e5SOllivier Robert struct tproval tprodata; /* data returned from tpro read */ 40c0b746e5SOllivier Robert }; 41c0b746e5SOllivier Robert 42c0b746e5SOllivier Robert /* 43c0b746e5SOllivier Robert * Function prototypes 44c0b746e5SOllivier Robert */ 452b15cb3dSCy Schubert static int tpro_start (int, struct peer *); 462b15cb3dSCy Schubert static void tpro_shutdown (int, struct peer *); 472b15cb3dSCy Schubert static void tpro_poll (int unit, struct peer *); 48c0b746e5SOllivier Robert 49c0b746e5SOllivier Robert /* 50c0b746e5SOllivier Robert * Transfer vector 51c0b746e5SOllivier Robert */ 52c0b746e5SOllivier Robert struct refclock refclock_tpro = { 53c0b746e5SOllivier Robert tpro_start, /* start up driver */ 54c0b746e5SOllivier Robert tpro_shutdown, /* shut down driver */ 55c0b746e5SOllivier Robert tpro_poll, /* transmit poll message */ 56c0b746e5SOllivier Robert noentry, /* not used (old tpro_control) */ 57c0b746e5SOllivier Robert noentry, /* initialize driver (not used) */ 58c0b746e5SOllivier Robert noentry, /* not used (old tpro_buginfo) */ 59c0b746e5SOllivier Robert NOFLAGS /* not used */ 60c0b746e5SOllivier Robert }; 61c0b746e5SOllivier Robert 62c0b746e5SOllivier Robert 63c0b746e5SOllivier Robert /* 64c0b746e5SOllivier Robert * tpro_start - open the TPRO device and initialize data for processing 65c0b746e5SOllivier Robert */ 66c0b746e5SOllivier Robert static int 67c0b746e5SOllivier Robert tpro_start( 68c0b746e5SOllivier Robert int unit, 69c0b746e5SOllivier Robert struct peer *peer 70c0b746e5SOllivier Robert ) 71c0b746e5SOllivier Robert { 72c0b746e5SOllivier Robert register struct tprounit *up; 73c0b746e5SOllivier Robert struct refclockproc *pp; 74c0b746e5SOllivier Robert char device[20]; 75c0b746e5SOllivier Robert int fd; 76c0b746e5SOllivier Robert 77c0b746e5SOllivier Robert /* 78c0b746e5SOllivier Robert * Open TPRO device 79c0b746e5SOllivier Robert */ 802b15cb3dSCy Schubert snprintf(device, sizeof(device), DEVICE, unit); 81c0b746e5SOllivier Robert fd = open(device, O_RDONLY | O_NDELAY, 0777); 82c0b746e5SOllivier Robert if (fd == -1) { 83c0b746e5SOllivier Robert msyslog(LOG_ERR, "tpro_start: open of %s: %m", device); 84c0b746e5SOllivier Robert return (0); 85c0b746e5SOllivier Robert } 86c0b746e5SOllivier Robert 87c0b746e5SOllivier Robert /* 88c0b746e5SOllivier Robert * Allocate and initialize unit structure 89c0b746e5SOllivier Robert */ 902b15cb3dSCy Schubert up = emalloc_zero(sizeof(*up)); 91c0b746e5SOllivier Robert pp = peer->procptr; 92c0b746e5SOllivier Robert pp->io.clock_recv = noentry; 932b15cb3dSCy Schubert pp->io.srcclock = peer; 94c0b746e5SOllivier Robert pp->io.datalen = 0; 95c0b746e5SOllivier Robert pp->io.fd = fd; 962b15cb3dSCy Schubert pp->unitptr = up; 97c0b746e5SOllivier Robert 98c0b746e5SOllivier Robert /* 99c0b746e5SOllivier Robert * Initialize miscellaneous peer variables 100c0b746e5SOllivier Robert */ 101c0b746e5SOllivier Robert peer->precision = PRECISION; 102c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 103c0b746e5SOllivier Robert memcpy((char *)&pp->refid, REFID, 4); 104c0b746e5SOllivier Robert return (1); 105c0b746e5SOllivier Robert } 106c0b746e5SOllivier Robert 107c0b746e5SOllivier Robert 108c0b746e5SOllivier Robert /* 109c0b746e5SOllivier Robert * tpro_shutdown - shut down the clock 110c0b746e5SOllivier Robert */ 111c0b746e5SOllivier Robert static void 112c0b746e5SOllivier Robert tpro_shutdown( 113c0b746e5SOllivier Robert int unit, 114c0b746e5SOllivier Robert struct peer *peer 115c0b746e5SOllivier Robert ) 116c0b746e5SOllivier Robert { 117c0b746e5SOllivier Robert register struct tprounit *up; 118c0b746e5SOllivier Robert struct refclockproc *pp; 119c0b746e5SOllivier Robert 120c0b746e5SOllivier Robert pp = peer->procptr; 1212b15cb3dSCy Schubert up = pp->unitptr; 122c0b746e5SOllivier Robert io_closeclock(&pp->io); 1232b15cb3dSCy Schubert if (NULL != up) 124c0b746e5SOllivier Robert free(up); 125c0b746e5SOllivier Robert } 126c0b746e5SOllivier Robert 127c0b746e5SOllivier Robert 128c0b746e5SOllivier Robert /* 129c0b746e5SOllivier Robert * tpro_poll - called by the transmit procedure 130c0b746e5SOllivier Robert */ 131c0b746e5SOllivier Robert static void 132c0b746e5SOllivier Robert tpro_poll( 133c0b746e5SOllivier Robert int unit, 134c0b746e5SOllivier Robert struct peer *peer 135c0b746e5SOllivier Robert ) 136c0b746e5SOllivier Robert { 137c0b746e5SOllivier Robert register struct tprounit *up; 138c0b746e5SOllivier Robert struct refclockproc *pp; 139c0b746e5SOllivier Robert struct tproval *tp; 140c0b746e5SOllivier Robert 141c0b746e5SOllivier Robert /* 142c0b746e5SOllivier Robert * This is the main routine. It snatches the time from the TPRO 143c0b746e5SOllivier Robert * board and tacks on a local timestamp. 144c0b746e5SOllivier Robert */ 145c0b746e5SOllivier Robert pp = peer->procptr; 1462b15cb3dSCy Schubert up = pp->unitptr; 147c0b746e5SOllivier Robert 148c0b746e5SOllivier Robert tp = &up->tprodata; 149c0b746e5SOllivier Robert if (read(pp->io.fd, (char *)tp, sizeof(struct tproval)) < 0) { 150c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 151c0b746e5SOllivier Robert return; 152c0b746e5SOllivier Robert } 153c0b746e5SOllivier Robert get_systime(&pp->lastrec); 154c0b746e5SOllivier Robert pp->polls++; 155c0b746e5SOllivier Robert 156c0b746e5SOllivier Robert /* 157c0b746e5SOllivier Robert * We get down to business, check the timecode format and decode 158c0b746e5SOllivier Robert * its contents. If the timecode has invalid length or is not in 159c0b746e5SOllivier Robert * proper format, we declare bad format and exit. Note: we 160c0b746e5SOllivier Robert * can't use the sec/usec conversion produced by the driver, 161c0b746e5SOllivier Robert * since the year may be suspect. All format error checking is 1622b15cb3dSCy Schubert * done by the snprintf() and sscanf() routines. 1639c2daa00SOllivier Robert * 1649c2daa00SOllivier Robert * Note that the refclockproc usec member has now become nsec. 1659c2daa00SOllivier Robert * We could either multiply the read-in usec value by 1000 or 1669c2daa00SOllivier Robert * we could pad the written string appropriately and read the 1679c2daa00SOllivier Robert * resulting value in already scaled. 168c0b746e5SOllivier Robert */ 1692b15cb3dSCy Schubert snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 170c0b746e5SOllivier Robert "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x", 171c0b746e5SOllivier Robert tp->day100, tp->day10, tp->day1, tp->hour10, tp->hour1, 172c0b746e5SOllivier Robert tp->min10, tp->min1, tp->sec10, tp->sec1, tp->ms100, 173c0b746e5SOllivier Robert tp->ms10, tp->ms1, tp->usec100, tp->usec10, tp->usec1, 174c0b746e5SOllivier Robert tp->status); 175c0b746e5SOllivier Robert pp->lencode = strlen(pp->a_lastcode); 176c0b746e5SOllivier Robert #ifdef DEBUG 177c0b746e5SOllivier Robert if (debug) 178c0b746e5SOllivier Robert printf("tpro: time %s timecode %d %s\n", 179c0b746e5SOllivier Robert ulfptoa(&pp->lastrec, 6), pp->lencode, 180c0b746e5SOllivier Robert pp->a_lastcode); 181c0b746e5SOllivier Robert #endif 182c0b746e5SOllivier Robert if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld", &pp->day, 1839c2daa00SOllivier Robert &pp->hour, &pp->minute, &pp->second, &pp->nsec) 184c0b746e5SOllivier Robert != 5) { 185c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 186c0b746e5SOllivier Robert return; 187c0b746e5SOllivier Robert } 1889c2daa00SOllivier Robert pp->nsec *= 1000; /* Convert usec to nsec */ 189c0b746e5SOllivier Robert if (!tp->status & 0x3) 190c0b746e5SOllivier Robert pp->leap = LEAP_NOTINSYNC; 191c0b746e5SOllivier Robert else 192c0b746e5SOllivier Robert pp->leap = LEAP_NOWARNING; 193c0b746e5SOllivier Robert if (!refclock_process(pp)) { 194c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 195c0b746e5SOllivier Robert return; 196c0b746e5SOllivier Robert } 197c0b746e5SOllivier Robert if (pp->coderecv == pp->codeproc) { 198c0b746e5SOllivier Robert refclock_report(peer, CEVNT_TIMEOUT); 199c0b746e5SOllivier Robert return; 200c0b746e5SOllivier Robert } 2019c2daa00SOllivier Robert pp->lastref = pp->lastrec; 202c0b746e5SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 203c0b746e5SOllivier Robert refclock_receive(peer); 204c0b746e5SOllivier Robert } 205c0b746e5SOllivier Robert 206c0b746e5SOllivier Robert #else 207*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 208c0b746e5SOllivier Robert #endif /* REFCLOCK */ 209