xref: /freebsd/contrib/ntp/ntpd/refclock_bancomm.c (revision 9c2daa00c2315f101948c7144d62af5d5fb515cf)
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 */
47 struct 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 
71 struct 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  */
97 extern u_long current_time;     /* current time(s) */
98 
99 /*
100  * Imported from ntpd module
101  */
102 extern 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  */
109 struct 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  */
117 static  void    vme_init        (void);
118 static  int     vme_start       (int, struct peer *);
119 static  void    vme_shutdown    (int, struct peer *);
120 static  void    vme_receive     (struct recvbuf *);
121 static  void    vme_poll        (int unit, struct peer *);
122 struct vmedate *get_datumtime(struct vmedate *);
123 
124 /*
125  * Transfer vector
126  */
127 struct  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 
137 int fd_vme;  /* file descriptor for ioctls */
138 int regvalue;
139 
140 
141 /*
142  * vme_start - open the VME device and initialize data for processing
143  */
144 static int
145 vme_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  */
208 static void
209 vme_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  */
234 static void
235 vme_receive(
236 	struct recvbuf *rbufp
237 	)
238 {
239 }
240 
241 
242 /*
243  * vme_poll - called by the transmit procedure
244  */
245 static void
246 vme_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 
325 struct vmedate *
326 get_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
432 int refclock_bancomm_bs;
433 #endif /* REFCLOCK */
434