refclock_bancomm.c revision 54359
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
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33
34#if defined(REFCLOCK) && defined(CLOCK_BANC)
35#include <stdio.h>
36#include <syslog.h>
37#include <ctype.h>
38#include <string.h>
39#include <strings.h>
40#include <sys/time.h>
41
42#include "ntpd.h"
43#include "ntp_io.h"
44#include "ntp_refclock.h"
45#include "ntp_unixtime.h"
46#include "ntp_stdlib.h"
47
48/*  STUFF BY RES */
49struct btfp_time                /* Structure for reading 5 time words   */
50                                /* in one ioctl(2) operation.           */
51{
52	unsigned short btfp_time[5];  /* Time words 0,1,2,3, and 4. (16bit)*/
53};
54
55/* SunOS5 ioctl commands definitions.*/
56#define BTFPIOC            ( 'b'<< 8 )
57#define IOCIO( l, n )      ( BTFPIOC | n )
58#define IOCIOR( l, n, s )  ( BTFPIOC | n )
59#define IOCIORN( l, n, s ) ( BTFPIOC | n )
60#define IOCIOWN( l, n, s ) ( BTFPIOC | n )
61
62/***** Simple ioctl commands *****/
63#define RUNLOCK     	IOCIOR(b, 19, int )  /* Release Capture Lockout */
64#define RCR0      	IOCIOR(b, 22, int )  /* Read control register zero.*/
65#define	WCR0		IOCIOWN(b, 23, int)	     /* Write control register zero*/
66
67/***** Compound ioctl commands *****/
68
69/* Read all 5 time words in one call.   */
70#define READTIME	IOCIORN(b, 32, sizeof( struct btfp_time ))
71#define VMEFD "/dev/btfp0"
72
73struct vmedate {               /* structure returned by get_vmetime.c */
74	unsigned short year;
75	unsigned short day;
76	unsigned short hr;
77	unsigned short mn;
78	unsigned short sec;
79	unsigned long frac;
80	unsigned short status;
81};
82
83/* END OF STUFF FROM RES */
84
85/*
86 * Definitions
87 */
88#define MAXUNITS 2              /* max number of VME units */
89
90/*
91 * VME interface parameters.
92 */
93#define VMEPRECISION    (-21)   /* precision assumed (1 us) */
94#define USNOREFID       "BTFP"  /* or whatever */
95#define VMEREFID        "BTFP"  /* reference id */
96#define VMEDESCRIPTION  "Bancomm bc635 TFP" /* who we are */
97#define VMEHSREFID      0x7f7f1000 /* 127.127.16.00 refid hi strata */
98/* clock type 16 is used here  */
99#define GMT           	0       /* hour offset from Greenwich */
100
101/*
102 * Imported from ntp_timer module
103 */
104extern u_long current_time;     /* current time(s) */
105
106/*
107 * Imported from ntpd module
108 */
109extern int debug;               /* global debug flag */
110
111/*
112 * VME unit control structure.
113 * Changes made to vmeunit structure. Most members are now available in the
114 * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
115 */
116struct vmeunit {
117	struct vmedate vmedata; /* data returned from vme read */
118	u_long lasttime;        /* last time clock heard from */
119};
120
121/*
122 * Keep the fudge factors separately so they can be set even
123 * when no clock is configured.
124 */
125static double fudgefactor[MAXUNITS];
126static u_char stratumtouse[MAXUNITS];
127static u_char sloppyclockflag[MAXUNITS];
128
129/*
130 * Function prototypes
131 */
132static  void    vme_init        (void);
133static  int     vme_start       (int, struct peer *);
134static  void    vme_shutdown    (int, struct peer *);
135static  void    vme_receive     (struct recvbuf *);
136static  void    vme_poll        (int unit, struct peer *);
137struct vmedate *get_datumtime(struct vmedate *);
138
139/*
140 * Transfer vector
141 */
142struct  refclock refclock_bancomm = {
143	vme_start,
144	vme_shutdown,
145	vme_poll,
146	noentry,       /* not used (old vme_control) */
147	vme_init,
148	noentry,       /* not used (old vme_buginfo) */
149	NOFLAGS
150};
151
152int fd_vme;  /* file descriptor for ioctls */
153int regvalue;
154
155/*
156 * vme_init - initialize internal vme driver data
157 */
158static void
159vme_init(void)
160{
161	register int i;
162
163	/*
164	 * Initialize fudge factors to default.
165	 */
166	for (i = 0; i < MAXUNITS; i++) {
167		fudgefactor[i]  = 0.0;
168		stratumtouse[i] = 0;
169		sloppyclockflag[i] = 0;
170	}
171}
172
173/*
174 * vme_start - open the VME device and initialize data for processing
175 */
176static int
177vme_start(
178	int unit,
179	struct peer *peer
180	)
181{
182	register struct vmeunit *vme;
183	struct refclockproc *pp;
184	int dummy;
185	char vmedev[20];
186
187	/*
188	 * Check configuration info.
189	 */
190	if (unit >= MAXUNITS) {
191		msyslog(LOG_ERR, "vme_start: unit %d invalid", unit);
192		return (0);
193	}
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  { /* Release capture lockout in case it was set from before. */
207		if( ioctl( fd_vme, RUNLOCK, &dummy ) )
208		    msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
209
210		regvalue = 0; /* More esoteric stuff to do... */
211		if( ioctl( fd_vme, WCR0, &regvalue ) )
212		    msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
213	}
214
215	/*
216	 * Allocate unit structure
217	 */
218	vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
219	bzero((char *)vme, sizeof(struct vmeunit));
220
221
222	/*
223	 * Set up the structures
224	 */
225	pp = peer->procptr;
226	pp->unitptr = (caddr_t) vme;
227	pp->timestarted = current_time;
228
229	pp->io.clock_recv = vme_receive;
230	pp->io.srcclock = (caddr_t)peer;
231	pp->io.datalen = 0;
232	pp->io.fd = fd_vme;
233
234	/*
235	 * All done.  Initialize a few random peer variables, then
236 	 * return success. Note that root delay and root dispersion are
237	 * always zero for this clock.
238	 */
239	pp->leap = LEAP_NOWARNING;
240	peer->precision = VMEPRECISION;
241	peer->stratum = stratumtouse[unit];
242	memcpy( (char *)&peer->refid, USNOREFID,4);
243
244	peer->refid = htonl(VMEHSREFID);
245
246	return (1);
247}
248
249
250/*
251 * vme_shutdown - shut down a VME clock
252 */
253static void
254vme_shutdown(
255	int unit,
256	struct peer *peer
257	)
258{
259	register struct vmeunit *vme;
260	struct refclockproc *pp;
261
262	pp = peer->procptr;
263
264	if (unit >= MAXUNITS) {
265		msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit);
266		return;
267	}
268
269	/*
270	 * Tell the I/O module to turn us off.  We're history.
271	 */
272	vme = (struct vmeunit *)pp->unitptr;
273	io_closeclock(&pp->io);
274	pp->unitptr = NULL;
275	free(vme);
276}
277
278
279/*
280 * vme_receive - receive data from the VME device.
281 *
282 * Note: This interface would be interrupt-driven. We don't use that
283 * now, but include a dummy routine for possible future adventures.
284 */
285static void
286vme_receive(
287	struct recvbuf *rbufp
288	)
289{
290}
291
292
293/*
294 * vme_poll - called by the transmit procedure
295 */
296static void
297vme_poll(
298	int unit,
299	struct peer *peer
300	)
301{
302	struct vmedate *tptr;
303	struct vmeunit *vme;
304	struct refclockproc *pp;
305	time_t tloc;
306	struct tm *tadr;
307
308	pp = peer->procptr;
309	vme = (struct vmeunit *)pp->unitptr;        /* Here is the structure */
310
311	tptr = &vme->vmedata;
312	if ((tptr = get_datumtime(tptr)) == NULL ) {
313		refclock_report(peer, CEVNT_BADREPLY);
314		return;
315	}
316
317	get_systime(&pp->lastrec);
318	pp->polls++;
319	vme->lasttime = current_time;
320
321	/*
322	 * Get VME time and convert to timestamp format.
323	 * The year must come from the system clock.
324	 */
325
326	  time(&tloc);
327	  tadr = gmtime(&tloc);
328	  tptr->year = (unsigned short)(tadr->tm_year + 1900);
329
330
331	sprintf(pp->a_lastcode,
332		"%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
333		tptr->day,
334		tptr->hr,
335		tptr->mn,
336		tptr->sec,
337		tptr->frac,
338		tptr->status);
339
340	pp->lencode = (u_short) strlen(pp->a_lastcode);
341
342	pp->day =  tptr->day;
343	pp->hour =   tptr->hr;
344	pp->minute =  tptr->mn;
345	pp->second =  tptr->sec;
346	pp->usec =   tptr->frac;
347
348#ifdef DEBUG
349	if (debug)
350	    printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
351		   pp->day, pp->hour, pp->minute, pp->second,
352		   pp->usec, tptr->status);
353#endif
354	if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
355		refclock_report(peer, CEVNT_BADREPLY);
356		return;
357	}
358
359	/*
360	 * Now, compute the reference time value. Use the heavy
361	 * machinery for the seconds and the millisecond field for the
362	 * fraction when present. If an error in conversion to internal
363	 * format is found, the program declares bad data and exits.
364	 * Note that this code does not yet know how to do the years and
365	 * relies on the clock-calendar chip for sanity.
366	 */
367	if (!refclock_process(pp)) {
368		refclock_report(peer, CEVNT_BADTIME);
369		return;
370	}
371	record_clock_stats(&peer->srcadr, pp->a_lastcode);
372	refclock_receive(peer);
373}
374
375struct vmedate *
376get_datumtime(struct vmedate *time_vme)
377{
378	unsigned short  status;
379	char cbuf[7];
380	struct btfp_time vts;
381
382	if ( time_vme == (struct vmedate *)NULL) {
383  	  time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
384	}
385
386	if( ioctl(fd_vme, READTIME, &vts))
387	    msyslog(LOG_ERR, "get_datumtime error: %m");
388
389	/* if you want to actually check the validity of these registers, do a
390	   define of CHECK   above this.  I didn't find it necessary. - RES
391	*/
392
393#ifdef CHECK
394
395	/* Get day */
396	sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
397		((vts.btfp_time[ 1 ] & 0xff00) >> 8));
398
399	if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) )
400	    time_vme->day = (unsigned short)atoi(cbuf);
401	else
402	    time_vme->day = (unsigned short) 0;
403
404	/* Get hour */
405	sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
406
407	if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
408	    time_vme->hr = (unsigned short)atoi(cbuf);
409	else
410	    time_vme->hr = (unsigned short) 0;
411
412	/* Get minutes */
413	sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
414	if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
415	    time_vme->mn = (unsigned short)atoi(cbuf);
416	else
417	    time_vme->mn = (unsigned short) 0;
418
419	/* Get seconds */
420	sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
421
422	if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
423	    time_vme->sec = (unsigned short)atoi(cbuf);
424	else
425	    time_vme->sec = (unsigned short) 0;
426
427	/* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so we can
428	   use the TVTOTSF function  later on...*/
429
430	sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
431		vts.btfp_time[ 4 ]>>8);
432
433	if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2])
434	    && isdigit(cbuf[3]) && isdigit(cbuf[4]) && isdigit(cbuf[5]))
435	    time_vme->frac = (u_long) atoi(cbuf);
436	else
437	    time_vme->frac = (u_long) 0;
438#else
439
440	/* DONT CHECK  just trust the card */
441
442	/* Get day */
443	sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
444		((vts.btfp_time[ 1 ] & 0xff00) >> 8));
445	time_vme->day = (unsigned short)atoi(cbuf);
446
447	/* Get hour */
448	sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
449
450	time_vme->hr = (unsigned short)atoi(cbuf);
451
452	/* Get minutes */
453	sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
454	time_vme->mn = (unsigned short)atoi(cbuf);
455
456	/* Get seconds */
457	sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
458	time_vme->sec = (unsigned short)atoi(cbuf);
459
460	/* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so we can
461	   use the TVTOTSF function  later on...*/
462
463	sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
464		vts.btfp_time[ 4 ]>>8);
465
466	time_vme->frac = (u_long) atoi(cbuf);
467
468#endif /* CHECK */
469
470	/* Get status bit */
471	status = (vts.btfp_time[0] & 0x0010) >>4;
472	time_vme->status = status;  /* Status=0 if locked to ref. */
473	/* Status=1 if flywheeling */
474	if (status) {        /* lost lock ? */
475		return ((void *)NULL);
476	}
477	else
478	    return (time_vme);
479}
480
481#else
482int refclock_bancomm_bs;
483#endif /* REFCLOCK */
484