/* refclock_bancomm.c - clock driver for the Datum/Bancomm bc635VME * Time and Frequency Processor. It requires the BANCOMM bc635VME/ * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc * IIi-cEngine running Solaris 2.6. * * Author(s): Ganesh Ramasivan & Gary Cliff, Computing Devices Canada, * Ottawa, Canada * * Date: July 1999 * * Note(s): The refclock type has been defined as 16. * * This program has been modelled after the Bancomm driver * originally written by R. Schmidt of Time Service, U.S. * Naval Observatory for a HP-UX machine. Since the original * authors no longer plan to maintain this code, all * references to the HP-UX vme2 driver subsystem bave been * removed. Functions vme_report_event(), vme_receive(), * vme_control() and vme_buginfo() have been deleted because * they are no longer being used. * * The time on the bc635 TFP must be set to GMT due to the * fact that NTP makes use of GMT for all its calculations. * * Installation of the Datum/Bancomm driver creates the * device file /dev/btfp0 */ #ifdef HAVE_CONFIG_H #include #endif #if defined(REFCLOCK) && defined(CLOCK_BANC) #include #include #include #include #include #include #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_unixtime.h" #include "ntp_stdlib.h" /* STUFF BY RES */ struct btfp_time /* Structure for reading 5 time words */ /* in one ioctl(2) operation. */ { unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/ }; /* SunOS5 ioctl commands definitions.*/ #define BTFPIOC ( 'b'<< 8 ) #define IOCIO( l, n ) ( BTFPIOC | n ) #define IOCIOR( l, n, s ) ( BTFPIOC | n ) #define IOCIORN( l, n, s ) ( BTFPIOC | n ) #define IOCIOWN( l, n, s ) ( BTFPIOC | n ) /***** Simple ioctl commands *****/ #define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */ #define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/ #define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/ /***** Compound ioctl commands *****/ /* Read all 5 time words in one call. */ #define READTIME IOCIORN(b, 32, sizeof( struct btfp_time )) #define VMEFD "/dev/btfp0" struct vmedate { /* structure returned by get_vmetime.c */ unsigned short year; unsigned short day; unsigned short hr; unsigned short mn; unsigned short sec; unsigned long frac; unsigned short status; }; /* END OF STUFF FROM RES */ /* * Definitions */ #define MAXUNITS 2 /* max number of VME units */ /* * VME interface parameters. */ #define VMEPRECISION (-21) /* precision assumed (1 us) */ #define USNOREFID "BTFP" /* or whatever */ #define VMEREFID "BTFP" /* reference id */ #define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */ #define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */ /* clock type 16 is used here */ #define GMT 0 /* hour offset from Greenwich */ /* * Imported from ntp_timer module */ extern u_long current_time; /* current time(s) */ /* * Imported from ntpd module */ extern int debug; /* global debug flag */ /* * VME unit control structure. * Changes made to vmeunit structure. Most members are now available in the * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan */ struct vmeunit { struct vmedate vmedata; /* data returned from vme read */ u_long lasttime; /* last time clock heard from */ }; /* * Keep the fudge factors separately so they can be set even * when no clock is configured. */ static double fudgefactor[MAXUNITS]; static u_char stratumtouse[MAXUNITS]; static u_char sloppyclockflag[MAXUNITS]; /* * Function prototypes */ static void vme_init (void); static int vme_start (int, struct peer *); static void vme_shutdown (int, struct peer *); static void vme_receive (struct recvbuf *); static void vme_poll (int unit, struct peer *); struct vmedate *get_datumtime(struct vmedate *); /* * Transfer vector */ struct refclock refclock_bancomm = { vme_start, vme_shutdown, vme_poll, noentry, /* not used (old vme_control) */ vme_init, noentry, /* not used (old vme_buginfo) */ NOFLAGS }; int fd_vme; /* file descriptor for ioctls */ int regvalue; /* * vme_init - initialize internal vme driver data */ static void vme_init(void) { register int i; /* * Initialize fudge factors to default. */ for (i = 0; i < MAXUNITS; i++) { fudgefactor[i] = 0.0; stratumtouse[i] = 0; sloppyclockflag[i] = 0; } } /* * vme_start - open the VME device and initialize data for processing */ static int vme_start( int unit, struct peer *peer ) { register struct vmeunit *vme; struct refclockproc *pp; int dummy; char vmedev[20]; /* * Check configuration info. */ if (unit >= MAXUNITS) { msyslog(LOG_ERR, "vme_start: unit %d invalid", unit); return (0); } /* * Open VME device */ #ifdef DEBUG printf("Opening DATUM VME DEVICE \n"); #endif if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) { msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev); return (0); } else { /* Release capture lockout in case it was set from before. */ if( ioctl( fd_vme, RUNLOCK, &dummy ) ) msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m"); regvalue = 0; /* More esoteric stuff to do... */ if( ioctl( fd_vme, WCR0, ®value ) ) msyslog(LOG_ERR, "vme_start: WCR0 failed %m"); } /* * Allocate unit structure */ vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit)); bzero((char *)vme, sizeof(struct vmeunit)); /* * Set up the structures */ pp = peer->procptr; pp->unitptr = (caddr_t) vme; pp->timestarted = current_time; pp->io.clock_recv = vme_receive; pp->io.srcclock = (caddr_t)peer; pp->io.datalen = 0; pp->io.fd = fd_vme; /* * All done. Initialize a few random peer variables, then * return success. Note that root delay and root dispersion are * always zero for this clock. */ pp->leap = LEAP_NOWARNING; peer->precision = VMEPRECISION; peer->stratum = stratumtouse[unit]; memcpy( (char *)&peer->refid, USNOREFID,4); peer->refid = htonl(VMEHSREFID); return (1); } /* * vme_shutdown - shut down a VME clock */ static void vme_shutdown( int unit, struct peer *peer ) { register struct vmeunit *vme; struct refclockproc *pp; pp = peer->procptr; if (unit >= MAXUNITS) { msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit); return; } /* * Tell the I/O module to turn us off. We're history. */ vme = (struct vmeunit *)pp->unitptr; io_closeclock(&pp->io); pp->unitptr = NULL; free(vme); } /* * vme_receive - receive data from the VME device. * * Note: This interface would be interrupt-driven. We don't use that * now, but include a dummy routine for possible future adventures. */ static void vme_receive( struct recvbuf *rbufp ) { } /* * vme_poll - called by the transmit procedure */ static void vme_poll( int unit, struct peer *peer ) { struct vmedate *tptr; struct vmeunit *vme; struct refclockproc *pp; time_t tloc; struct tm *tadr; pp = peer->procptr; vme = (struct vmeunit *)pp->unitptr; /* Here is the structure */ tptr = &vme->vmedata; if ((tptr = get_datumtime(tptr)) == NULL ) { refclock_report(peer, CEVNT_BADREPLY); return; } get_systime(&pp->lastrec); pp->polls++; vme->lasttime = current_time; /* * Get VME time and convert to timestamp format. * The year must come from the system clock. */ time(&tloc); tadr = gmtime(&tloc); tptr->year = (unsigned short)(tadr->tm_year + 1900); sprintf(pp->a_lastcode, "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d", tptr->day, tptr->hr, tptr->mn, tptr->sec, tptr->frac, tptr->status); pp->lencode = (u_short) strlen(pp->a_lastcode); pp->day = tptr->day; pp->hour = tptr->hr; pp->minute = tptr->mn; pp->second = tptr->sec; pp->usec = tptr->frac; #ifdef DEBUG if (debug) printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n", pp->day, pp->hour, pp->minute, pp->second, pp->usec, tptr->status); #endif if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */ refclock_report(peer, CEVNT_BADREPLY); return; } /* * Now, compute the reference time value. Use the heavy * machinery for the seconds and the millisecond field for the * fraction when present. If an error in conversion to internal * format is found, the program declares bad data and exits. * Note that this code does not yet know how to do the years and * relies on the clock-calendar chip for sanity. */ if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } record_clock_stats(&peer->srcadr, pp->a_lastcode); refclock_receive(peer); } struct vmedate * get_datumtime(struct vmedate *time_vme) { unsigned short status; char cbuf[7]; struct btfp_time vts; if ( time_vme == (struct vmedate *)NULL) { time_vme = (struct vmedate *)malloc(sizeof(struct vmedate )); } if( ioctl(fd_vme, READTIME, &vts)) msyslog(LOG_ERR, "get_datumtime error: %m"); /* if you want to actually check the validity of these registers, do a define of CHECK above this. I didn't find it necessary. - RES */ #ifdef CHECK /* Get day */ sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) ) time_vme->day = (unsigned short)atoi(cbuf); else time_vme->day = (unsigned short) 0; /* Get hour */ sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff); if (isdigit(cbuf[0]) && isdigit(cbuf[1])) time_vme->hr = (unsigned short)atoi(cbuf); else time_vme->hr = (unsigned short) 0; /* Get minutes */ sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8); if (isdigit(cbuf[0]) && isdigit(cbuf[1])) time_vme->mn = (unsigned short)atoi(cbuf); else time_vme->mn = (unsigned short) 0; /* Get seconds */ sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff); if (isdigit(cbuf[0]) && isdigit(cbuf[1])) time_vme->sec = (unsigned short)atoi(cbuf); else time_vme->sec = (unsigned short) 0; /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can use the TVTOTSF function later on...*/ sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ], vts.btfp_time[ 4 ]>>8); if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) && isdigit(cbuf[3]) && isdigit(cbuf[4]) && isdigit(cbuf[5])) time_vme->frac = (u_long) atoi(cbuf); else time_vme->frac = (u_long) 0; #else /* DONT CHECK just trust the card */ /* Get day */ sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); time_vme->day = (unsigned short)atoi(cbuf); /* Get hour */ sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff); time_vme->hr = (unsigned short)atoi(cbuf); /* Get minutes */ sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8); time_vme->mn = (unsigned short)atoi(cbuf); /* Get seconds */ sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff); time_vme->sec = (unsigned short)atoi(cbuf); /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can use the TVTOTSF function later on...*/ sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ], vts.btfp_time[ 4 ]>>8); time_vme->frac = (u_long) atoi(cbuf); #endif /* CHECK */ /* Get status bit */ status = (vts.btfp_time[0] & 0x0010) >>4; time_vme->status = status; /* Status=0 if locked to ref. */ /* Status=1 if flywheeling */ if (status) { /* lost lock ? */ return ((void *)NULL); } else return (time_vme); } #else int refclock_bancomm_bs; #endif /* REFCLOCK */