1182007Sroberto/* refclock_psc.c: clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */ 254359Sroberto 3182007Sroberto#ifdef HAVE_CONFIG_H 4182007Sroberto#include <config.h> 5182007Sroberto#endif /* HAVE_CONFIG_H */ 654359Sroberto 7182007Sroberto#if defined(REFCLOCK) && defined(CLOCK_GPSVME) 882498Sroberto 9182007Sroberto#include "ntpd.h" 10182007Sroberto#include "ntp_io.h" 11182007Sroberto#include "ntp_refclock.h" 12182007Sroberto#include "ntp_unixtime.h" 13182007Sroberto#include "ntp_stdlib.h" 1482498Sroberto 15182007Sroberto#ifdef __hpux 16182007Sroberto#include <sys/rtprio.h> /* may already be included above */ 17182007Sroberto#include <sys/lock.h> /* NEEDED for PROCLOCK */ 18182007Sroberto#endif /* __hpux */ 1954359Sroberto 20182007Sroberto#ifdef __linux__ 21182007Sroberto#include <sys/ioctl.h> /* for _IOR, ioctl */ 22182007Sroberto#endif /* __linux__ */ 2354359Sroberto 24182007Srobertoenum { /* constants */ 25182007Sroberto BUFSIZE = 32, 26182007Sroberto PSC_SYNC_OK = 0x40, /* Sync status bit */ 27182007Sroberto DP_LEAPSEC_DAY10DAY1 = 0x82, /* DP RAM address */ 28182007Sroberto DP_LEAPSEC_DAY1000DAY100 = 0x83, 29182007Sroberto DELAY = 1, 30182007Sroberto NUNIT = 2 /* max UNITS */ 3154359Sroberto}; 3254359Sroberto 33182007Sroberto/* clock card registers */ 34182007Srobertostruct psc_regs { 35182007Sroberto uint32_t low_time; /* card base + 0x00 */ 36182007Sroberto uint32_t high_time; /* card base + 0x04 */ 37182007Sroberto uint32_t ext_low_time; /* card base + 0x08 */ 38182007Sroberto uint32_t ext_high_time; /* card base + 0x0C */ 39182007Sroberto uint8_t device_status; /* card base + 0x10 */ 40182007Sroberto uint8_t device_control; /* card base + 0x11 */ 41182007Sroberto uint8_t reserved0; /* card base + 0x12 */ 42182007Sroberto uint8_t ext_100ns; /* card base + 0x13 */ 43182007Sroberto uint8_t match_usec; /* card base + 0x14 */ 44182007Sroberto uint8_t match_msec; /* card base + 0x15 */ 45182007Sroberto uint8_t reserved1; /* card base + 0x16 */ 46182007Sroberto uint8_t reserved2; /* card base + 0x17 */ 47182007Sroberto uint8_t reserved3; /* card base + 0x18 */ 48182007Sroberto uint8_t reserved4; /* card base + 0x19 */ 49182007Sroberto uint8_t dp_ram_addr; /* card base + 0x1A */ 50182007Sroberto uint8_t reserved5; /* card base + 0x1B */ 51182007Sroberto uint8_t reserved6; /* card base + 0x1C */ 52182007Sroberto uint8_t reserved7; /* card base + 0x1D */ 53182007Sroberto uint8_t dp_ram_data; /* card base + 0x1E */ 54182007Sroberto uint8_t reserved8; /* card base + 0x1F */ 55182007Sroberto} *volatile regp[NUNIT]; 5654359Sroberto 57182007Sroberto#define PSC_REGS _IOR('K', 0, long) /* ioctl argument */ 5854359Sroberto 59182007Sroberto/* Macros to swap byte order and convert BCD to binary */ 60182007Sroberto#define SWAP(val) ( ((val) >> 24) | (((val) & 0x00ff0000) >> 8) | \ 61182007Sroberto(((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24) ) 62182007Sroberto#define BCD2INT2(val) ( ((val) >> 4 & 0x0f)*10 + ((val) & 0x0f) ) 63182007Sroberto#define BCD2INT3(val) ( ((val) >> 8 & 0x0f)*100 + ((val) >> 4 & 0x0f)*10 + \ 64182007Sroberto((val) & 0x0f) ) 6554359Sroberto 66182007Sroberto/* PSC interface definitions */ 67182007Sroberto#define PRECISION (-20) /* precision assumed (1 us) */ 68182007Sroberto#define REFID "USNO" /* reference ID */ 69182007Sroberto#define DESCRIPTION "Brandywine PCI-SyncClock32" 70182007Sroberto#define DEVICE "/dev/refclock%1d" /* device file */ 7154359Sroberto 72182007Sroberto/* clock unit control structure */ 73182007Srobertostruct psc_unit { 74182007Sroberto short unit; /* NTP refclock unit number */ 75182007Sroberto short last_hour; /* last hour (monitor leap sec) */ 76182007Sroberto int msg_flag[2]; /* count error messages */ 7754359Sroberto}; 78182007Srobertoint fd[NUNIT]; /* file descriptor */ 7954359Sroberto 80182007Sroberto/* Local function prototypes */ 81182007Srobertostatic int psc_start(int, struct peer *); 82182007Srobertostatic void psc_shutdown(int, struct peer *); 83182007Srobertostatic void psc_poll(int, struct peer *); 84182007Srobertostatic void check_leap_sec(struct refclockproc *, int); 8554359Sroberto 86182007Sroberto/* Transfer vector */ 87182007Srobertostruct refclock refclock_gpsvme = { 88182007Sroberto psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS 8954359Sroberto}; 9054359Sroberto 91182007Sroberto/* psc_start: open device and initialize data for processing */ 9254359Srobertostatic int 93182007Srobertopsc_start( 94182007Sroberto int unit, 95182007Sroberto struct peer *peer 96182007Sroberto ) 9754359Sroberto{ 98182007Sroberto char buf[BUFSIZE]; 99182007Sroberto struct refclockproc *pp; 100182007Sroberto struct psc_unit *up = emalloc(sizeof *up); 10154359Sroberto 102182007Sroberto if (unit < 0 || unit > 1) { /* support units 0 and 1 */ 103182007Sroberto msyslog(LOG_ERR, "psc_start: bad unit: %d", unit); 104182007Sroberto return 0; 105182007Sroberto } 10654359Sroberto 107182007Sroberto memset(up, '\0', sizeof *up); 10854359Sroberto 109290001Sglebius snprintf(buf, sizeof(buf), DEVICE, unit); /* dev file name */ 110182007Sroberto fd[unit] = open(buf, O_RDONLY); /* open device file */ 111182007Sroberto if (fd[unit] < 0) { 112182007Sroberto msyslog(LOG_ERR, "psc_start: unit: %d, open failed. %m", unit); 113182007Sroberto return 0; 114182007Sroberto } 115182007Sroberto 116182007Sroberto /* get the address of the mapped regs */ 117182007Sroberto if (ioctl(fd[unit], PSC_REGS, ®p[unit]) < 0) { 118182007Sroberto msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed. %m", unit); 119182007Sroberto return 0; 120182007Sroberto } 12154359Sroberto 122182007Sroberto /* initialize peer variables */ 123182007Sroberto pp = peer->procptr; 124182007Sroberto pp->io.clock_recv = noentry; 125290001Sglebius pp->io.srcclock = peer; 126182007Sroberto pp->io.datalen = 0; 127182007Sroberto pp->io.fd = -1; 128290001Sglebius pp->unitptr = up; 129182007Sroberto get_systime(&pp->lastrec); 130290001Sglebius memcpy(&pp->refid, REFID, 4); 131182007Sroberto peer->precision = PRECISION; 132182007Sroberto pp->clockdesc = DESCRIPTION; 133182007Sroberto up->unit = unit; 134182007Sroberto#ifdef __hpux 135182007Sroberto rtprio(0,120); /* set real time priority */ 136182007Sroberto plock(PROCLOCK); /* lock process in memory */ 137182007Sroberto#endif /* __hpux */ 138182007Sroberto return 1; 13954359Sroberto} 14054359Sroberto 141182007Sroberto/* psc_shutdown: shut down the clock */ 14254359Srobertostatic void 143182007Srobertopsc_shutdown( 144182007Sroberto int unit, 145182007Sroberto struct peer *peer 146182007Sroberto ) 14754359Sroberto{ 148290001Sglebius if (NULL != peer->procptr->unitptr) 149290001Sglebius free(peer->procptr->unitptr); 150290001Sglebius if (fd[unit] > 0) 151290001Sglebius close(fd[unit]); 15254359Sroberto} 15354359Sroberto 154182007Sroberto/* psc_poll: read, decode, and record device time */ 15554359Srobertostatic void 156182007Srobertopsc_poll( 157182007Sroberto int unit, 158182007Sroberto struct peer *peer 159182007Sroberto ) 16054359Sroberto{ 161182007Sroberto struct refclockproc *pp = peer->procptr; 162182007Sroberto struct psc_unit *up; 163182007Sroberto unsigned tlo, thi; 164182007Sroberto unsigned char status; 16554359Sroberto 166182007Sroberto up = (struct psc_unit *) pp->unitptr; 167182007Sroberto tlo = regp[unit]->low_time; /* latch and read first 4 bytes */ 168182007Sroberto thi = regp[unit]->high_time; /* read 4 higher order bytes */ 169182007Sroberto status = regp[unit]->device_status; /* read device status byte */ 17054359Sroberto 171182007Sroberto if (!(status & PSC_SYNC_OK)) { 172182007Sroberto refclock_report(peer, CEVNT_BADTIME); 173182007Sroberto if (!up->msg_flag[unit]) { /* write once to system log */ 174182007Sroberto msyslog(LOG_WARNING, 175182007Sroberto "SYNCHRONIZATION LOST on unit %1d, status %02x\n", 176290001Sglebius unit, status); 177182007Sroberto up->msg_flag[unit] = 1; 17854359Sroberto } 179182007Sroberto return; 180182007Sroberto } 18154359Sroberto 182182007Sroberto get_systime(&pp->lastrec); 183182007Sroberto pp->polls++; 184182007Sroberto 185182007Sroberto tlo = SWAP(tlo); /* little to big endian swap on */ 186182007Sroberto thi = SWAP(thi); /* copy of data */ 187182007Sroberto /* convert the BCD time to broken down time used by refclockproc */ 188182007Sroberto pp->day = BCD2INT3((thi & 0x0FFF0000) >> 16); 189182007Sroberto pp->hour = BCD2INT2((thi & 0x0000FF00) >> 8); 190182007Sroberto pp->minute = BCD2INT2(thi & 0x000000FF); 191182007Sroberto pp->second = BCD2INT2(tlo >> 24); 192182007Sroberto /* ntp_process() in ntp_refclock.c appears to use usec as fraction of 193182007Sroberto second in microseconds if usec is nonzero. */ 194182007Sroberto pp->nsec = 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) + 195182007Sroberto BCD2INT3(tlo & 0x00000FFF); 19654359Sroberto 197290001Sglebius snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 198290001Sglebius "%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x", pp->day, 199290001Sglebius pp->hour, pp->minute, pp->second, pp->nsec, status, thi, 200290001Sglebius tlo); 201182007Sroberto pp->lencode = strlen(pp->a_lastcode); 20254359Sroberto 203182007Sroberto /* compute the timecode timestamp */ 204182007Sroberto if (!refclock_process(pp)) { 205182007Sroberto refclock_report(peer, CEVNT_BADTIME); 206182007Sroberto return; 207182007Sroberto } 208182007Sroberto /* simulate the NTP receive and packet procedures */ 209182007Sroberto refclock_receive(peer); 210182007Sroberto /* write clock statistics to file */ 211182007Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 21254359Sroberto 213182007Sroberto /* With the first timecode beginning the day, check for a GPS 214182007Sroberto leap second notification. */ 215182007Sroberto if (pp->hour < up->last_hour) { 216182007Sroberto check_leap_sec(pp, unit); 217182007Sroberto up->msg_flag[0] = up->msg_flag[1] = 0; /* reset flags */ 218182007Sroberto } 219182007Sroberto up->last_hour = pp->hour; 22054359Sroberto} 22154359Sroberto 222182007Sroberto/* check_leap_sec: read the Dual Port RAM leap second day registers. The 223182007Sroberto onboard GPS receiver should write the hundreds digit of day of year in 224182007Sroberto DP_LeapSec_Day1000Day100 and the tens and ones digits in 225182007Sroberto DP_LeapSec_Day10Day1. If these values are nonzero and today, we have 226182007Sroberto a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND. 227182007Sroberto If the BCD data are zero or a date other than today, set pp->leap to 228182007Sroberto LEAP_NOWARNING. */ 22954359Srobertostatic void 230182007Srobertocheck_leap_sec(struct refclockproc *pp, int unit) 23154359Sroberto{ 232182007Sroberto unsigned char dhi, dlo; 233182007Sroberto int leap_day; 234182007Sroberto 235182007Sroberto regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1; 236182007Sroberto usleep(DELAY); 237182007Sroberto dlo = regp[unit]->dp_ram_data; 238182007Sroberto regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100; 239182007Sroberto usleep(DELAY); 240182007Sroberto dhi = regp[unit]->dp_ram_data; 241182007Sroberto leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F); 24254359Sroberto 243182007Sroberto pp->leap = LEAP_NOWARNING; /* default */ 244182007Sroberto if (leap_day && leap_day == pp->day) { 245182007Sroberto pp->leap = LEAP_ADDSECOND; /* leap second today */ 246182007Sroberto msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).", 247182007Sroberto leap_day, dhi, dlo); 248182007Sroberto } 24954359Sroberto} 25054359Sroberto 25154359Sroberto#else 252182007Srobertoint refclock_gpsvme_bs; 253182007Sroberto#endif /* REFCLOCK */ 254