xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_tt560.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: refclock_tt560.c,v 1.6 2024/08/18 20:47:19 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel /*
4abb0f93cSkardel  * refclock_tt560 - clock driver for the TrueTime 560 IRIG-B decoder
5abb0f93cSkardel  */
6abb0f93cSkardel 
7abb0f93cSkardel #ifdef HAVE_CONFIG_H
8abb0f93cSkardel #include <config.h>
9abb0f93cSkardel #endif
10abb0f93cSkardel 
11abb0f93cSkardel #if defined(REFCLOCK) && defined(CLOCK_TT560)
12abb0f93cSkardel 
13abb0f93cSkardel #include "ntpd.h"
14abb0f93cSkardel #include "ntp_io.h"
15abb0f93cSkardel #include "ntp_refclock.h"
16abb0f93cSkardel #include "ntp_unixtime.h"
17abb0f93cSkardel #include "sys/tt560_api.h"
18abb0f93cSkardel #include "ntp_stdlib.h"
19abb0f93cSkardel 
20abb0f93cSkardel #include <stdio.h>
21abb0f93cSkardel #include <ctype.h>
22abb0f93cSkardel 
23abb0f93cSkardel /*
24abb0f93cSkardel  * This driver supports the TrueTime 560 IRIG-B decoder for the PCI bus.
25abb0f93cSkardel  */
26abb0f93cSkardel 
27abb0f93cSkardel /*
28abb0f93cSkardel  * TT560 interface definitions
29abb0f93cSkardel  */
30abb0f93cSkardel #define	DEVICE		 "/dev/tt560%d" /* device name and unit */
31abb0f93cSkardel #define	PRECISION	(-20)	/* precision assumed (1 us) */
32abb0f93cSkardel #define	REFID		"IRIG"	/* reference ID */
33abb0f93cSkardel #define	DESCRIPTION	"TrueTime 560 IRIG-B PCI Decoder"
34abb0f93cSkardel 
35abb0f93cSkardel /*
36abb0f93cSkardel  * Unit control structure
37abb0f93cSkardel  */
38abb0f93cSkardel struct tt560unit {
39abb0f93cSkardel 	tt_mem_space_t	 *tt_mem;	/* mapped address of PCI board */
40abb0f93cSkardel 	time_freeze_reg_t tt560rawt;	/* data returned from PCI board */
41abb0f93cSkardel };
42abb0f93cSkardel 
43abb0f93cSkardel typedef union byteswap_u
44abb0f93cSkardel {
45abb0f93cSkardel     unsigned int long_word;
46abb0f93cSkardel     unsigned char byte[4];
47abb0f93cSkardel } byteswap_t;
48abb0f93cSkardel 
49abb0f93cSkardel /*
50abb0f93cSkardel  * Function prototypes
51abb0f93cSkardel  */
52abb0f93cSkardel static	int	tt560_start	(int, struct peer *);
53abb0f93cSkardel static	void	tt560_shutdown	(int, struct peer *);
54abb0f93cSkardel static	void	tt560_poll	(int unit, struct peer *);
55abb0f93cSkardel 
56abb0f93cSkardel /*
57abb0f93cSkardel  * Transfer vector
58abb0f93cSkardel  */
59abb0f93cSkardel struct	refclock refclock_tt560 = {
60abb0f93cSkardel 	tt560_start,		/* clock_start    */
61abb0f93cSkardel 	tt560_shutdown,		/* clock_shutdown */
62abb0f93cSkardel 	tt560_poll,		/* clock_poll     */
63abb0f93cSkardel 	noentry,		/* clock_control (not used) */
64abb0f93cSkardel 	noentry,		/* clock_init    (not used) */
65abb0f93cSkardel 	noentry,		/* clock_buginfo (not used) */
66abb0f93cSkardel 	NOFLAGS			/* clock_flags   (not used) */
67abb0f93cSkardel };
68abb0f93cSkardel 
69abb0f93cSkardel 
70abb0f93cSkardel /*
71abb0f93cSkardel  * tt560_start - open the TT560 device and initialize data for processing
72abb0f93cSkardel  */
73abb0f93cSkardel static int
74abb0f93cSkardel tt560_start(
75abb0f93cSkardel 	int unit,
76abb0f93cSkardel 	struct peer *peer
77abb0f93cSkardel 	)
78abb0f93cSkardel {
79abb0f93cSkardel 	register struct tt560unit *up;
80abb0f93cSkardel 	struct refclockproc *pp;
81abb0f93cSkardel 	char	device[20];
82abb0f93cSkardel 	int	fd;
83abb0f93cSkardel 	caddr_t membase;
84abb0f93cSkardel 
85abb0f93cSkardel 	/*
86abb0f93cSkardel 	 * Open TT560 device
87abb0f93cSkardel 	 */
888585484eSchristos 	snprintf(device, sizeof(device), DEVICE, unit);
89abb0f93cSkardel 	fd = open(device, O_RDWR);
90abb0f93cSkardel 	if (fd == -1) {
91abb0f93cSkardel 		msyslog(LOG_ERR, "tt560_start: open of %s: %m", device);
92abb0f93cSkardel 		return (0);
93abb0f93cSkardel 	}
94abb0f93cSkardel 
95abb0f93cSkardel 	/*
96abb0f93cSkardel 	 * Map the device registers into user space.
97abb0f93cSkardel 	 */
98abb0f93cSkardel 	membase = mmap ((caddr_t) 0, TTIME_MEMORY_SIZE,
99abb0f93cSkardel 			PROT_READ | PROT_WRITE,
100abb0f93cSkardel 			MAP_SHARED, fd, (off_t)0);
101abb0f93cSkardel 
102abb0f93cSkardel 	if (membase == (caddr_t) -1) {
103abb0f93cSkardel 		msyslog(LOG_ERR, "tt560_start: mapping of %s: %m", device);
104abb0f93cSkardel 		(void) close(fd);
105abb0f93cSkardel 		return (0);
106abb0f93cSkardel 	}
107abb0f93cSkardel 
108abb0f93cSkardel 	/*
109abb0f93cSkardel 	 * Allocate and initialize unit structure
110abb0f93cSkardel 	 */
111abb0f93cSkardel 	if (!(up = (struct tt560unit *) emalloc(sizeof(struct tt560unit)))) {
112abb0f93cSkardel 		(void) close(fd);
113abb0f93cSkardel 		return (0);
114abb0f93cSkardel 	}
115abb0f93cSkardel 	memset((char *)up, 0, sizeof(struct tt560unit));
116abb0f93cSkardel 	up->tt_mem = (tt_mem_space_t *)membase;
117abb0f93cSkardel 	pp = peer->procptr;
118abb0f93cSkardel 	pp->io.clock_recv = noentry;
119abb0f93cSkardel 	pp->io.srcclock = (caddr_t)peer;
120abb0f93cSkardel 	pp->io.datalen = 0;
121abb0f93cSkardel 	pp->io.fd = fd;
122abb0f93cSkardel 	pp->unitptr = (caddr_t)up;
123abb0f93cSkardel 
124abb0f93cSkardel 	/*
125abb0f93cSkardel 	 * Initialize miscellaneous peer variables
126abb0f93cSkardel 	 */
127abb0f93cSkardel 	peer->precision = PRECISION;
128abb0f93cSkardel 	pp->clockdesc = DESCRIPTION;
129abb0f93cSkardel 	memcpy((char *)&pp->refid, REFID, 4);
130abb0f93cSkardel 	return (1);
131abb0f93cSkardel }
132abb0f93cSkardel 
133abb0f93cSkardel 
134abb0f93cSkardel /*
135abb0f93cSkardel  * tt560_shutdown - shut down the clock
136abb0f93cSkardel  */
137abb0f93cSkardel static void
138abb0f93cSkardel tt560_shutdown(
139abb0f93cSkardel 	int unit,
140abb0f93cSkardel 	struct peer *peer
141abb0f93cSkardel 	)
142abb0f93cSkardel {
143abb0f93cSkardel 	register struct tt560unit *up;
144abb0f93cSkardel 	struct refclockproc *pp;
145abb0f93cSkardel 
146abb0f93cSkardel 	pp = peer->procptr;
147abb0f93cSkardel 	up = (struct tt560unit *)pp->unitptr;
148abb0f93cSkardel 	io_closeclock(&pp->io);
149abb0f93cSkardel 	free(up);
150abb0f93cSkardel }
151abb0f93cSkardel 
152abb0f93cSkardel 
153abb0f93cSkardel /*
154abb0f93cSkardel  * tt560_poll - called by the transmit procedure
155abb0f93cSkardel  */
156abb0f93cSkardel static void
157abb0f93cSkardel tt560_poll(
158abb0f93cSkardel 	int unit,
159abb0f93cSkardel 	struct peer *peer
160abb0f93cSkardel 	)
161abb0f93cSkardel {
162abb0f93cSkardel 	register struct tt560unit *up;
163abb0f93cSkardel 	struct refclockproc       *pp;
164abb0f93cSkardel 	time_freeze_reg_t         *tp;
165abb0f93cSkardel 	tt_mem_space_t            *mp;
166abb0f93cSkardel 
167abb0f93cSkardel 	int i;
168abb0f93cSkardel 	unsigned int *p_time_t, *tt_mem_t;
169abb0f93cSkardel 
170abb0f93cSkardel 	/*
171abb0f93cSkardel 	 * This is the main routine. It snatches the time from the TT560
172abb0f93cSkardel 	 * board and tacks on a local timestamp.
173abb0f93cSkardel 	 */
174abb0f93cSkardel 	pp = peer->procptr;
175abb0f93cSkardel 	up = (struct tt560unit *)pp->unitptr;
176abb0f93cSkardel 	mp = up->tt_mem;
177abb0f93cSkardel 	tp = &up->tt560rawt;
178abb0f93cSkardel 
179abb0f93cSkardel 	p_time_t = (unsigned int *)tp;
180abb0f93cSkardel 	tt_mem_t = (unsigned int *)&mp->time_freeze_reg;
181abb0f93cSkardel 
182abb0f93cSkardel 	*tt_mem_t = 0;		/* update the time freeze register */
183abb0f93cSkardel 				/* and copy time stamp to memory */
184abb0f93cSkardel 	for (i=0; i < TIME_FREEZE_REG_LEN; i++) {
185abb0f93cSkardel 	    *p_time_t = byte_swap(*tt_mem_t);
186abb0f93cSkardel 	     p_time_t++;
187abb0f93cSkardel 	     tt_mem_t++;
188abb0f93cSkardel 	}
189abb0f93cSkardel 
190abb0f93cSkardel 	get_systime(&pp->lastrec);
191abb0f93cSkardel 	pp->polls++;
192abb0f93cSkardel 
193abb0f93cSkardel 	/*
194abb0f93cSkardel 	 * We get down to business, check the timecode format and decode
195abb0f93cSkardel 	 * its contents. If the timecode has invalid length or is not in
196abb0f93cSkardel 	 * proper format, we declare bad format and exit. Note: we
197abb0f93cSkardel 	 * can't use the sec/usec conversion produced by the driver,
198abb0f93cSkardel 	 * since the year may be suspect. All format error checking is
1998585484eSchristos 	 * done by the snprintf() and sscanf() routines.
200abb0f93cSkardel 	 */
2018585484eSchristos 	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
202abb0f93cSkardel 	    "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
203abb0f93cSkardel 	    tp->hun_day,  tp->tens_day,  tp->unit_day,
204abb0f93cSkardel 	                  tp->tens_hour, tp->unit_hour,
205abb0f93cSkardel 	                  tp->tens_min,  tp->unit_min,
206abb0f93cSkardel 	                  tp->tens_sec,  tp->unit_sec,
207abb0f93cSkardel 	    tp->hun_ms,   tp->tens_ms,   tp->unit_ms,
208abb0f93cSkardel 	    tp->hun_us,   tp->tens_us,   tp->unit_us,
209abb0f93cSkardel 	    tp->status);
210abb0f93cSkardel 	    pp->lencode = strlen(pp->a_lastcode);
211abb0f93cSkardel #ifdef DEBUG
212abb0f93cSkardel 	if (debug)
213abb0f93cSkardel 		printf("tt560: time %s timecode %d %s\n",
214abb0f93cSkardel 		   ulfptoa(&pp->lastrec, 6), pp->lencode,
215abb0f93cSkardel 		   pp->a_lastcode);
216abb0f93cSkardel #endif
217abb0f93cSkardel 	if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld",
218abb0f93cSkardel                   &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->usec)
219abb0f93cSkardel 	    != 5) {
220abb0f93cSkardel 		refclock_report(peer, CEVNT_BADTIME);
221abb0f93cSkardel 		return;
222abb0f93cSkardel 	}
223abb0f93cSkardel 	if ((tp->status & 0x6) != 0x6)
224abb0f93cSkardel 		pp->leap = LEAP_NOTINSYNC;
225abb0f93cSkardel 	else
226abb0f93cSkardel 		pp->leap = LEAP_NOWARNING;
227abb0f93cSkardel 	if (!refclock_process(pp)) {
228abb0f93cSkardel 		refclock_report(peer, CEVNT_BADTIME);
229abb0f93cSkardel 		return;
230abb0f93cSkardel 	}
231abb0f93cSkardel 	if (pp->coderecv == pp->codeproc) {
232abb0f93cSkardel 		refclock_report(peer, CEVNT_TIMEOUT);
233abb0f93cSkardel 		return;
234abb0f93cSkardel 	}
235abb0f93cSkardel 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
236abb0f93cSkardel 	refclock_receive(peer);
237abb0f93cSkardel }
238abb0f93cSkardel 
239abb0f93cSkardel /******************************************************************
240abb0f93cSkardel  *
241abb0f93cSkardel  *  byte_swap
242abb0f93cSkardel  *
243abb0f93cSkardel  *  Inputs: 32 bit integer
244abb0f93cSkardel  *
245abb0f93cSkardel  *  Output: byte swapped 32 bit integer.
246abb0f93cSkardel  *
247abb0f93cSkardel  *  This routine is used to compensate for the byte alignment
248abb0f93cSkardel  *  differences between big-endian and little-endian integers.
249abb0f93cSkardel  *
250abb0f93cSkardel  ******************************************************************/
251abb0f93cSkardel static unsigned int
252abb0f93cSkardel byte_swap(unsigned int input_num)
253abb0f93cSkardel {
254abb0f93cSkardel     byteswap_t    byte_swap;
255abb0f93cSkardel     unsigned char temp;
256abb0f93cSkardel 
257abb0f93cSkardel     byte_swap.long_word = input_num;
258abb0f93cSkardel 
259abb0f93cSkardel     temp              = byte_swap.byte[3];
260abb0f93cSkardel     byte_swap.byte[3] = byte_swap.byte[0];
261abb0f93cSkardel     byte_swap.byte[0] = temp;
262abb0f93cSkardel 
263abb0f93cSkardel     temp              = byte_swap.byte[2];
264abb0f93cSkardel     byte_swap.byte[2] = byte_swap.byte[1];
265abb0f93cSkardel     byte_swap.byte[1] = temp;
266abb0f93cSkardel 
267abb0f93cSkardel     return (byte_swap.long_word);
268abb0f93cSkardel }
269abb0f93cSkardel 
270abb0f93cSkardel #else
271*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT
272abb0f93cSkardel #endif /* REFCLOCK */
273