2b15cb3d09
Thanks to roberto for providing pointers to wedge this into HEAD. Approved by: roberto
254 lines
7.8 KiB
C
254 lines
7.8 KiB
C
/* refclock_psc.c: clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
#if defined(REFCLOCK) && defined(CLOCK_GPSVME)
|
|
|
|
#include "ntpd.h"
|
|
#include "ntp_io.h"
|
|
#include "ntp_refclock.h"
|
|
#include "ntp_unixtime.h"
|
|
#include "ntp_stdlib.h"
|
|
|
|
#ifdef __hpux
|
|
#include <sys/rtprio.h> /* may already be included above */
|
|
#include <sys/lock.h> /* NEEDED for PROCLOCK */
|
|
#endif /* __hpux */
|
|
|
|
#ifdef __linux__
|
|
#include <sys/ioctl.h> /* for _IOR, ioctl */
|
|
#endif /* __linux__ */
|
|
|
|
enum { /* constants */
|
|
BUFSIZE = 32,
|
|
PSC_SYNC_OK = 0x40, /* Sync status bit */
|
|
DP_LEAPSEC_DAY10DAY1 = 0x82, /* DP RAM address */
|
|
DP_LEAPSEC_DAY1000DAY100 = 0x83,
|
|
DELAY = 1,
|
|
NUNIT = 2 /* max UNITS */
|
|
};
|
|
|
|
/* clock card registers */
|
|
struct psc_regs {
|
|
uint32_t low_time; /* card base + 0x00 */
|
|
uint32_t high_time; /* card base + 0x04 */
|
|
uint32_t ext_low_time; /* card base + 0x08 */
|
|
uint32_t ext_high_time; /* card base + 0x0C */
|
|
uint8_t device_status; /* card base + 0x10 */
|
|
uint8_t device_control; /* card base + 0x11 */
|
|
uint8_t reserved0; /* card base + 0x12 */
|
|
uint8_t ext_100ns; /* card base + 0x13 */
|
|
uint8_t match_usec; /* card base + 0x14 */
|
|
uint8_t match_msec; /* card base + 0x15 */
|
|
uint8_t reserved1; /* card base + 0x16 */
|
|
uint8_t reserved2; /* card base + 0x17 */
|
|
uint8_t reserved3; /* card base + 0x18 */
|
|
uint8_t reserved4; /* card base + 0x19 */
|
|
uint8_t dp_ram_addr; /* card base + 0x1A */
|
|
uint8_t reserved5; /* card base + 0x1B */
|
|
uint8_t reserved6; /* card base + 0x1C */
|
|
uint8_t reserved7; /* card base + 0x1D */
|
|
uint8_t dp_ram_data; /* card base + 0x1E */
|
|
uint8_t reserved8; /* card base + 0x1F */
|
|
} *volatile regp[NUNIT];
|
|
|
|
#define PSC_REGS _IOR('K', 0, long) /* ioctl argument */
|
|
|
|
/* Macros to swap byte order and convert BCD to binary */
|
|
#define SWAP(val) ( ((val) >> 24) | (((val) & 0x00ff0000) >> 8) | \
|
|
(((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24) )
|
|
#define BCD2INT2(val) ( ((val) >> 4 & 0x0f)*10 + ((val) & 0x0f) )
|
|
#define BCD2INT3(val) ( ((val) >> 8 & 0x0f)*100 + ((val) >> 4 & 0x0f)*10 + \
|
|
((val) & 0x0f) )
|
|
|
|
/* PSC interface definitions */
|
|
#define PRECISION (-20) /* precision assumed (1 us) */
|
|
#define REFID "USNO" /* reference ID */
|
|
#define DESCRIPTION "Brandywine PCI-SyncClock32"
|
|
#define DEVICE "/dev/refclock%1d" /* device file */
|
|
|
|
/* clock unit control structure */
|
|
struct psc_unit {
|
|
short unit; /* NTP refclock unit number */
|
|
short last_hour; /* last hour (monitor leap sec) */
|
|
int msg_flag[2]; /* count error messages */
|
|
};
|
|
int fd[NUNIT]; /* file descriptor */
|
|
|
|
/* Local function prototypes */
|
|
static int psc_start(int, struct peer *);
|
|
static void psc_shutdown(int, struct peer *);
|
|
static void psc_poll(int, struct peer *);
|
|
static void check_leap_sec(struct refclockproc *, int);
|
|
|
|
/* Transfer vector */
|
|
struct refclock refclock_gpsvme = {
|
|
psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS
|
|
};
|
|
|
|
/* psc_start: open device and initialize data for processing */
|
|
static int
|
|
psc_start(
|
|
int unit,
|
|
struct peer *peer
|
|
)
|
|
{
|
|
char buf[BUFSIZE];
|
|
struct refclockproc *pp;
|
|
struct psc_unit *up = emalloc(sizeof *up);
|
|
|
|
if (unit < 0 || unit > 1) { /* support units 0 and 1 */
|
|
msyslog(LOG_ERR, "psc_start: bad unit: %d", unit);
|
|
return 0;
|
|
}
|
|
|
|
memset(up, '\0', sizeof *up);
|
|
|
|
snprintf(buf, sizeof(buf), DEVICE, unit); /* dev file name */
|
|
fd[unit] = open(buf, O_RDONLY); /* open device file */
|
|
if (fd[unit] < 0) {
|
|
msyslog(LOG_ERR, "psc_start: unit: %d, open failed. %m", unit);
|
|
return 0;
|
|
}
|
|
|
|
/* get the address of the mapped regs */
|
|
if (ioctl(fd[unit], PSC_REGS, ®p[unit]) < 0) {
|
|
msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed. %m", unit);
|
|
return 0;
|
|
}
|
|
|
|
/* initialize peer variables */
|
|
pp = peer->procptr;
|
|
pp->io.clock_recv = noentry;
|
|
pp->io.srcclock = peer;
|
|
pp->io.datalen = 0;
|
|
pp->io.fd = -1;
|
|
pp->unitptr = up;
|
|
get_systime(&pp->lastrec);
|
|
memcpy(&pp->refid, REFID, 4);
|
|
peer->precision = PRECISION;
|
|
pp->clockdesc = DESCRIPTION;
|
|
up->unit = unit;
|
|
#ifdef __hpux
|
|
rtprio(0,120); /* set real time priority */
|
|
plock(PROCLOCK); /* lock process in memory */
|
|
#endif /* __hpux */
|
|
return 1;
|
|
}
|
|
|
|
/* psc_shutdown: shut down the clock */
|
|
static void
|
|
psc_shutdown(
|
|
int unit,
|
|
struct peer *peer
|
|
)
|
|
{
|
|
if (NULL != peer->procptr->unitptr)
|
|
free(peer->procptr->unitptr);
|
|
if (fd[unit] > 0)
|
|
close(fd[unit]);
|
|
}
|
|
|
|
/* psc_poll: read, decode, and record device time */
|
|
static void
|
|
psc_poll(
|
|
int unit,
|
|
struct peer *peer
|
|
)
|
|
{
|
|
struct refclockproc *pp = peer->procptr;
|
|
struct psc_unit *up;
|
|
unsigned tlo, thi;
|
|
unsigned char status;
|
|
|
|
up = (struct psc_unit *) pp->unitptr;
|
|
tlo = regp[unit]->low_time; /* latch and read first 4 bytes */
|
|
thi = regp[unit]->high_time; /* read 4 higher order bytes */
|
|
status = regp[unit]->device_status; /* read device status byte */
|
|
|
|
if (!(status & PSC_SYNC_OK)) {
|
|
refclock_report(peer, CEVNT_BADTIME);
|
|
if (!up->msg_flag[unit]) { /* write once to system log */
|
|
msyslog(LOG_WARNING,
|
|
"SYNCHRONIZATION LOST on unit %1d, status %02x\n",
|
|
unit, status);
|
|
up->msg_flag[unit] = 1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
get_systime(&pp->lastrec);
|
|
pp->polls++;
|
|
|
|
tlo = SWAP(tlo); /* little to big endian swap on */
|
|
thi = SWAP(thi); /* copy of data */
|
|
/* convert the BCD time to broken down time used by refclockproc */
|
|
pp->day = BCD2INT3((thi & 0x0FFF0000) >> 16);
|
|
pp->hour = BCD2INT2((thi & 0x0000FF00) >> 8);
|
|
pp->minute = BCD2INT2(thi & 0x000000FF);
|
|
pp->second = BCD2INT2(tlo >> 24);
|
|
/* ntp_process() in ntp_refclock.c appears to use usec as fraction of
|
|
second in microseconds if usec is nonzero. */
|
|
pp->nsec = 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) +
|
|
BCD2INT3(tlo & 0x00000FFF);
|
|
|
|
snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
|
|
"%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x", pp->day,
|
|
pp->hour, pp->minute, pp->second, pp->nsec, status, thi,
|
|
tlo);
|
|
pp->lencode = strlen(pp->a_lastcode);
|
|
|
|
/* compute the timecode timestamp */
|
|
if (!refclock_process(pp)) {
|
|
refclock_report(peer, CEVNT_BADTIME);
|
|
return;
|
|
}
|
|
/* simulate the NTP receive and packet procedures */
|
|
refclock_receive(peer);
|
|
/* write clock statistics to file */
|
|
record_clock_stats(&peer->srcadr, pp->a_lastcode);
|
|
|
|
/* With the first timecode beginning the day, check for a GPS
|
|
leap second notification. */
|
|
if (pp->hour < up->last_hour) {
|
|
check_leap_sec(pp, unit);
|
|
up->msg_flag[0] = up->msg_flag[1] = 0; /* reset flags */
|
|
}
|
|
up->last_hour = pp->hour;
|
|
}
|
|
|
|
/* check_leap_sec: read the Dual Port RAM leap second day registers. The
|
|
onboard GPS receiver should write the hundreds digit of day of year in
|
|
DP_LeapSec_Day1000Day100 and the tens and ones digits in
|
|
DP_LeapSec_Day10Day1. If these values are nonzero and today, we have
|
|
a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND.
|
|
If the BCD data are zero or a date other than today, set pp->leap to
|
|
LEAP_NOWARNING. */
|
|
static void
|
|
check_leap_sec(struct refclockproc *pp, int unit)
|
|
{
|
|
unsigned char dhi, dlo;
|
|
int leap_day;
|
|
|
|
regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1;
|
|
usleep(DELAY);
|
|
dlo = regp[unit]->dp_ram_data;
|
|
regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100;
|
|
usleep(DELAY);
|
|
dhi = regp[unit]->dp_ram_data;
|
|
leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F);
|
|
|
|
pp->leap = LEAP_NOWARNING; /* default */
|
|
if (leap_day && leap_day == pp->day) {
|
|
pp->leap = LEAP_ADDSECOND; /* leap second today */
|
|
msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).",
|
|
leap_day, dhi, dlo);
|
|
}
|
|
}
|
|
|
|
#else
|
|
int refclock_gpsvme_bs;
|
|
#endif /* REFCLOCK */
|