1 /*	$NetBSD: refclock_tt560.c,v 1.1.1.1 2009/12/13 16:56:04 kardel 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
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 	(void)sprintf(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 	peer->burst = NSTAGE;
129 	pp->clockdesc = DESCRIPTION;
130 	memcpy((char *)&pp->refid, REFID, 4);
131 	return (1);
132 }
133 
134 
135 /*
136  * tt560_shutdown - shut down the clock
137  */
138 static void
139 tt560_shutdown(
140 	int unit,
141 	struct peer *peer
142 	)
143 {
144 	register struct tt560unit *up;
145 	struct refclockproc *pp;
146 
147 	pp = peer->procptr;
148 	up = (struct tt560unit *)pp->unitptr;
149 	io_closeclock(&pp->io);
150 	free(up);
151 }
152 
153 
154 /*
155  * tt560_poll - called by the transmit procedure
156  */
157 static void
158 tt560_poll(
159 	int unit,
160 	struct peer *peer
161 	)
162 {
163 	register struct tt560unit *up;
164 	struct refclockproc       *pp;
165 	time_freeze_reg_t         *tp;
166 	tt_mem_space_t            *mp;
167 
168 	int i;
169 	unsigned int *p_time_t, *tt_mem_t;
170 
171 	/*
172 	 * This is the main routine. It snatches the time from the TT560
173 	 * board and tacks on a local timestamp.
174 	 */
175 	pp = peer->procptr;
176 	up = (struct tt560unit *)pp->unitptr;
177 	mp = up->tt_mem;
178 	tp = &up->tt560rawt;
179 
180 	p_time_t = (unsigned int *)tp;
181 	tt_mem_t = (unsigned int *)&mp->time_freeze_reg;
182 
183 	*tt_mem_t = 0;		/* update the time freeze register */
184 				/* and copy time stamp to memory */
185 	for (i=0; i < TIME_FREEZE_REG_LEN; i++) {
186 	    *p_time_t = byte_swap(*tt_mem_t);
187 	     p_time_t++;
188 	     tt_mem_t++;
189 	}
190 
191 	get_systime(&pp->lastrec);
192 	pp->polls++;
193 
194 	/*
195 	 * We get down to business, check the timecode format and decode
196 	 * its contents. If the timecode has invalid length or is not in
197 	 * proper format, we declare bad format and exit. Note: we
198 	 * can't use the sec/usec conversion produced by the driver,
199 	 * since the year may be suspect. All format error checking is
200 	 * done by the sprintf() and sscanf() routines.
201 	 */
202 	sprintf(pp->a_lastcode,
203 	    "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
204 	    tp->hun_day,  tp->tens_day,  tp->unit_day,
205 	                  tp->tens_hour, tp->unit_hour,
206 	                  tp->tens_min,  tp->unit_min,
207 	                  tp->tens_sec,  tp->unit_sec,
208 	    tp->hun_ms,   tp->tens_ms,   tp->unit_ms,
209 	    tp->hun_us,   tp->tens_us,   tp->unit_us,
210 	    tp->status);
211 	    pp->lencode = strlen(pp->a_lastcode);
212 #ifdef DEBUG
213 	if (debug)
214 		printf("tt560: time %s timecode %d %s\n",
215 		   ulfptoa(&pp->lastrec, 6), pp->lencode,
216 		   pp->a_lastcode);
217 #endif
218 	if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld",
219                   &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->usec)
220 	    != 5) {
221 		refclock_report(peer, CEVNT_BADTIME);
222 		return;
223 	}
224 	if ((tp->status & 0x6) != 0x6)
225 		pp->leap = LEAP_NOTINSYNC;
226 	else
227 		pp->leap = LEAP_NOWARNING;
228 	if (!refclock_process(pp)) {
229 		refclock_report(peer, CEVNT_BADTIME);
230 		return;
231 	}
232 	if (peer->burst > 0)
233 		return;
234 	if (pp->coderecv == pp->codeproc) {
235 		refclock_report(peer, CEVNT_TIMEOUT);
236 		return;
237 	}
238 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
239 	refclock_receive(peer);
240 	peer->burst = NSTAGE;
241 }
242 
243 /******************************************************************
244  *
245  *  byte_swap
246  *
247  *  Inputs: 32 bit integer
248  *
249  *  Output: byte swapped 32 bit integer.
250  *
251  *  This routine is used to compensate for the byte alignment
252  *  differences between big-endian and little-endian integers.
253  *
254  ******************************************************************/
255 static unsigned int
256 byte_swap(unsigned int input_num)
257 {
258     byteswap_t    byte_swap;
259     unsigned char temp;
260 
261     byte_swap.long_word = input_num;
262 
263     temp              = byte_swap.byte[3];
264     byte_swap.byte[3] = byte_swap.byte[0];
265     byte_swap.byte[0] = temp;
266 
267     temp              = byte_swap.byte[2];
268     byte_swap.byte[2] = byte_swap.byte[1];
269     byte_swap.byte[1] = temp;
270 
271     return (byte_swap.long_word);
272 }
273 
274 #else
275 int refclock_tt560_bs;
276 #endif /* REFCLOCK */
277