1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
|
/* 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 */
|