1 /*	$NetBSD: refclock_bancomm.c,v 1.5 2020/05/25 20:47:25 christos 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 #ifdef HAVE_SYS_IOCTL_H
66 # include <sys/ioctl.h>
67 #endif
68 
69 struct btfp_time                /* Structure for reading 5 time words   */
70                                 /* in one ioctl(2) operation.           */
71 {
72 	unsigned short btfp_time[5];  /* Time words 0,1,2,3, and 4. (16bit)*/
73 };
74 /* SunOS5 ioctl commands definitions.*/
75 #define BTFPIOC            ( 'b'<< 8 )
76 #define IOCIO( l, n )      ( BTFPIOC | n )
77 #define IOCIOR( l, n, s )  ( BTFPIOC | n )
78 #define IOCIORN( l, n, s ) ( BTFPIOC | n )
79 #define IOCIOWN( l, n, s ) ( BTFPIOC | n )
80 
81 /***** Simple ioctl commands *****/
82 #define RUNLOCK		IOCIOR(b, 19, int )  /* Release Capture Lockout */
83 #define RCR0		IOCIOR(b, 22, int )  /* Read control register zero.*/
84 #define	WCR0		IOCIOWN(b, 23, int)  /* Write control register zero*/
85 /***** Compound ioctl commands *****/
86 
87 /* Read all 5 time words in one call.   */
88 #if defined(__FreeBSD__)
89 # define READTIME	_IOR('u', 5, struct btfp_time )
90 #else
91 # define READTIME	IOCIORN(b, 32, sizeof( struct btfp_time ))
92 #endif
93 
94 /* Solaris specific section */
95 struct	stfp_tm {
96 	int32_t tm_sec;
97 	int32_t tm_min;
98 	int32_t tm_hour;
99 	int32_t tm_mday;
100 	int32_t tm_mon;
101 	int32_t tm_year;
102 	int32_t tm_wday;
103 	int32_t tm_yday;
104 	int32_t tm_isdst;
105 };
106 
107 struct stfp_time {
108 	struct stfp_tm	tm;
109 	int32_t 	usec;			/* usec 0 - 999999 */
110 	int32_t 	hnsec;			/* hnsec 0 - 9 (hundreds of nsecs) */
111 	int32_t 	status;
112 };
113 
114 #define SELTIMEFORMAT	2
115 #	define TIME_DECIMAL 0
116 #	define TIME_BINARY	1
117 
118 #if defined(__sun__)
119 #undef	READTIME
120 #define READTIME		9
121 #endif /** __sun___ **/
122 /* end solaris specific section */
123 
124 struct vmedate {			   /* structure returned by get_vmetime.c */
125 	unsigned short year;
126 	unsigned short day;
127 	unsigned short hr;
128 	unsigned short mn;
129 	unsigned short sec;
130 	long frac;
131 	unsigned short status;
132 };
133 
134 typedef void *SYMMT_PCI_HANDLE;
135 
136 /*
137  * VME interface parameters.
138  */
139 #define VMEPRECISION    (-21)   /* precision assumed (1 us) */
140 #define USNOREFID       "BTFP"  /* or whatever */
141 #define VMEREFID        "BTFP"  /* reference id */
142 #define VMEDESCRIPTION  "Bancomm bc635 TFP" /* who we are */
143 #define VMEHSREFID      0x7f7f1000 /* 127.127.16.00 refid hi strata */
144 /* clock type 16 is used here  */
145 #define GMT           	0       /* hour offset from Greenwich */
146 
147 /*
148  * Imported from ntp_timer module
149  */
150 extern u_long current_time;     /* current time(s) */
151 
152 /*
153  * VME unit control structure.
154  * Changes made to vmeunit structure. Most members are now available in the
155  * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
156  */
157 struct vmeunit {
158 	struct vmedate vmedata; /* data returned from vme read */
159 	u_long lasttime;        /* last time clock heard from */
160 };
161 
162 /*
163  * Function prototypes
164  */
165 static  int     vme_start       (int, struct peer *);
166 static  void    vme_shutdown    (int, struct peer *);
167 static  void    vme_receive     (struct recvbuf *);
168 static  void    vme_poll        (int unit, struct peer *);
169 struct vmedate *get_datumtime(struct vmedate *);
170 void	tvme_fill(struct vmedate *, uint32_t btm[2]);
171 void	stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp);
172 static const char *get_devicename(int n);
173 
174 /* [Bug 3558] and [Bug 1674]  perlinger@ntp.org says:
175  *
176  * bcReadBinTime() is defined to use two DWORD pointers on Windows and
177  * Linux in the BANCOMM SDK.  DWORD is of course Windows-specific
178  * (*shudder*), and it is defined as 'unsigned long' under
179  * Linux/Unix. (*sigh*)
180  *
181  * This creates quite some headache.  The size of 'unsigned long' is
182  * platform/compiler/memory-model dependent (LP32 vs LP64 vs LLP64),
183  * while the card itself always creates 32bit time stamps.  What a
184  * bummer.  And DWORD has tendency to contain 64bit on Win64 (which is
185  * why we have a DWORD32 defined on Win64) so it can be used as
186  * substitute for 'UINT_PTR' in Windows API headers.  I won't even try
187  * to comment on that, because anything I have to say will not be civil.
188  *
189  * We work around this by possibly using a wrapper function that makes
190  * the necessary conversions/casts.  It might be a bit tricky to
191  * maintain the conditional logic below, but any lingering disease needs
192  * constant care to avoid a breakout.
193  */
194 #if defined(__linux__)
195   typedef unsigned long bcBinTimeT;
196 # if SIZEOF_LONG == 4
197 #   define safeReadBinTime bcReadBinTime
198 # endif
199 #elif defined(SYS_WINNT)
200   typedef DWORD bcBinTimeT;
201 # if !defined(_WIN64) || _WIN64 == 0
202 #   define safeReadBinTime bcReadBinTime
203 # endif
204 #else
205   typedef uint32_t bcBinTimeT;
206 # define safeReadBinTime bcReadBinTime
207 #endif
208 
209 /*
210  * Define the bc*() functions as weak so we can compile/link without them.
211  * Only clients with the card will have the proprietary vendor device driver
212  * and interface library needed for use on Linux/Windows platforms.
213  */
214 extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, bcBinTimeT*, bcBinTimeT*, uint8_t*);
215 extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
216 extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
217 
218 /* This is the conversion wrapper for the long/DWORD/uint32_t clash in
219  * reading binary times.
220  */
221 #ifndef safeReadBinTime
222 static uint32_t
safeReadBinTime(SYMMT_PCI_HANDLE hnd,uint32_t * pt1,uint32_t * pt2,uint8_t * p3)223 safeReadBinTime(
224 	SYMMT_PCI_HANDLE hnd,
225 	uint32_t        *pt1,
226 	uint32_t        *pt2,
227 	uint8_t         *p3
228 	)
229 {
230 	bcBinTimeT t1, t2;
231 	uint32_t   rc;
232 
233 	rc = bcReadBinTime(hnd, &t1, &t2, p3);
234 	if (rc != 0) {
235 		*pt1 = (uint32_t)t1;
236 		*pt2 = (uint32_t)t2;
237 	}
238 	return rc;
239 }
240 #endif /* !defined(safeReadBinTime) */
241 
242 /*
243  * Transfer vector
244  */
245 struct  refclock refclock_bancomm = {
246 	vme_start, 		/* start up driver */
247 	vme_shutdown,		/* shut down driver */
248 	vme_poll,		/* transmit poll message */
249 	noentry,		/* not used (old vme_control) */
250 	noentry,		/* initialize driver */
251 	noentry,		/* not used (old vme_buginfo) */
252 	NOFLAGS			/* not used */
253 };
254 
255 int fd_vme;  /* file descriptor for ioctls */
256 int regvalue;
257 int tfp_type;	/* mode selector, indicate platform and driver interface */
258 SYMMT_PCI_HANDLE stfp_handle;
259 
260 /* This helper function returns the device name based on the platform we
261  * are running on and the device number.
262  *
263  * Uses a static buffer, so the result is valid only to the next call of
264  * this function!
265  */
266 static const char*
get_devicename(int n)267 get_devicename(int n)
268 {
269 
270 #   if defined(__sun__)
271 	static const char * const template ="/dev/stfp%d";
272 #   else
273 	static const char * const template ="/dev/btfp%d";
274 #   endif
275 	static char namebuf[20];
276 
277 	snprintf(namebuf, sizeof(namebuf), template, n);
278 	namebuf[sizeof(namebuf)-1] = '\0'; /* paranoia rulez! */
279 	return namebuf;
280 }
281 
282 /*
283  * vme_start - open the VME device and initialize data for processing
284  */
285 static int
vme_start(int unit,struct peer * peer)286 vme_start(
287 	int unit,
288 	struct peer *peer
289 	)
290 {
291 	register struct vmeunit *vme;
292 	struct refclockproc *pp;
293 	int dummy;
294 	char vmedev[20];
295 
296 	tfp_type = (int)(peer->ttl);
297 	switch (tfp_type) {
298 		case 1:
299 		case 3:
300 			break;
301 		case 2:
302 			stfp_handle = bcStartPci(); 	/* init the card in lin/win */
303 			break;
304 		default:
305 			break;
306 	}
307 	/*
308 	 * Open VME device
309 	 */
310 #ifdef DEBUG
311 
312 	printf("Opening DATUM DEVICE %s\n",get_devicename(peer->refclkunit));
313 #endif
314 	if ( (fd_vme = open(get_devicename(peer->refclkunit), O_RDWR)) < 0) {
315 		msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
316 		return (0);
317 	}
318 	else  {
319 		switch (tfp_type) {
320 		  	case 1:	break;
321 			case 2: break;
322 			case 3:break;
323 			default:
324 				/* Release capture lockout in case it was set before. */
325 				if( ioctl( fd_vme, RUNLOCK, &dummy ) )
326 		    		msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
327 
328 				regvalue = 0; /* More esoteric stuff to do... */
329 				if( ioctl( fd_vme, WCR0, &regvalue ) )
330 		    		msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
331 				break;
332 		}
333 	}
334 
335 	/*
336 	 * Allocate unit structure
337 	 */
338 	vme = emalloc_zero(sizeof(struct vmeunit));
339 
340 
341 	/*
342 	 * Set up the structures
343 	 */
344 	pp = peer->procptr;
345 	pp->unitptr = vme;
346 	pp->timestarted = current_time;
347 
348 	pp->io.clock_recv = vme_receive;
349 	pp->io.srcclock = peer;
350 	pp->io.datalen = 0;
351 	pp->io.fd = fd_vme;
352 	/* shouldn't there be an io_addclock() call? */
353 
354 	/*
355 	 * All done.  Initialize a few random peer variables, then
356  	 * return success. Note that root delay and root dispersion are
357 	 * always zero for this clock.
358 	 */
359 	peer->precision = VMEPRECISION;
360 	memcpy(&pp->refid, USNOREFID,4);
361 	return (1);
362 }
363 
364 
365 /*
366  * vme_shutdown - shut down a VME clock
367  */
368 static void
vme_shutdown(int unit,struct peer * peer)369 vme_shutdown(
370 	int unit,
371 	struct peer *peer
372 	)
373 {
374 	register struct vmeunit *vme;
375 	struct refclockproc *pp;
376 
377 	/*
378 	 * Tell the I/O module to turn us off.  We're history.
379 	 */
380 	pp = peer->procptr;
381 	vme = pp->unitptr;
382 	io_closeclock(&pp->io);
383 	pp->unitptr = NULL;
384 	if (NULL != vme)
385 		free(vme);
386 	if (tfp_type == 2)
387 		bcStopPci(stfp_handle);
388 }
389 
390 
391 /*
392  * vme_receive - receive data from the VME device.
393  *
394  * Note: This interface would be interrupt-driven. We don't use that
395  * now, but include a dummy routine for possible future adventures.
396  */
397 static void
vme_receive(struct recvbuf * rbufp)398 vme_receive(
399 	struct recvbuf *rbufp
400 	)
401 {
402 }
403 
404 
405 /*
406  * vme_poll - called by the transmit procedure
407  */
408 static void
vme_poll(int unit,struct peer * peer)409 vme_poll(
410 	int unit,
411 	struct peer *peer
412 	)
413 {
414 	struct vmedate *tptr;
415 	struct vmeunit *vme;
416 	struct refclockproc *pp;
417 	time_t tloc;
418 	struct tm *tadr;
419 
420 	pp = peer->procptr;
421 	vme = pp->unitptr;        /* Here is the structure */
422 
423 	tptr = &vme->vmedata;
424 	if ((tptr = get_datumtime(tptr)) == NULL ) {
425 		refclock_report(peer, CEVNT_BADREPLY);
426 		return;
427 	}
428 
429 	get_systime(&pp->lastrec);
430 	pp->polls++;
431 	vme->lasttime = current_time;
432 
433 	/*
434 	 * Get VME time and convert to timestamp format.
435 	 * The year must come from the system clock.
436 	 */
437 
438 	  time(&tloc);
439 	  tadr = gmtime(&tloc);
440 	  tptr->year = (unsigned short)(tadr->tm_year + 1900);
441 
442 	snprintf(pp->a_lastcode,
443 		 sizeof(pp->a_lastcode),
444 		 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
445 		 tptr->day,
446 		 tptr->hr,
447 		 tptr->mn,
448 		 tptr->sec,
449 		 tptr->frac,
450 		 tptr->status);
451 
452 	pp->lencode = (u_short) strlen(pp->a_lastcode);
453 
454 	pp->day =  tptr->day;
455 	pp->hour =   tptr->hr;
456 	pp->minute =  tptr->mn;
457 	pp->second =  tptr->sec;
458 	pp->nsec =   tptr->frac;
459 
460 #ifdef DEBUG
461 	if (debug)
462 	    printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
463 		   pp->day, pp->hour, pp->minute, pp->second,
464 		   pp->nsec, tptr->status);
465 #endif
466 	if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
467 		refclock_report(peer, CEVNT_BADREPLY);
468 		return;
469 	}
470 
471 	/*
472 	 * Now, compute the reference time value. Use the heavy
473 	 * machinery for the seconds and the millisecond field for the
474 	 * fraction when present. If an error in conversion to internal
475 	 * format is found, the program declares bad data and exits.
476 	 * Note that this code does not yet know how to do the years and
477 	 * relies on the clock-calendar chip for sanity.
478 	 */
479 	if (!refclock_process(pp)) {
480 		refclock_report(peer, CEVNT_BADTIME);
481 		return;
482 	}
483 	pp->lastref = pp->lastrec;
484 	refclock_receive(peer);
485 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
486 }
487 
488 struct vmedate *
get_datumtime(struct vmedate * time_vme)489 get_datumtime(struct vmedate *time_vme)
490 {
491 	char cbuf[7];
492 	struct btfp_time vts;
493 	uint32_t btm[2];
494 	uint8_t dmy;
495 	struct stfp_time stfpm;
496 
497 	if (time_vme == NULL)
498   		time_vme = emalloc(sizeof(*time_vme));
499 
500 	switch (tfp_type) {
501 		case 1:				/* BSD, PCI, 2 32bit time words */
502 			if (ioctl(fd_vme, READTIME, &btm)) {
503 	    		msyslog(LOG_ERR, "get_bc63x error: %m");
504 				return(NULL);
505 			}
506 			tvme_fill(time_vme, btm);
507 			break;
508 
509 		case 2:				/* Linux/Windows, PCI, 2 32bit time words */
510 			if (safeReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
511 	    		msyslog(LOG_ERR, "get_datumtime error: %m");
512 				return(NULL);
513 			}
514 			tvme_fill(time_vme, btm);
515 			break;
516 
517 		case 3: /** solaris **/
518 			memset(&stfpm,0,sizeof(stfpm));
519 
520 			/* we need the time in decimal format */
521 			/* Here we rudely assume that we are the only user of the driver.
522 			 * Other programs will have to set their own time format before reading
523 			 * the time.
524 			 */
525 			if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){
526 					msyslog(LOG_ERR, "Could not set time format");
527 					return (NULL);
528 			}
529 			/* read the time */
530 			if (ioctl(fd_vme, READTIME, &stfpm)) {
531 				msyslog(LOG_ERR, "ioctl error: %m");
532 				return(NULL);
533 			}
534 			stfp_time2tvme(time_vme,  &stfpm);
535 			break;
536 
537 		default:			/* legacy bancomm card */
538 
539 			if (ioctl(fd_vme, READTIME, &vts)) {
540 				msyslog(LOG_ERR,
541 					"get_datumtime error: %m");
542 				return(NULL);
543 			}
544 			/* Get day */
545 			snprintf(cbuf, sizeof(cbuf), "%3.3x",
546 				 ((vts.btfp_time[ 0 ] & 0x000f) << 8) +
547 				  ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
548 			time_vme->day = (unsigned short)atoi(cbuf);
549 
550 			/* Get hour */
551 			snprintf(cbuf, sizeof(cbuf), "%2.2x",
552 				 vts.btfp_time[ 1 ] & 0x00ff);
553 			time_vme->hr = (unsigned short)atoi(cbuf);
554 
555 			/* Get minutes */
556 			snprintf(cbuf, sizeof(cbuf), "%2.2x",
557 				 (vts.btfp_time[ 2 ] & 0xff00) >> 8);
558 			time_vme->mn = (unsigned short)atoi(cbuf);
559 
560 			/* Get seconds */
561 			snprintf(cbuf, sizeof(cbuf), "%2.2x",
562 				 vts.btfp_time[ 2 ] & 0x00ff);
563 			time_vme->sec = (unsigned short)atoi(cbuf);
564 
565 			/* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so
566 				 we can use the TVTOTSF function  later on...*/
567 
568 			snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x",
569 				 vts.btfp_time[ 3 ],
570 				 vts.btfp_time[ 4 ] >> 8);
571 			time_vme->frac = (u_long) atoi(cbuf);
572 
573 			/* Get status bit */
574 			time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4;
575 
576 			break;
577 	}
578 
579 	if (time_vme->status)
580 		return ((void *)NULL);
581 	else
582 	    return (time_vme);
583 }
584 /* Assign values to time_vme struct. Mostly for readability */
585 void
tvme_fill(struct vmedate * time_vme,uint32_t btm[2])586 tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
587 {
588 	struct tm maj;
589 	time_t   dmaj;
590 	uint32_t dmin;
591 
592 	dmaj = btm[1];			/* syntax sugar & expansion */
593 	dmin = btm[0];			/* just syntax sugar */
594 
595 	gmtime_r(&dmaj, &maj);
596 	time_vme->day  = maj.tm_yday+1;
597 	time_vme->hr   = maj.tm_hour;
598 	time_vme->mn   = maj.tm_min;
599 	time_vme->sec  = maj.tm_sec;
600 	time_vme->frac = (dmin & 0x000fffff) * 1000;
601 	time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
602 	time_vme->status = (dmin & 0x01000000) >> 24;
603 	return;
604 }
605 
606 
607 /* Assign values to time_vme struct. Mostly for readability */
608 void
stfp_time2tvme(struct vmedate * time_vme,struct stfp_time * stfp)609 stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp)
610 {
611 
612 	time_vme->day  = stfp->tm.tm_yday+1;
613 	time_vme->hr   = stfp->tm.tm_hour;
614 	time_vme->mn   = stfp->tm.tm_min;
615 	time_vme->sec  = stfp->tm.tm_sec;
616 	time_vme->frac = stfp->usec*1000;
617 	time_vme->frac += stfp->hnsec * 100;
618 	time_vme->status = stfp->status;
619 	return;
620 }
621 #else
622 int refclock_bancomm_bs;
623 #endif /* REFCLOCK */
624