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