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.
7 * Author(s): Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
12 * Note(s): The refclock type has been defined as 16.
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.
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.
26 * Installation of the Datum/Bancomm driver creates the
27 * device file /dev/btfp0
34 #if defined(REFCLOCK) && defined(CLOCK_BANC)
38 #include "ntp_refclock.h"
39 #include "ntp_unixtime.h"
40 #include "ntp_stdlib.h"
47 struct btfp_time /* Structure for reading 5 time words */
48 /* in one ioctl(2) operation. */
50 unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/
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 )
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*/
65 /***** Compound ioctl commands *****/
67 /* Read all 5 time words in one call. */
68 #define READTIME IOCIORN(b, 32, sizeof( struct btfp_time ))
69 #define VMEFD "/dev/btfp0"
71 struct vmedate { /* structure returned by get_vmetime.c */
78 unsigned short status;
81 /* END OF STUFF FROM RES */
84 * VME interface parameters.
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 */
95 * Imported from ntp_timer module
97 extern u_long current_time; /* current time(s) */
100 * Imported from ntpd module
102 extern int debug; /* global debug flag */
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
110 struct vmedate vmedata; /* data returned from vme read */
111 u_long lasttime; /* last time clock heard from */
115 * Function prototypes
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 *);
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 */
137 int fd_vme; /* file descriptor for ioctls */
142 * vme_start - open the VME device and initialize data for processing
150 register struct vmeunit *vme;
151 struct refclockproc *pp;
160 printf("Opening DATUM VME DEVICE \n");
162 if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) {
163 msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
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");
170 regvalue = 0; /* More esoteric stuff to do... */
171 if( ioctl( fd_vme, WCR0, ®value ) )
172 msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
176 * Allocate unit structure
178 vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
179 bzero((char *)vme, sizeof(struct vmeunit));
183 * Set up the structures
186 pp->unitptr = (caddr_t) vme;
187 pp->timestarted = current_time;
189 pp->io.clock_recv = vme_receive;
190 pp->io.srcclock = (caddr_t)peer;
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.
199 peer->precision = VMEPRECISION;
200 memcpy(&pp->refid, USNOREFID,4);
206 * vme_shutdown - shut down a VME clock
214 register struct vmeunit *vme;
215 struct refclockproc *pp;
218 * Tell the I/O module to turn us off. We're history.
221 vme = (struct vmeunit *)pp->unitptr;
222 io_closeclock(&pp->io);
229 * vme_receive - receive data from the VME device.
231 * Note: This interface would be interrupt-driven. We don't use that
232 * now, but include a dummy routine for possible future adventures.
236 struct recvbuf *rbufp
243 * vme_poll - called by the transmit procedure
251 struct vmedate *tptr;
253 struct refclockproc *pp;
258 vme = (struct vmeunit *)pp->unitptr; /* Here is the structure */
260 tptr = &vme->vmedata;
261 if ((tptr = get_datumtime(tptr)) == NULL ) {
262 refclock_report(peer, CEVNT_BADREPLY);
266 get_systime(&pp->lastrec);
268 vme->lasttime = current_time;
271 * Get VME time and convert to timestamp format.
272 * The year must come from the system clock.
276 tadr = gmtime(&tloc);
277 tptr->year = (unsigned short)(tadr->tm_year + 1900);
280 sprintf(pp->a_lastcode,
281 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
289 pp->lencode = (u_short) strlen(pp->a_lastcode);
293 pp->minute = tptr->mn;
294 pp->second = tptr->sec;
295 pp->usec = tptr->frac;
299 printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
300 pp->day, pp->hour, pp->minute, pp->second,
301 pp->usec, tptr->status);
303 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
304 refclock_report(peer, CEVNT_BADREPLY);
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.
316 if (!refclock_process(pp)) {
317 refclock_report(peer, CEVNT_BADTIME);
320 record_clock_stats(&peer->srcadr, pp->a_lastcode);
321 refclock_receive(peer);
325 get_datumtime(struct vmedate *time_vme)
327 unsigned short status;
329 struct btfp_time vts;
331 if ( time_vme == (struct vmedate *)NULL) {
332 time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
335 if( ioctl(fd_vme, READTIME, &vts))
336 msyslog(LOG_ERR, "get_datumtime error: %m");
338 /* if you want to actually check the validity of these registers, do a
339 define of CHECK above this. I didn't find it necessary. - RES
345 sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
346 ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
348 if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) )
349 time_vme->day = (unsigned short)atoi(cbuf);
351 time_vme->day = (unsigned short) 0;
354 sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
356 if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
357 time_vme->hr = (unsigned short)atoi(cbuf);
359 time_vme->hr = (unsigned short) 0;
362 sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
363 if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
364 time_vme->mn = (unsigned short)atoi(cbuf);
366 time_vme->mn = (unsigned short) 0;
369 sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
371 if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
372 time_vme->sec = (unsigned short)atoi(cbuf);
374 time_vme->sec = (unsigned short) 0;
376 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can
377 use the TVTOTSF function later on...*/
379 sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
380 vts.btfp_time[ 4 ]>>8);
382 if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2])
383 && isdigit(cbuf[3]) && isdigit(cbuf[4]) && isdigit(cbuf[5]))
384 time_vme->frac = (u_long) atoi(cbuf);
386 time_vme->frac = (u_long) 0;
389 /* DONT CHECK just trust the card */
392 sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
393 ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
394 time_vme->day = (unsigned short)atoi(cbuf);
397 sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
399 time_vme->hr = (unsigned short)atoi(cbuf);
402 sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
403 time_vme->mn = (unsigned short)atoi(cbuf);
406 sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
407 time_vme->sec = (unsigned short)atoi(cbuf);
409 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can
410 use the TVTOTSF function later on...*/
412 sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
413 vts.btfp_time[ 4 ]>>8);
415 time_vme->frac = (u_long) atoi(cbuf);
420 status = (vts.btfp_time[0] & 0x0010) >>4;
421 time_vme->status = status; /* Status=0 if locked to ref. */
422 /* Status=1 if flywheeling */
423 if (status) { /* lost lock ? */
424 return ((void *)NULL);
431 int refclock_bancomm_bs;
432 #endif /* REFCLOCK */