xref: /freebsd/contrib/ntp/ntpd/refclock_tpro.c (revision 9c2daa00)
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  */
45c0b746e5SOllivier Robert static	int	tpro_start	(int, struct peer *);
46c0b746e5SOllivier Robert static	void	tpro_shutdown	(int, struct peer *);
47c0b746e5SOllivier Robert 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
tpro_start(int unit,struct peer * peer)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 	 */
80c0b746e5SOllivier Robert 	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 	 */
90c0b746e5SOllivier Robert 	up = emalloc_zero(sizeof(*up));
91c0b746e5SOllivier Robert 	pp = peer->procptr;
92c0b746e5SOllivier Robert 	pp->io.clock_recv = noentry;
93c0b746e5SOllivier Robert 	pp->io.srcclock = peer;
94c0b746e5SOllivier Robert 	pp->io.datalen = 0;
95c0b746e5SOllivier Robert 	pp->io.fd = fd;
96c0b746e5SOllivier Robert 	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
tpro_shutdown(int unit,struct peer * peer)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;
121c0b746e5SOllivier Robert 	up = pp->unitptr;
122c0b746e5SOllivier Robert 	io_closeclock(&pp->io);
123c0b746e5SOllivier Robert 	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
tpro_poll(int unit,struct peer * peer)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;
146c0b746e5SOllivier Robert 	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
162c0b746e5SOllivier Robert 	 * done by the snprintf() and sscanf() routines.
163c0b746e5SOllivier Robert 	 *
164c0b746e5SOllivier Robert 	 * Note that the refclockproc usec member has now become nsec.
165c0b746e5SOllivier Robert 	 * We could either multiply the read-in usec value by 1000 or
166c0b746e5SOllivier Robert 	 * we could pad the written string appropriately and read the
1679c2daa00SOllivier Robert 	 * resulting value in already scaled.
1689c2daa00SOllivier Robert 	 */
1699c2daa00SOllivier Robert 	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
1709c2daa00SOllivier Robert 		 "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
1719c2daa00SOllivier 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,
183c0b746e5SOllivier Robert 	    &pp->hour, &pp->minute, &pp->second, &pp->nsec)
184c0b746e5SOllivier Robert 	    != 5) {
185c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
186c0b746e5SOllivier Robert 		return;
1879c2daa00SOllivier Robert 	}
188c0b746e5SOllivier Robert 	pp->nsec *= 1000;	/* Convert usec to nsec */
189c0b746e5SOllivier Robert 	if (!tp->status & 0x3)
190c0b746e5SOllivier Robert 		pp->leap = LEAP_NOTINSYNC;
191c0b746e5SOllivier Robert 	else
1929c2daa00SOllivier 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 	}
201c0b746e5SOllivier 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
2079c2daa00SOllivier Robert int refclock_tpro_bs;
2089c2daa00SOllivier Robert #endif /* REFCLOCK */
209c0b746e5SOllivier Robert