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