diff options
Diffstat (limited to 'contrib/ntp/ntpd/refclock_gpsvme.c')
-rw-r--r-- | contrib/ntp/ntpd/refclock_gpsvme.c | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/refclock_gpsvme.c b/contrib/ntp/ntpd/refclock_gpsvme.c new file mode 100644 index 0000000000000..a7c6d23d3a4f1 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_gpsvme.c @@ -0,0 +1,613 @@ +/* + * refclock_gpsvme.c NTP clock driver for the TrueTime GPS-VME + * R. Schmidt, Time Service, US Naval Obs. res@tuttle.usno.navy.mil + * + * The refclock type has been defined as 16 (until new id assigned). + * These DEFS are included in the Makefile: + * DEFS= -DHAVE_TERMIOS -DSYS_HPUX=9 + * DEFS_LOCAL= -DREFCLOCK + * CLOCKDEFS= -DGPSVME + * The file map_vme.c does the VME memory mapping, and includes vme_init(). + * map_vme.c is HP-UX specific, because HPUX cannot mmap() device files! Boo! + * The file gps.h provides TrueTime register info. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_GPSVME) +#include <stdio.h> +#include <syslog.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <sys/time.h> + +#include "gps.h" +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.h" +#include "/etc/conf/h/io.h" + +/* GLOBAL STUFF BY RES */ + +#include <time.h> + +#define PRIO 120 /* set the realtime priority */ +#define NREGS 7 /* number of registers we will use */ + +extern int init_vme(); /* This is just a call to map_vme() */ + /* It doesn't have to be extern */ +unsigned short *greg[NREGS]; /* GPS registers defined in gps.h */ +void *gps_base; /* Base address of GPS VME card returned by */ + /* the map_vme() call */ +extern caddr_t map_vme (); +extern void unmap_vme(); /* Unmaps the VME space */ + +struct vmedate { /* structure needed by ntp */ + unsigned short year; /* *tptr is a pointer to this */ + unsigned short doy; + unsigned short hr; + unsigned short mn; + unsigned short sec; + unsigned long frac; + unsigned short status; +}; + +struct vmedate *get_gpsvme_time(); + +/* 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\0" /* Or whatever? */ +#define VMEREFID "GPS" /* reference id */ +#define VMEDESCRIPTION "GPS" /* who we are */ +#define VMEHSREFID 0x7f7f1001 /* 127.127.16.01 refid hi strata */ + +/* I'm using clock type 16 until one is assigned */ +/* This is set also in vme_control, below */ + + +#define GMT 0 /* hour offset from Greenwich */ + +/* + * 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 (void); +static int vme_start (u_int, struct peer *); +static void vme_shutdown (int); +static void vme_report_event (struct vmeunit *, int); +static void vme_receive (struct recvbuf *); +static void vme_poll (int unit, struct peer *); +static void vme_control (u_int, struct refclockstat *, struct refclockstat *); +static void vme_buginfo (int, struct refclockbug *); + +/* + * Transfer vector + */ +struct refclock refclock_gpsvme = { + 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(void) +{ + 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( + 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 VME DEVICE \n"); +#endif + init_vme(); /* This is in the map_vme.c external file */ + + /* + * 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. + */ + peer->precision = VMEPRECISION; + 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( + 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. + */ + unmap_vme(); + 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( + 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( + 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; + 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_gpsvme_time()) == 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->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->lastcode); + vme->lencode = (u_short) strlen(vme->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_gpsvme: 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); +} + +/* + * vme_control - set fudge factors, return statistics + */ +static void +vme_control( + 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 = 16; /*set by RES SHOULD BE CHANGED */ + 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; + out->lastcode = vme->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->lastcode = ""; + 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( + 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; +} +/* -------------------------------------------------------*/ +/* get_gpsvme_time() */ +/* R. Schmidt, USNO, 1995 */ +/* It's ugly, but hey, it works and its free */ + +#include "gps.h" /* defines for TrueTime GPS-VME */ + +#define PBIAS 193 /* 193 microsecs to read the GPS experimentally found */ + +struct vmedate * +get_gpsvme_time(void) +{ + struct vmedate *time_vme; + unsigned short set, hr, min, sec, ums, hms, status; + int ret; + char ti[3]; + + long tloc ; + time_t mktime(),time(); + struct tm *gmtime(), *gmt; + char *gpsmicro; + gpsmicro = (char *) malloc(7); + + time_vme = (struct vmedate *)malloc(sizeof(struct vmedate )); + *greg = (unsigned short *)malloc(sizeof(short) * NREGS); + + + /* reference the freeze command address general register 1 */ + set = *greg[0]; + /* read the registers : */ + /* get year */ + time_vme->year = (unsigned short) *greg[6]; + /* Get doy */ + time_vme->doy = (unsigned short) (*greg[5] & MASKDAY); + /* Get hour */ + time_vme->hr = (unsigned short) ((*greg[4] & MASKHI) >>8); + /* Get minutes */ + time_vme->mn = (unsigned short) (*greg[4] & MASKLO); + /* Get seconds */ + time_vme->sec = (unsigned short) (*greg[3] & MASKHI) >>8; + /* get microseconds in 2 parts and put together */ + ums = *greg[2]; + hms = *greg[3] & MASKLO; + + time_vme->status = (unsigned short) *greg[5] >>13; + + /* reference the unfreeze command address general register 1 */ + set = *greg[1]; + + sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums); + time_vme->frac = (u_long) gpsmicro; + + /* unmap_vme(); */ + + if (!status) { + return ((void *)NULL); + } + else + return (time_vme); +} + +#else +int refclock_gpsvme_bs; +#endif /* REFCLOCK */ |