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