aboutsummaryrefslogtreecommitdiff
path: root/ntpd/refclock_bancomm.c
blob: 49922e39345cf29f3f217597135056d058593351 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
/* refclock_bancomm.c - clock driver for the  Datum/Bancomm bc635VME 
 * Time and Frequency Processor. It requires the BANCOMM bc635VME/
 * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x 
 * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc 
 * IIi-cEngine running Solaris 2.6.
 * 
 * Author(s): 	Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
 *		Ottawa, Canada
 *
 * Date: 	July 1999
 *
 * Note(s):	The refclock type has been defined as 16.
 *
 *		This program has been modelled after the Bancomm driver
 *		originally written by R. Schmidt of Time Service, U.S. 
 *		Naval Observatory for a HP-UX machine. Since the original
 *		authors no longer plan to maintain this code, all 
 *		references to the HP-UX vme2 driver subsystem bave been
 *		removed. Functions vme_report_event(), vme_receive(), 
 *		vme_control() and vme_buginfo() have been deleted because
 *		they are no longer being used.
 *
 *	04/28/2005 Rob Neal 
 *		Modified to add support for Symmetricom bc637PCI-U Time & 
 *		Frequency Processor. 
 *	2/21/2007 Ali Ghorashi
 *	        Modified to add support for Symmetricom bc637PCI-U Time & 
 *		Frequency Processor on Solaris.
 *		Tested on Solaris 10 with a bc635 card.
 *
 *		Card bus type (VME/VXI or PCI) and environment are specified via the
 *		"mode" keyword on the server command in ntp.conf.
 *		server 127.127.16.u prefer mode M
 *		where u is the id (usually 0) of the entry in /dev (/dev/stfp0)
 *	
 *		and M is one of the following modes: 
 *		1		: FreeBSD PCI 635/637.
 *		2		: Linux or Windows PCI 635/637.
 *		3		: Solaris PCI 635/637
 *		not specified, or other number: 
 *				: Assumed to be VME/VXI legacy Bancomm card on Solaris.
 *		Linux and Windows platforms require Symmetricoms' proprietary driver
 *		for the TFP card.
 *		Solaris requires Symmetricom's driver and its header file (freely distributed) to 
 *		be installed and running.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(REFCLOCK) && defined(CLOCK_BANC) 

#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h"
#include "ntp_stdlib.h"

#include <stdio.h>
#include <syslog.h>
#include <ctype.h>

struct btfp_time                /* Structure for reading 5 time words   */
                                /* in one ioctl(2) operation.           */
{
	unsigned short btfp_time[5];  /* Time words 0,1,2,3, and 4. (16bit)*/
};
/* SunOS5 ioctl commands definitions.*/
#define BTFPIOC            ( 'b'<< 8 )
#define IOCIO( l, n )      ( BTFPIOC | n )
#define IOCIOR( l, n, s )  ( BTFPIOC | n )
#define IOCIORN( l, n, s ) ( BTFPIOC | n )
#define IOCIOWN( l, n, s ) ( BTFPIOC | n )

/***** Simple ioctl commands *****/
#define RUNLOCK     	IOCIOR(b, 19, int )  /* Release Capture Lockout */
#define RCR0      	IOCIOR(b, 22, int )  /* Read control register zero.*/
#define	WCR0		IOCIOWN(b, 23, int)	     /* Write control register zero*/
/***** Compound ioctl commands *****/

/* Read all 5 time words in one call.   */
#define READTIME	IOCIORN(b, 32, sizeof( struct btfp_time ))

#if defined(__FreeBSD__) 
#undef  READTIME
#define READTIME	_IOR('u', 5, struct btfp_time )
#endif 

/* Solaris specific section */
struct	stfp_tm {
	int32_t tm_sec; 
	int32_t tm_min;
	int32_t tm_hour;
	int32_t tm_mday;
	int32_t tm_mon;
	int32_t tm_year;
	int32_t tm_wday;
	int32_t tm_yday;
	int32_t tm_isdst;
};

struct stfp_time {
	struct stfp_tm	tm;
	int32_t 	usec;			/* usec 0 - 999999 */
	int32_t 	hnsec;			/* hnsec 0 - 9 (hundreds of nsecs) */
	int32_t 	status;
};

#define SELTIMEFORMAT	2	
#	define TIME_DECIMAL 0
#	define TIME_BINARY	1

#if defined(__sun__)
#undef	READTIME
#define READTIME		9
#endif /** __sun___ **/
/* end solaris specific section */

struct vmedate {			   /* structure returned by get_vmetime.c */
	unsigned short year;
	unsigned short day;
	unsigned short hr;
	unsigned short mn;
	unsigned short sec;
	long frac;
	unsigned short status;
};

typedef void *SYMMT_PCI_HANDLE;

/*
 * VME interface parameters. 
 */
#define VMEPRECISION    (-21)   /* precision assumed (1 us) */
#define USNOREFID       "BTFP"  /* or whatever */
#define VMEREFID        "BTFP"  /* reference id */
#define VMEDESCRIPTION  "Bancomm bc635 TFP" /* who we are */
#define VMEHSREFID      0x7f7f1000 /* 127.127.16.00 refid hi strata */
/* clock type 16 is used here  */
#define GMT           	0       /* hour offset from Greenwich */

/*
 * Imported from ntp_timer module
 */
extern u_long current_time;     /* current time(s) */

/*
 * VME unit control structure.
 * Changes made to vmeunit structure. Most members are now available in the 
 * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
 */
struct vmeunit {
	struct vmedate vmedata; /* data returned from vme read */
	u_long lasttime;        /* last time clock heard from */
};

/*
 * Function prototypes
 */
static  int     vme_start       (int, struct peer *);
static  void    vme_shutdown    (int, struct peer *);
static  void    vme_receive     (struct recvbuf *);
static  void    vme_poll        (int unit, struct peer *);
struct vmedate *get_datumtime(struct vmedate *);	
void	tvme_fill(struct vmedate *, uint32_t btm[2]);
void	stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp);
inline const char *DEVICE_NAME(int n);


/*
 * Define the bc*() functions as weak so we can compile/link without them.
 * Only clients with the card will have the proprietary vendor device driver
 * and interface library needed for use on Linux/Windows platforms.
 */
extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);

/*
 * Transfer vector
 */
struct  refclock refclock_bancomm = {
	vme_start, 		/* start up driver */
	vme_shutdown,		/* shut down driver */
	vme_poll,		/* transmit poll message */
	noentry,		/* not used (old vme_control) */
	noentry,		/* initialize driver */ 
	noentry,		/* not used (old vme_buginfo) */ 
	NOFLAGS			/* not used */
};

int fd_vme;  /* file descriptor for ioctls */
int regvalue;
int tfp_type;	/* mode selector, indicate platform and driver interface */
SYMMT_PCI_HANDLE stfp_handle;

/** 
 * this macro returns the device name based on
 * the platform we are running on and the device number
 */
#if defined(__sun__)
inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;}
#else
inline const char* DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/btfp%d",n);return s;}
#endif /**__sun__**/

/*
 * vme_start - open the VME device and initialize data for processing
 */
static int
vme_start(
	int unit,
	struct peer *peer
	)
{
	register struct vmeunit *vme;
	struct refclockproc *pp;
	int dummy;
	char vmedev[20];
	
	tfp_type = (int)(peer->ttl);
	switch (tfp_type) {		
		case 1:
		case 3:
			break;
		case 2:
			stfp_handle = bcStartPci(); 	/* init the card in lin/win */
			break;
		default:
			break;
	}
	/*
	 * Open VME device
	 */
#ifdef DEBUG

	printf("Opening DATUM DEVICE %s\n",DEVICE_NAME(peer->refclkunit));
#endif
	if ( (fd_vme = open(DEVICE_NAME(peer->refclkunit), O_RDWR)) < 0) {
		msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
		return (0);
	}
	else  { 
		switch (tfp_type) {
		  	case 1:	break;
			case 2: break;
			case 3:break;
			default: 
				/* Release capture lockout in case it was set before. */
				if( ioctl( fd_vme, RUNLOCK, &dummy ) )
		    		msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");

				regvalue = 0; /* More esoteric stuff to do... */
				if( ioctl( fd_vme, WCR0, &regvalue ) )
		    		msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
				break;
		}
	}

	/*
	 * Allocate unit structure
	 */
	vme = emalloc_zero(sizeof(struct vmeunit));


	/*
	 * Set up the structures
	 */
	pp = peer->procptr;
	pp->unitptr = vme;
	pp->timestarted = current_time;

	pp->io.clock_recv = vme_receive;
	pp->io.srcclock = peer;
	pp->io.datalen = 0;
	pp->io.fd = fd_vme;
	/* shouldn't there be an io_addclock() call? */

	/*
	 * All done.  Initialize a few random peer variables, then
 	 * return success. Note that root delay and root dispersion are
	 * always zero for this clock.
	 */
	peer->precision = VMEPRECISION;
	memcpy(&pp->refid, USNOREFID,4);
	return (1);
}


/*
 * vme_shutdown - shut down a VME clock
 */
static void
vme_shutdown(
	int unit, 
	struct peer *peer
	)
{
	register struct vmeunit *vme;
	struct refclockproc *pp;

	/*
	 * Tell the I/O module to turn us off.  We're history.
	 */
	pp = peer->procptr;
	vme = pp->unitptr;
	io_closeclock(&pp->io);
	pp->unitptr = NULL;
	if (NULL != vme)
		free(vme);
	if (tfp_type == 2)
		bcStopPci(stfp_handle); 
}


/*
 * vme_receive - receive data from the VME device.
 *
 * Note: This interface would be interrupt-driven. We don't use that
 * now, but include a dummy routine for possible future adventures.
 */
static void
vme_receive(
	struct recvbuf *rbufp
	)
{
}


/*
 * vme_poll - called by the transmit procedure
 */
static void
vme_poll(
	int unit,
	struct peer *peer
	)
{
	struct vmedate *tptr; 
	struct vmeunit *vme;
	struct refclockproc *pp;
	time_t tloc;
	struct tm *tadr;
        
	pp = peer->procptr;	 
	vme = pp->unitptr;        /* Here is the structure */

	tptr = &vme->vmedata; 
	if ((tptr = get_datumtime(tptr)) == NULL ) {
		refclock_report(peer, CEVNT_BADREPLY);
		return;
	}

	get_systime(&pp->lastrec);
	pp->polls++;
	vme->lasttime = current_time;

	/*
	 * Get VME time and convert to timestamp format. 
	 * The year must come from the system clock.
	 */
	
	  time(&tloc);
	  tadr = gmtime(&tloc);
	  tptr->year = (unsigned short)(tadr->tm_year + 1900);

	snprintf(pp->a_lastcode,
		 sizeof(pp->a_lastcode),
		 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
		 tptr->day, 
		 tptr->hr, 
		 tptr->mn,
		 tptr->sec, 
		 tptr->frac, 
		 tptr->status);

	pp->lencode = (u_short) strlen(pp->a_lastcode);

	pp->day =  tptr->day;
	pp->hour =   tptr->hr;
	pp->minute =  tptr->mn;
	pp->second =  tptr->sec;
	pp->nsec =   tptr->frac;	

#ifdef DEBUG
	if (debug)
	    printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
		   pp->day, pp->hour, pp->minute, pp->second,
		   pp->nsec, tptr->status);
#endif
	if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
		refclock_report(peer, CEVNT_BADREPLY);
		return;
	}

	/*
	 * Now, compute the reference time value. Use the heavy
	 * machinery for the seconds and the millisecond field for the
	 * fraction when present. If an error in conversion to internal
	 * format is found, the program declares bad data and exits.
	 * Note that this code does not yet know how to do the years and
	 * relies on the clock-calendar chip for sanity.
	 */
	if (!refclock_process(pp)) {
		refclock_report(peer, CEVNT_BADTIME);
		return;
	}
	pp->lastref = pp->lastrec;
	refclock_receive(peer);
	record_clock_stats(&peer->srcadr, pp->a_lastcode);
}

struct vmedate *
get_datumtime(struct vmedate *time_vme)
{
	char cbuf[7];
	struct btfp_time vts;
	uint32_t btm[2];
	uint8_t dmy;
	struct stfp_time stfpm;
	
	if (time_vme == NULL)
  		time_vme = emalloc(sizeof(*time_vme));

	switch (tfp_type) {
		case 1:				/* BSD, PCI, 2 32bit time words */
			if (ioctl(fd_vme, READTIME, &btm)) {
	    		msyslog(LOG_ERR, "get_bc63x error: %m");
				return(NULL);
			}
			tvme_fill(time_vme, btm);
			break;

		case 2:				/* Linux/Windows, PCI, 2 32bit time words */
			if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
	    		msyslog(LOG_ERR, "get_datumtime error: %m"); 
				return(NULL);
			}
			tvme_fill(time_vme, btm);
			break;
			
		case 3: /** solaris **/
			memset(&stfpm,0,sizeof(stfpm));
			
			/* we need the time in decimal format */
			/* Here we rudely assume that we are the only user of the driver.
			 * Other programs will have to set their own time format before reading 
			 * the time.
			 */
			if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){	
					msyslog(LOG_ERR, "Could not set time format");
					return (NULL);	
			}
			/* read the time */
			if (ioctl(fd_vme, READTIME, &stfpm)) {
				msyslog(LOG_ERR, "ioctl error: %m");
				return(NULL);
			}
			stfp_time2tvme(time_vme,  &stfpm);
			break;			

		default:			/* legacy bancomm card */

			if (ioctl(fd_vme, READTIME, &vts)) {
				msyslog(LOG_ERR,
					"get_datumtime error: %m");
				return(NULL);
			}
			/* Get day */
			snprintf(cbuf, sizeof(cbuf), "%3.3x",
				 ((vts.btfp_time[ 0 ] & 0x000f) << 8) +
				  ((vts.btfp_time[ 1 ] & 0xff00) >> 8));  
			time_vme->day = (unsigned short)atoi(cbuf);

			/* Get hour */
			snprintf(cbuf, sizeof(cbuf), "%2.2x",
				 vts.btfp_time[ 1 ] & 0x00ff);
			time_vme->hr = (unsigned short)atoi(cbuf);

			/* Get minutes */
			snprintf(cbuf, sizeof(cbuf), "%2.2x",
				 (vts.btfp_time[ 2 ] & 0xff00) >> 8);
			time_vme->mn = (unsigned short)atoi(cbuf);

			/* Get seconds */
			snprintf(cbuf, sizeof(cbuf), "%2.2x",
				 vts.btfp_time[ 2 ] & 0x00ff);
			time_vme->sec = (unsigned short)atoi(cbuf);

			/* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so
				 we can use the TVTOTSF function  later on...*/

			snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x",
				 vts.btfp_time[ 3 ],
				 vts.btfp_time[ 4 ] >> 8);
			time_vme->frac = (u_long) atoi(cbuf);

			/* Get status bit */
			time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4;

			break;
	}

	if (time_vme->status) 
		return ((void *)NULL);
	else
	    return (time_vme);
}
/* Assign values to time_vme struct. Mostly for readability */
void
tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
{
	struct tm maj;
	uint32_t dmaj, dmin;

	dmaj = btm[1];			/* syntax sugar */
	dmin = btm[0];

	gmtime_r(&dmaj, &maj);
	time_vme->day  = maj.tm_yday+1;
	time_vme->hr   = maj.tm_hour;
	time_vme->mn   = maj.tm_min;
	time_vme->sec  = maj.tm_sec;
	time_vme->frac = (dmin & 0x000fffff) * 1000; 
	time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
	time_vme->status = (dmin & 0x01000000) >> 24;
	return;
}


/* Assign values to time_vme struct. Mostly for readability */
void
stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp)
{

	time_vme->day  = stfp->tm.tm_yday+1;
	time_vme->hr   = stfp->tm.tm_hour;
	time_vme->mn   = stfp->tm.tm_min;
	time_vme->sec  = stfp->tm.tm_sec;
	time_vme->frac = stfp->usec*1000;  
	time_vme->frac += stfp->hnsec * 100;
	time_vme->status = stfp->status;
	return;
}
#else
int refclock_bancomm_bs;
#endif /* REFCLOCK */