#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#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>
#include <sys/lock.h>
#endif
#ifdef __linux__
#include <sys/ioctl.h>
#endif
enum {
BUFSIZE = 32,
PSC_SYNC_OK = 0x40,
DP_LEAPSEC_DAY10DAY1 = 0x82,
DP_LEAPSEC_DAY1000DAY100 = 0x83,
DELAY = 1,
NUNIT = 2
};
struct psc_regs {
uint32_t low_time;
uint32_t high_time;
uint32_t ext_low_time;
uint32_t ext_high_time;
uint8_t device_status;
uint8_t device_control;
uint8_t reserved0;
uint8_t ext_100ns;
uint8_t match_usec;
uint8_t match_msec;
uint8_t reserved1;
uint8_t reserved2;
uint8_t reserved3;
uint8_t reserved4;
uint8_t dp_ram_addr;
uint8_t reserved5;
uint8_t reserved6;
uint8_t reserved7;
uint8_t dp_ram_data;
uint8_t reserved8;
} *volatile regp[NUNIT];
#define PSC_REGS _IOR('K', 0, long)
#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) )
#define PRECISION (-20)
#define REFID "USNO"
#define DESCRIPTION "Brandywine PCI-SyncClock32"
#define DEVICE "/dev/refclock%1d"
struct psc_unit {
short unit;
short last_hour;
int msg_flag[2];
};
int fd[NUNIT];
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);
struct refclock refclock_gpsvme = {
psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS
};
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) {
msyslog(LOG_ERR, "psc_start: bad unit: %d", unit);
return 0;
}
if (!up) {
msyslog(LOG_ERR, "psc_start: unit: %d, emalloc: %m", unit);
return 0;
}
memset(up, '\0', sizeof *up);
sprintf(buf, DEVICE, unit);
fd[unit] = open(buf, O_RDONLY);
if (fd[unit] < 0) {
msyslog(LOG_ERR, "psc_start: unit: %d, open failed. %m", unit);
return 0;
}
if (ioctl(fd[unit], PSC_REGS, ®p[unit]) < 0) {
msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed. %m", unit);
return 0;
}
pp = peer->procptr;
pp->io.clock_recv = noentry;
pp->io.srcclock = (caddr_t) peer;
pp->io.datalen = 0;
pp->io.fd = -1;
pp->unitptr = (caddr_t) up;
get_systime(&pp->lastrec);
memcpy((char *)&pp->refid, REFID, 4);
peer->precision = PRECISION;
pp->clockdesc = DESCRIPTION;
up->unit = unit;
#ifdef __hpux
rtprio(0,120);
plock(PROCLOCK);
#endif
return 1;
}
static void
psc_shutdown(
int unit,
struct peer *peer
)
{
free(peer->procptr->unitptr);
close(fd[unit]);
}
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;
thi = regp[unit]->high_time;
status = regp[unit]->device_status;
if (!(status & PSC_SYNC_OK)) {
refclock_report(peer, CEVNT_BADTIME);
if (!up->msg_flag[unit]) {
msyslog(LOG_WARNING,
"SYNCHRONIZATION LOST on unit %1d, status %02x\n",
status, unit);
up->msg_flag[unit] = 1;
}
return;
}
get_systime(&pp->lastrec);
pp->polls++;
tlo = SWAP(tlo);
thi = SWAP(thi);
pp->day = BCD2INT3((thi & 0x0FFF0000) >> 16);
pp->hour = BCD2INT2((thi & 0x0000FF00) >> 8);
pp->minute = BCD2INT2(thi & 0x000000FF);
pp->second = BCD2INT2(tlo >> 24);
pp->nsec = 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) +
BCD2INT3(tlo & 0x00000FFF);
sprintf(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);
if (!refclock_process(pp)) {
refclock_report(peer, CEVNT_BADTIME);
return;
}
refclock_receive(peer);
record_clock_stats(&peer->srcadr, pp->a_lastcode);
if (pp->hour < up->last_hour) {
check_leap_sec(pp, unit);
up->msg_flag[0] = up->msg_flag[1] = 0;
}
up->last_hour = pp->hour;
}
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;
if (leap_day && leap_day == pp->day) {
pp->leap = LEAP_ADDSECOND;
msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).",
leap_day, dhi, dlo);
}
}
#else
int refclock_gpsvme_bs;
#endif