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
66struct 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 */
93struct	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
105struct 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
122struct 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
132typedef 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 */
148extern u_long current_time;     /* current time(s) */
149
150/*
151 * Imported from ntpd module
152 */
153extern 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 */
160struct 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 */
168static  int     vme_start       (int, struct peer *);
169static  void    vme_shutdown    (int, struct peer *);
170static  void    vme_receive     (struct recvbuf *);
171static  void    vme_poll        (int unit, struct peer *);
172struct vmedate *get_datumtime(struct vmedate *);
173void	tvme_fill(struct vmedate *, uint32_t btm[2]);
174void	stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp);
175inline 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 */
183extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
184extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
185extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
186
187/*
188 * Transfer vector
189 */
190struct  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
200int fd_vme;  /* file descriptor for ioctls */
201int regvalue;
202int tfp_type;	/* mode selector, indicate platform and driver interface */
203SYMMT_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__)
210inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;}
211#else
212inline 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 */
218static int
219vme_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 */
301static void
302vme_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 */
330static void
331vme_receive(
332	struct recvbuf *rbufp
333	)
334{
335}
336
337
338/*
339 * vme_poll - called by the transmit procedure
340 */
341static void
342vme_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
421struct vmedate *
422get_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 */
518void
519tvme_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 */
540void
541stfp_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
554int refclock_bancomm_bs;
555#endif /* REFCLOCK */
556