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