xref: /freebsd/contrib/ntp/ntpd/refclock_bancomm.c (revision 40a8ac8f62b535d30349faf28cf47106b7041b83)
1 /* refclock_bancomm.c - clock driver for the  Datum/Bancomm bc635VME
2  * Time and Frequency Processor. It requires the BANCOMM bc635VME/
3  * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x
4  * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc
5  * IIi-cEngine running Solaris 2.6.
6  *
7  * Author(s): 	Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
8  *		Ottawa, Canada
9  *
10  * Date: 	July 1999
11  *
12  * Note(s):	The refclock type has been defined as 16.
13  *
14  *		This program has been modelled after the Bancomm driver
15  *		originally written by R. Schmidt of Time Service, U.S.
16  *		Naval Observatory for a HP-UX machine. Since the original
17  *		authors no longer plan to maintain this code, all
18  *		references to the HP-UX vme2 driver subsystem bave been
19  *		removed. Functions vme_report_event(), vme_receive(),
20  *		vme_control() and vme_buginfo() have been deleted because
21  *		they are no longer being used.
22  *
23  *		The time on the bc635 TFP must be set to GMT due to the
24  *		fact that NTP makes use of GMT for all its calculations.
25  *
26  *		Installation of the Datum/Bancomm driver creates the
27  *		device file /dev/btfp0
28  *
29  *	04/28/2005 Rob Neal
30  *		Modified to add support for Symmetricom bc637PCI-U Time &
31  *		Frequency Processor.
32  *		Card bus type (VME/VXI or PCI) and environment are specified via the
33  *		"mode" keyword on the server command in ntp.conf.
34  *		server 127.127.16.u prefer mode m (...)
35  *		Modes currently supported are
36  *		1		: FreeBSD PCI 635/637.
37  *		2		: Linux or Windows PCI 635/637.
38  *		not specified, or other number:
39  *				: Assumed to be VME/VXI legacy Bancomm card on Solaris.
40  *		Linux and Windows platforms require Symmetricoms' proprietary driver
41  *		for the TFP card.
42  *		Tested on FreeBSD 5.3 with a 637 card.
43  */
44 
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48 
49 #if defined(REFCLOCK) && defined(CLOCK_BANC)
50 
51 #include "ntpd.h"
52 #include "ntp_io.h"
53 #include "ntp_refclock.h"
54 #include "ntp_unixtime.h"
55 #include "ntp_stdlib.h"
56 
57 #include <stdio.h>
58 #include <syslog.h>
59 #include <ctype.h>
60 
61 /*  STUFF BY RES */
62 struct btfp_time                /* Structure for reading 5 time words   */
63                                 /* in one ioctl(2) operation.           */
64 {
65 	unsigned short btfp_time[5];  /* Time words 0,1,2,3, and 4. (16bit)*/
66 };
67 /* SunOS5 ioctl commands definitions.*/
68 #define BTFPIOC            ( 'b'<< 8 )
69 #define IOCIO( l, n )      ( BTFPIOC | n )
70 #define IOCIOR( l, n, s )  ( BTFPIOC | n )
71 #define IOCIORN( l, n, s ) ( BTFPIOC | n )
72 #define IOCIOWN( l, n, s ) ( BTFPIOC | n )
73 
74 /***** Simple ioctl commands *****/
75 #define RUNLOCK     	IOCIOR(b, 19, int )  /* Release Capture Lockout */
76 #define RCR0      	IOCIOR(b, 22, int )  /* Read control register zero.*/
77 #define	WCR0		IOCIOWN(b, 23, int)	     /* Write control register zero*/
78 /***** Compound ioctl commands *****/
79 
80 /* Read all 5 time words in one call.   */
81 #define READTIME	IOCIORN(b, 32, sizeof( struct btfp_time ))
82 
83 #if defined(__FreeBSD__)
84 #undef  READTIME
85 #define READTIME	_IOR('u', 5, struct btfp_time )
86 #endif
87 
88 #define VMEFD "/dev/btfp0"
89 
90 struct vmedate {               /* structure returned by get_vmetime.c */
91 	unsigned short year;
92 	unsigned short day;
93 	unsigned short hr;
94 	unsigned short mn;
95 	unsigned short sec;
96 	long frac;
97 	unsigned short status;
98 };
99 
100 /* END OF STUFF FROM RES */
101 typedef void *SYMMT_PCI_HANDLE;
102 
103 /*
104  * VME interface parameters.
105  */
106 #define VMEPRECISION    (-21)   /* precision assumed (1 us) */
107 #define USNOREFID       "BTFP"  /* or whatever */
108 #define VMEREFID        "BTFP"  /* reference id */
109 #define VMEDESCRIPTION  "Bancomm bc635 TFP" /* who we are */
110 #define VMEHSREFID      0x7f7f1000 /* 127.127.16.00 refid hi strata */
111 /* clock type 16 is used here  */
112 #define GMT           	0       /* hour offset from Greenwich */
113 
114 /*
115  * Imported from ntp_timer module
116  */
117 extern u_long current_time;     /* current time(s) */
118 
119 /*
120  * Imported from ntpd module
121  */
122 extern volatile int debug;               /* global debug flag */
123 
124 /*
125  * VME unit control structure.
126  * Changes made to vmeunit structure. Most members are now available in the
127  * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
128  */
129 struct vmeunit {
130 	struct vmedate vmedata; /* data returned from vme read */
131 	u_long lasttime;        /* last time clock heard from */
132 };
133 
134 /*
135  * Function prototypes
136  */
137 static  int     vme_start       (int, struct peer *);
138 static  void    vme_shutdown    (int, struct peer *);
139 static  void    vme_receive     (struct recvbuf *);
140 static  void    vme_poll        (int unit, struct peer *);
141 struct vmedate *get_datumtime(struct vmedate *);
142 void 	tvme_fill(struct vmedate *, uint32_t btm[2]);
143 /*
144  * Define the bc*() functions as weak so we can compile/link without them.
145  * Only clients with the card will have the proprietary vendor device driver
146  * and interface library needed for use on Linux/Windows platforms.
147  */
148 extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
149 extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
150 extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
151 
152 /*
153  * Transfer vector
154  */
155 struct  refclock refclock_bancomm = {
156 	vme_start, 		/* start up driver */
157 	vme_shutdown,		/* shut down driver */
158 	vme_poll,		/* transmit poll message */
159 	noentry,		/* not used (old vme_control) */
160 	noentry,		/* initialize driver */
161 	noentry,		/* not used (old vme_buginfo) */
162 	NOFLAGS			/* not used */
163 };
164 
165 int fd_vme;  /* file descriptor for ioctls */
166 int regvalue;
167 int tfp_type;	/* mode selector, indicate platform and driver interface */
168 SYMMT_PCI_HANDLE stfp_handle;
169 
170 
171 /*
172  * vme_start - open the VME device and initialize data for processing
173  */
174 static int
175 vme_start(
176 	int unit,
177 	struct peer *peer
178 	)
179 {
180 	register struct vmeunit *vme;
181 	struct refclockproc *pp;
182 	int dummy;
183 	char vmedev[20];
184 
185 	tfp_type = (int)(peer->ttl);
186 	switch (tfp_type) {
187 		case 1:
188 			break;
189 		case 2:
190 			stfp_handle = bcStartPci(); 	/* init the card in lin/win */
191 			break;
192 		default:
193 			break;
194 	}
195 	/*
196 	 * Open VME device
197 	 */
198 #ifdef DEBUG
199 
200 	printf("Opening DATUM VME DEVICE \n");
201 #endif
202 	if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) {
203 		msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
204 		return (0);
205 	}
206 	else  {
207 		switch (tfp_type) {
208 		  	case 1:	break;
209 			case 2: break;
210 			default:
211 				/* Release capture lockout in case it was set before. */
212 				if( ioctl( fd_vme, RUNLOCK, &dummy ) )
213 		    		msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
214 
215 				regvalue = 0; /* More esoteric stuff to do... */
216 				if( ioctl( fd_vme, WCR0, &regvalue ) )
217 		    		msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
218 				break;
219 		}
220 	}
221 
222 	/*
223 	 * Allocate unit structure
224 	 */
225 	vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
226 	bzero((char *)vme, sizeof(struct vmeunit));
227 
228 
229 	/*
230 	 * Set up the structures
231 	 */
232 	pp = peer->procptr;
233 	pp->unitptr = (caddr_t) vme;
234 	pp->timestarted = current_time;
235 
236 	pp->io.clock_recv = vme_receive;
237 	pp->io.srcclock = (caddr_t)peer;
238 	pp->io.datalen = 0;
239 	pp->io.fd = fd_vme;
240 
241 	/*
242 	 * All done.  Initialize a few random peer variables, then
243  	 * return success. Note that root delay and root dispersion are
244 	 * always zero for this clock.
245 	 */
246 	peer->precision = VMEPRECISION;
247 	memcpy(&pp->refid, USNOREFID,4);
248 	return (1);
249 }
250 
251 
252 /*
253  * vme_shutdown - shut down a VME clock
254  */
255 static void
256 vme_shutdown(
257 	int unit,
258 	struct peer *peer
259 	)
260 {
261 	register struct vmeunit *vme;
262 	struct refclockproc *pp;
263 
264 	/*
265 	 * Tell the I/O module to turn us off.  We're history.
266 	 */
267 	pp = peer->procptr;
268 	vme = (struct vmeunit *)pp->unitptr;
269 	io_closeclock(&pp->io);
270 	pp->unitptr = NULL;
271 	free(vme);
272 	if (tfp_type == 2) bcStopPci(stfp_handle);
273 }
274 
275 
276 /*
277  * vme_receive - receive data from the VME device.
278  *
279  * Note: This interface would be interrupt-driven. We don't use that
280  * now, but include a dummy routine for possible future adventures.
281  */
282 static void
283 vme_receive(
284 	struct recvbuf *rbufp
285 	)
286 {
287 }
288 
289 
290 /*
291  * vme_poll - called by the transmit procedure
292  */
293 static void
294 vme_poll(
295 	int unit,
296 	struct peer *peer
297 	)
298 {
299 	struct vmedate *tptr;
300 	struct vmeunit *vme;
301 	struct refclockproc *pp;
302 	time_t tloc;
303 	struct tm *tadr;
304 
305 	pp = peer->procptr;
306 	vme = (struct vmeunit *)pp->unitptr;        /* Here is the structure */
307 
308 	tptr = &vme->vmedata;
309 	if ((tptr = get_datumtime(tptr)) == NULL ) {
310 		refclock_report(peer, CEVNT_BADREPLY);
311 		return;
312 	}
313 
314 	get_systime(&pp->lastrec);
315 	pp->polls++;
316 	vme->lasttime = current_time;
317 
318 	/*
319 	 * Get VME time and convert to timestamp format.
320 	 * The year must come from the system clock.
321 	 */
322 
323 	  time(&tloc);
324 	  tadr = gmtime(&tloc);
325 	  tptr->year = (unsigned short)(tadr->tm_year + 1900);
326 
327 	sprintf(pp->a_lastcode,
328 		"%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
329 		tptr->day,
330 		tptr->hr,
331 		tptr->mn,
332 		tptr->sec,
333 		tptr->frac,
334 		tptr->status);
335 
336 	pp->lencode = (u_short) strlen(pp->a_lastcode);
337 
338 	pp->day =  tptr->day;
339 	pp->hour =   tptr->hr;
340 	pp->minute =  tptr->mn;
341 	pp->second =  tptr->sec;
342 	pp->nsec =   tptr->frac;
343 
344 #ifdef DEBUG
345 	if (debug)
346 	    printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
347 		   pp->day, pp->hour, pp->minute, pp->second,
348 		   pp->nsec, tptr->status);
349 #endif
350 	if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
351 		refclock_report(peer, CEVNT_BADREPLY);
352 		return;
353 	}
354 
355 	/*
356 	 * Now, compute the reference time value. Use the heavy
357 	 * machinery for the seconds and the millisecond field for the
358 	 * fraction when present. If an error in conversion to internal
359 	 * format is found, the program declares bad data and exits.
360 	 * Note that this code does not yet know how to do the years and
361 	 * relies on the clock-calendar chip for sanity.
362 	 */
363 	if (!refclock_process(pp)) {
364 		refclock_report(peer, CEVNT_BADTIME);
365 		return;
366 	}
367 	pp->lastref = pp->lastrec;
368 	refclock_receive(peer);
369 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
370 }
371 
372 struct vmedate *
373 get_datumtime(struct vmedate *time_vme)
374 {
375 	char cbuf[7];
376 	struct btfp_time vts;
377 	uint32_t btm[2];
378 	uint8_t dmy;
379 
380 	if ( time_vme == (struct vmedate *)NULL) {
381   	  time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
382 	}
383 
384 	switch (tfp_type) {
385 		case 1:				/* BSD, PCI, 2 32bit time words */
386 			if (ioctl(fd_vme, READTIME, &btm)) {
387 	    		msyslog(LOG_ERR, "get_bc63x error: %m");
388 				return(NULL);
389 			}
390 			tvme_fill(time_vme, btm);
391 			break;
392 
393 		case 2:				/* Linux/Windows, PCI, 2 32bit time words */
394 			if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
395 	    		msyslog(LOG_ERR, "get_datumtime error: %m");
396 				return(NULL);
397 			}
398 			tvme_fill(time_vme, btm);
399 			break;
400 
401 		default:			/* legacy bancomm card */
402 
403 			if (ioctl(fd_vme, READTIME, &vts)) {
404 	    		msyslog(LOG_ERR, "get_datumtime error: %m");
405 				return(NULL);
406 			}
407 			/* Get day */
408 			sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
409 				((vts.btfp_time[ 1 ] & 0xff00) >> 8));
410 			time_vme->day = (unsigned short)atoi(cbuf);
411 
412 			/* Get hour */
413 			sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
414 
415 			time_vme->hr = (unsigned short)atoi(cbuf);
416 
417 			/* Get minutes */
418 			sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
419 			time_vme->mn = (unsigned short)atoi(cbuf);
420 
421 			/* Get seconds */
422 			sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
423 			time_vme->sec = (unsigned short)atoi(cbuf);
424 
425 			/* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so
426 				 we can use the TVTOTSF function  later on...*/
427 
428 			sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
429 			vts.btfp_time[ 4 ]>>8);
430 
431 			time_vme->frac = (u_long) atoi(cbuf);
432 
433 			/* Get status bit */
434 			time_vme->status = (vts.btfp_time[0] & 0x0010) >>4;
435 
436 			break;
437 	}
438 
439 	if (time_vme->status)
440 		return ((void *)NULL);
441 	else
442 	    return (time_vme);
443 }
444 /* Assign values to time_vme struct. Mostly for readability */
445 void
446 tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
447 {
448 	struct tm maj;
449 	uint32_t dmaj, dmin;
450 
451 	dmaj = btm[1];			/* syntax sugar */
452 	dmin = btm[0];
453 
454 	gmtime_r(&dmaj, &maj);
455 	time_vme->day  = maj.tm_yday+1;
456 	time_vme->hr   = maj.tm_hour;
457 	time_vme->mn   = maj.tm_min;
458 	time_vme->sec  = maj.tm_sec;
459 	time_vme->frac = (dmin & 0x000fffff) * 1000;
460 	time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
461 	time_vme->status = (dmin & 0x01000000) >> 24;
462 	return;
463 }
464 
465 #else
466 int refclock_bancomm_bs;
467 #endif /* REFCLOCK */
468