/* 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);
/* 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;
}
/* 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);
/* 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;