xref: /freebsd/contrib/ntp/ntpd/refclock_bancomm.c (revision 5129159789cc9d7bc514e4546b88e3427695002d)
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 */
49 struct 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 
73 struct 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  */
104 extern u_long current_time;     /* current time(s) */
105 
106 /*
107  * Imported from ntpd module
108  */
109 extern 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  */
116 struct 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  */
125 static double fudgefactor[MAXUNITS];
126 static u_char stratumtouse[MAXUNITS];
127 static u_char sloppyclockflag[MAXUNITS];
128 
129 /*
130  * Function prototypes
131  */
132 static  void    vme_init        (void);
133 static  int     vme_start       (int, struct peer *);
134 static  void    vme_shutdown    (int, struct peer *);
135 static  void    vme_receive     (struct recvbuf *);
136 static  void    vme_poll        (int unit, struct peer *);
137 struct vmedate *get_datumtime(struct vmedate *);
138 
139 /*
140  * Transfer vector
141  */
142 struct  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 
152 int fd_vme;  /* file descriptor for ioctls */
153 int regvalue;
154 
155 /*
156  * vme_init - initialize internal vme driver data
157  */
158 static void
159 vme_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  */
176 static int
177 vme_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  */
253 static void
254 vme_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  */
285 static void
286 vme_receive(
287 	struct recvbuf *rbufp
288 	)
289 {
290 }
291 
292 
293 /*
294  * vme_poll - called by the transmit procedure
295  */
296 static void
297 vme_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 
375 struct vmedate *
376 get_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
482 int refclock_bancomm_bs;
483 #endif /* REFCLOCK */
484