NetBSD/usr.sbin/xntp/xntpd/refclock_gpsvme.c

620 lines
19 KiB
C
Raw Normal View History

1997-04-18 17:22:49 +04:00
/*
* 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(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 */
/*
* 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 *));
/*
* 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()
{
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 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. 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.
*/
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(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_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, &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 = 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(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;
}
/* -------------------------------------------------------*/
/* 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()
{
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 /* not (REFCLOCK && GPSVME) */
int refclock_gpsvme_bs;
#endif /* not (REFCLOCK && GPSVME) */