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