xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_tpro.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
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