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