/* $NetBSD: refclock_bancomm.c,v 1.3 1998/03/06 18:17:23 christos Exp $ */ /* * refclock_bancomm.c - clock driver for the Datum/Bancomm * bc635VME Time and Frequency Processor. * R. Schmidt, Time Service, US Naval Obs. May 94 * modelled after the TPRO NTP driver. * * This requires the Datum HP-UX V9.01 kernel driver and the HP-UX vme2 * driver subsystem. It has been tested on an HP9000/747i at HP-UX 9.03. * There are no restrictions on release and use of the following code. * The refclock type has been defined as 16. * Installation of the Datum/Bancomm HPUX driver creates the device file * /dev/btfp0 * * These DEFS are included in the Makefile: * DEFS= -DHAVE_TERMIOS -DSYS_HPUX=9 * DEFS_LOCAL= -DREFCLOCK * CLOCKDEFS= -DBANC */ #ifdef HAVE_CONFIG_H #include #endif #if defined(REFCLOCK) && defined(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" #include "/etc/conf/machine/vme2.h" #include "/etc/conf/h/io.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)*/ }; /***** Simple ioctl commands *****/ #define RUNLOCK _IO('X',19) /* Release Capture Lockout */ #define RCR0 _IOR('X',22,unsigned int) /* Read control register */ #define WCR0 _IOW('X',23,unsigned int) /* Write control register */ /***** Compound ioctl commands *****/ /* Read all 5 time words in one call. */ #define READTIME _IOR('X',32,struct btfp_time) #define VMEFD "/dev/btfp0" struct vmedate { /* structure returned by get_vmetime.c */ unsigned short year; unsigned short doy; 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 */ #define BMAX 50 /* timecode buffer length */ /* * VME interface parameters. */ #define VMEPRECISION (-21) /* precision assumed (1 us) */ #define USNOREFID "USNO" /* or whatever */ #define VMEREFID "USNO" /* reference id */ #define VMEDESCRIPTION "USNO Master Clock 2" /* 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. */ struct vmeunit { struct peer *peer; /* associated peer structure */ struct refclockio io; /* given to the I/O handler */ struct vmedate vmedata; /* data returned from vme read */ l_fp lastrec; /* last local time */ l_fp lastref; /* last timecode time */ char lastcode[BMAX]; /* last timecode received */ u_short lencode; /* length of last timecode */ u_long lasttime; /* last time clock heard from */ u_short unit; /* unit number for this guy */ u_short status; /* clock status */ u_short lastevent; /* last clock event */ u_short year; /* year of eternity */ u_short day; /* day of year */ u_short hour; /* hour of day */ u_short minute; /* minute of hour */ u_short second; /* seconds of minute */ u_long usec; /* microsecond of second */ u_long yearstart; /* start of current year */ u_short leap; /* leap indicators */ /* * Status tallies */ u_long polls; /* polls sent */ u_long noreply; /* no replies to polls */ u_long coderecv; /* timecodes received */ u_long badformat; /* bad format */ u_long baddata; /* bad data */ u_long timestarted; /* time we started this */ }; /* * Data space for the unit structures. Note that we allocate these on * the fly, but never give them back. */ static struct vmeunit *vmeunits[MAXUNITS]; static u_char unitinuse[MAXUNITS]; /* * Keep the fudge factors separately so they can be set even * when no clock is configured. */ static l_fp fudgefactor[MAXUNITS]; static u_char stratumtouse[MAXUNITS]; static u_char sloppyclockflag[MAXUNITS]; /* * Function prototypes */ static void vme_init P(()); static int vme_start P((u_int, struct peer *)); static void vme_shutdown P((int)); static void vme_report_event P((struct vmeunit *, int)); static void vme_receive P((struct recvbuf *)); static void vme_poll P((int unit, struct peer *)); static void vme_control P((u_int, struct refclockstat *, struct refclockstat *)); static void vme_buginfo P((int, struct refclockbug *)); struct vmedate *get_datumtime(); /* * Transfer vector */ struct refclock refclock_bancomm = { vme_start, vme_shutdown, vme_poll, vme_control, vme_init, vme_buginfo, NOFLAGS }; int fd_vme; /* file descriptor for ioctls */ int regvalue; /* * vme_init - initialize internal vme driver data */ static void vme_init() { register int i; /* * Just zero the data arrays */ /* bzero((char *)vmeunits, sizeof vmeunits); bzero((char *)unitinuse, sizeof unitinuse); */ /* * Initialize fudge factors to default. */ for (i = 0; i < MAXUNITS; i++) { fudgefactor[i].l_ui = 0; fudgefactor[i].l_uf = 0; stratumtouse[i] = 0; sloppyclockflag[i] = 0; } } /* * vme_start - open the VME device and initialize data for processing */ static int vme_start(unit, peer) u_int unit; struct peer *peer; { register struct vmeunit *vme; register int i; int dummy; char vmedev[20]; /* * Check configuration info. */ if (unit >= MAXUNITS) { msyslog(LOG_ERR, "vme_start: unit %d invalid", unit); return (0); } if (unitinuse[unit]) { msyslog(LOG_ERR, "vme_start: unit %d in use", 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 */ if (vmeunits[unit] != 0) { vme = vmeunits[unit]; /* The one we want is okay */ } else { for (i = 0; i < MAXUNITS; i++) { if (!unitinuse[i] && vmeunits[i] != 0) break; } if (i < MAXUNITS) { /* * Reclaim this one */ vme = vmeunits[i]; vmeunits[i] = 0; } else { vme = (struct vmeunit *) emalloc(sizeof(struct vmeunit)); } } bzero((char *)vme, sizeof(struct vmeunit)); vmeunits[unit] = vme; /* * Set up the structures */ vme->peer = peer; vme->unit = (u_short)unit; vme->timestarted = current_time; vme->io.clock_recv = vme_receive; vme->io.srcclock = (caddr_t)vme; vme->io.datalen = 0; vme->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. */ peer->precision = VMEPRECISION; peer->rootdelay = 0; peer->rootdispersion = 0; peer->stratum = stratumtouse[unit]; memcpy( (char *)&peer->refid, USNOREFID,4); /* peer->refid = htonl(VMEHSREFID); */ unitinuse[unit] = 1; return (1); } /* * vme_shutdown - shut down a VME clock */ static void vme_shutdown(unit) int unit; { register struct vmeunit *vme; if (unit >= MAXUNITS) { msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit); return; } if (!unitinuse[unit]) { msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit); return; } /* * Tell the I/O module to turn us off. We're history. */ vme = vmeunits[unit]; io_closeclock(&vme->io); unitinuse[unit] = 0; } /* * vme_report_event - note the occurance of an event * * This routine presently just remembers the report and logs it, but * does nothing heroic for the trap handler. */ static void vme_report_event(vme, code) struct vmeunit *vme; int code; { struct peer *peer; peer = vme->peer; if (vme->status != (u_short)code) { vme->status = (u_short)code; if (code != CEVNT_NOMINAL) vme->lastevent = (u_short)code; msyslog(LOG_INFO, "clock %s event %x", ntoa(&peer->srcadr), code); } } /* * 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(rbufp) struct recvbuf *rbufp; { } /* * vme_poll - called by the transmit procedure */ static void vme_poll(unit, peer) int unit; struct peer *peer; { struct vmedate *tptr; struct vmeunit *vme; l_fp tstmp; time_t tloc; struct tm *tadr; vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit *)); tptr = (struct vmedate *)emalloc(sizeof(struct vmedate *)); if (unit >= MAXUNITS) { msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit); return; } if (!unitinuse[unit]) { msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit); return; } vme = vmeunits[unit]; /* Here is the structure */ vme->polls++; tptr = &vme->vmedata; if ((tptr = get_datumtime()) == NULL ) { vme_report_event(vme, CEVNT_BADREPLY); return; } get_systime(&vme->lastrec); 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(vme->a_lastcode, "%3.3d %2.2d:%2.2d:%2.2d.%.6d %1d\0", tptr->doy, tptr->hr, tptr->mn, tptr->sec, tptr->frac, tptr->status); record_clock_stats(&(vme->peer->srcadr), vme->a_lastcode); vme->lencode = (u_short) strlen(vme->a_lastcode); vme->day = tptr->doy; vme->hour = tptr->hr; vme->minute = tptr->mn; vme->second = tptr->sec; vme->usec = tptr->frac; #ifdef DEBUG if (debug) printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n", vme->day, vme->hour, vme->minute, vme->second, vme->usec, tptr->status); #endif if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */ vme_report_event(vme, 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 (!clocktime(vme->day, vme->hour, vme->minute, vme->second, GMT, vme->lastrec.l_ui, &vme->yearstart, &vme->lastref.l_ui)) { vme->baddata++; vme_report_event(vme, CEVNT_BADTIME); msyslog(LOG_ERR, "refclock_datum: bad data!!"); return; } TVUTOTSF(vme->usec, vme->lastref.l_uf); tstmp = vme->lastref; L_SUB(&tstmp, &vme->lastrec); vme->coderecv++; L_ADD(&tstmp, &(fudgefactor[vme->unit])); refclock_receive(vme->peer, &tstmp, GMT, 0, &vme->lastrec, &vme->lastrec, vme->leap); } /* * vme_control - set fudge factors, return statistics */ static void vme_control(unit, in, out) u_int unit; struct refclockstat *in; struct refclockstat *out; { register struct vmeunit *vme; if (unit >= MAXUNITS) { msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit); return; } if (in != 0) { if (in->haveflags & CLK_HAVETIME1) fudgefactor[unit] = in->fudgetime1; if (in->haveflags & CLK_HAVEVAL1) { stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf); if (unitinuse[unit]) { struct peer *peer; /* * Should actually reselect clock, but * will wait for the next timecode */ vme = vmeunits[unit]; peer = vme->peer; peer->stratum = stratumtouse[unit]; if (stratumtouse[unit] <= 1) memcpy( (char *)&peer->refid, USNOREFID,4); else peer->refid = htonl(VMEHSREFID); } } if (in->haveflags & CLK_HAVEFLAG1) { sloppyclockflag[unit] = in->flags & CLK_FLAG1; } } if (out != 0) { /* out->type = REFCLK_IRIG_VME; */ out->type = 15; /* made up by RES */ out->haveflags = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1; out->clockdesc = VMEDESCRIPTION; out->fudgetime1 = fudgefactor[unit]; out->fudgetime2.l_ui = 0; out->fudgetime2.l_uf = 0; out->fudgeval1 = (long)stratumtouse[unit]; out->fudgeval2 = 0; out->flags = sloppyclockflag[unit]; if (unitinuse[unit]) { vme = vmeunits[unit]; out->lencode = vme->lencode; strcpy(out->a_lastcode, vme->a_lastcode); out->timereset = current_time - vme->timestarted; out->polls = vme->polls; out->noresponse = vme->noreply; out->badformat = vme->badformat; out->baddata = vme->baddata; out->lastevent = vme->lastevent; out->currentstatus = vme->status; } else { out->lencode = 0; out->a_lastcode[0] = '\0'; out->polls = out->noresponse = 0; out->badformat = out->baddata = 0; out->timereset = 0; out->currentstatus = out->lastevent = CEVNT_NOMINAL; } } } /* * vme_buginfo - return clock dependent debugging info */ static void vme_buginfo(unit, bug) int unit; register struct refclockbug *bug; { register struct vmeunit *vme; if (unit >= MAXUNITS) { msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit); return; } if (!unitinuse[unit]) return; vme = vmeunits[unit]; bug->nvalues = 11; bug->ntimes = 5; if (vme->lasttime != 0) bug->values[0] = current_time - vme->lasttime; else bug->values[0] = 0; bug->values[2] = (u_long)vme->year; bug->values[3] = (u_long)vme->day; bug->values[4] = (u_long)vme->hour; bug->values[5] = (u_long)vme->minute; bug->values[6] = (u_long)vme->second; bug->values[7] = (u_long)vme->usec; bug->values[9] = vme->yearstart; bug->stimes = 0x1c; bug->times[0] = vme->lastref; bug->times[1] = vme->lastrec; } struct vmedate *get_datumtime() { unsigned short status; char cbuf[7]; struct vmedate *time_vme; struct btfp_time vts; 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 doy */ sprintf(cbuf,"%3.3x\0", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) ) time_vme->doy = (unsigned short)atoi(cbuf); else time_vme->doy = (unsigned short) 0; /* Get hour */ sprintf(cbuf,"%2.2x\0", 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\0", (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\0", 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\0", 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 doy */ sprintf(cbuf,"%3.3x\0", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); time_vme->doy = (unsigned short)atoi(cbuf); /* Get hour */ sprintf(cbuf,"%2.2x\0", vts.btfp_time[ 1 ] & 0x00ff); time_vme->hr = (unsigned short)atoi(cbuf); /* Get minutes */ sprintf(cbuf,"%2.2x\0", (vts.btfp_time[ 2 ] & 0xff00) >>8); time_vme->mn = (unsigned short)atoi(cbuf); /* Get seconds */ sprintf(cbuf,"%2.2x\0", 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\0", 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 /* not (REFCLOCK && BANC) */ int refclock_bancomm_bs; #endif /* not (REFCLOCK && BANC) */