1ea906c41SOllivier Robert /* refclock_psc.c: clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */ 2ea906c41SOllivier Robert 3c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 4c0b746e5SOllivier Robert #include <config.h> 5ea906c41SOllivier Robert #endif /* HAVE_CONFIG_H */ 6c0b746e5SOllivier Robert 7c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_GPSVME) 8c0b746e5SOllivier Robert 9c0b746e5SOllivier Robert #include "ntpd.h" 10c0b746e5SOllivier Robert #include "ntp_io.h" 11c0b746e5SOllivier Robert #include "ntp_refclock.h" 12c0b746e5SOllivier Robert #include "ntp_unixtime.h" 13c0b746e5SOllivier Robert #include "ntp_stdlib.h" 14224ba2bdSOllivier Robert 15ea906c41SOllivier Robert #ifdef __hpux 16ea906c41SOllivier Robert #include <sys/rtprio.h> /* may already be included above */ 17ea906c41SOllivier Robert #include <sys/lock.h> /* NEEDED for PROCLOCK */ 18ea906c41SOllivier Robert #endif /* __hpux */ 19224ba2bdSOllivier Robert 20ea906c41SOllivier Robert #ifdef __linux__ 21ea906c41SOllivier Robert #include <sys/ioctl.h> /* for _IOR, ioctl */ 22ea906c41SOllivier Robert #endif /* __linux__ */ 23c0b746e5SOllivier Robert 24ea906c41SOllivier Robert enum { /* constants */ 25ea906c41SOllivier Robert BUFSIZE = 32, 26ea906c41SOllivier Robert PSC_SYNC_OK = 0x40, /* Sync status bit */ 27ea906c41SOllivier Robert DP_LEAPSEC_DAY10DAY1 = 0x82, /* DP RAM address */ 28ea906c41SOllivier Robert DP_LEAPSEC_DAY1000DAY100 = 0x83, 29ea906c41SOllivier Robert DELAY = 1, 30ea906c41SOllivier Robert NUNIT = 2 /* max UNITS */ 31c0b746e5SOllivier Robert }; 32c0b746e5SOllivier Robert 33ea906c41SOllivier Robert /* clock card registers */ 34ea906c41SOllivier Robert struct psc_regs { 35ea906c41SOllivier Robert uint32_t low_time; /* card base + 0x00 */ 36ea906c41SOllivier Robert uint32_t high_time; /* card base + 0x04 */ 37ea906c41SOllivier Robert uint32_t ext_low_time; /* card base + 0x08 */ 38ea906c41SOllivier Robert uint32_t ext_high_time; /* card base + 0x0C */ 39ea906c41SOllivier Robert uint8_t device_status; /* card base + 0x10 */ 40ea906c41SOllivier Robert uint8_t device_control; /* card base + 0x11 */ 41ea906c41SOllivier Robert uint8_t reserved0; /* card base + 0x12 */ 42ea906c41SOllivier Robert uint8_t ext_100ns; /* card base + 0x13 */ 43ea906c41SOllivier Robert uint8_t match_usec; /* card base + 0x14 */ 44ea906c41SOllivier Robert uint8_t match_msec; /* card base + 0x15 */ 45ea906c41SOllivier Robert uint8_t reserved1; /* card base + 0x16 */ 46ea906c41SOllivier Robert uint8_t reserved2; /* card base + 0x17 */ 47ea906c41SOllivier Robert uint8_t reserved3; /* card base + 0x18 */ 48ea906c41SOllivier Robert uint8_t reserved4; /* card base + 0x19 */ 49ea906c41SOllivier Robert uint8_t dp_ram_addr; /* card base + 0x1A */ 50ea906c41SOllivier Robert uint8_t reserved5; /* card base + 0x1B */ 51ea906c41SOllivier Robert uint8_t reserved6; /* card base + 0x1C */ 52ea906c41SOllivier Robert uint8_t reserved7; /* card base + 0x1D */ 53ea906c41SOllivier Robert uint8_t dp_ram_data; /* card base + 0x1E */ 54ea906c41SOllivier Robert uint8_t reserved8; /* card base + 0x1F */ 55ea906c41SOllivier Robert } *volatile regp[NUNIT]; 56c0b746e5SOllivier Robert 57ea906c41SOllivier Robert #define PSC_REGS _IOR('K', 0, long) /* ioctl argument */ 58c0b746e5SOllivier Robert 59ea906c41SOllivier Robert /* Macros to swap byte order and convert BCD to binary */ 60ea906c41SOllivier Robert #define SWAP(val) ( ((val) >> 24) | (((val) & 0x00ff0000) >> 8) | \ 61ea906c41SOllivier Robert (((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24) ) 62ea906c41SOllivier Robert #define BCD2INT2(val) ( ((val) >> 4 & 0x0f)*10 + ((val) & 0x0f) ) 63ea906c41SOllivier Robert #define BCD2INT3(val) ( ((val) >> 8 & 0x0f)*100 + ((val) >> 4 & 0x0f)*10 + \ 64ea906c41SOllivier Robert ((val) & 0x0f) ) 65c0b746e5SOllivier Robert 66ea906c41SOllivier Robert /* PSC interface definitions */ 67ea906c41SOllivier Robert #define PRECISION (-20) /* precision assumed (1 us) */ 68ea906c41SOllivier Robert #define REFID "USNO" /* reference ID */ 69ea906c41SOllivier Robert #define DESCRIPTION "Brandywine PCI-SyncClock32" 70ea906c41SOllivier Robert #define DEVICE "/dev/refclock%1d" /* device file */ 71c0b746e5SOllivier Robert 72ea906c41SOllivier Robert /* clock unit control structure */ 73ea906c41SOllivier Robert struct psc_unit { 74ea906c41SOllivier Robert short unit; /* NTP refclock unit number */ 75ea906c41SOllivier Robert short last_hour; /* last hour (monitor leap sec) */ 76ea906c41SOllivier Robert int msg_flag[2]; /* count error messages */ 77c0b746e5SOllivier Robert }; 78ea906c41SOllivier Robert int fd[NUNIT]; /* file descriptor */ 79c0b746e5SOllivier Robert 80ea906c41SOllivier Robert /* Local function prototypes */ 81ea906c41SOllivier Robert static int psc_start(int, struct peer *); 82ea906c41SOllivier Robert static void psc_shutdown(int, struct peer *); 83ea906c41SOllivier Robert static void psc_poll(int, struct peer *); 84ea906c41SOllivier Robert static void check_leap_sec(struct refclockproc *, int); 85c0b746e5SOllivier Robert 86ea906c41SOllivier Robert /* Transfer vector */ 87c0b746e5SOllivier Robert struct refclock refclock_gpsvme = { 88ea906c41SOllivier Robert psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS 89c0b746e5SOllivier Robert }; 90c0b746e5SOllivier Robert 91ea906c41SOllivier Robert /* psc_start: open device and initialize data for processing */ 92c0b746e5SOllivier Robert static int 93ea906c41SOllivier Robert psc_start( 94c0b746e5SOllivier Robert int unit, 95c0b746e5SOllivier Robert struct peer *peer 96c0b746e5SOllivier Robert ) 97c0b746e5SOllivier Robert { 98ea906c41SOllivier Robert char buf[BUFSIZE]; 99ea906c41SOllivier Robert struct refclockproc *pp; 100ea906c41SOllivier Robert struct psc_unit *up = emalloc(sizeof *up); 101c0b746e5SOllivier Robert 102ea906c41SOllivier Robert if (unit < 0 || unit > 1) { /* support units 0 and 1 */ 103ea906c41SOllivier Robert msyslog(LOG_ERR, "psc_start: bad unit: %d", unit); 104ea906c41SOllivier Robert return 0; 105c0b746e5SOllivier Robert } 106c0b746e5SOllivier Robert 107ea906c41SOllivier Robert memset(up, '\0', sizeof *up); 108c0b746e5SOllivier Robert 1092b15cb3dSCy Schubert snprintf(buf, sizeof(buf), DEVICE, unit); /* dev file name */ 110ea906c41SOllivier Robert fd[unit] = open(buf, O_RDONLY); /* open device file */ 111ea906c41SOllivier Robert if (fd[unit] < 0) { 112ea906c41SOllivier Robert msyslog(LOG_ERR, "psc_start: unit: %d, open failed. %m", unit); 113ea906c41SOllivier Robert return 0; 114c0b746e5SOllivier Robert } 115c0b746e5SOllivier Robert 116ea906c41SOllivier Robert /* get the address of the mapped regs */ 117ea906c41SOllivier Robert if (ioctl(fd[unit], PSC_REGS, ®p[unit]) < 0) { 118ea906c41SOllivier Robert msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed. %m", unit); 119ea906c41SOllivier Robert return 0; 120c0b746e5SOllivier Robert } 121c0b746e5SOllivier Robert 122ea906c41SOllivier Robert /* initialize peer variables */ 123ea906c41SOllivier Robert pp = peer->procptr; 124ea906c41SOllivier Robert pp->io.clock_recv = noentry; 1252b15cb3dSCy Schubert pp->io.srcclock = peer; 126ea906c41SOllivier Robert pp->io.datalen = 0; 127ea906c41SOllivier Robert pp->io.fd = -1; 1282b15cb3dSCy Schubert pp->unitptr = up; 129ea906c41SOllivier Robert get_systime(&pp->lastrec); 1302b15cb3dSCy Schubert memcpy(&pp->refid, REFID, 4); 131ea906c41SOllivier Robert peer->precision = PRECISION; 132ea906c41SOllivier Robert pp->clockdesc = DESCRIPTION; 133ea906c41SOllivier Robert up->unit = unit; 134ea906c41SOllivier Robert #ifdef __hpux 135ea906c41SOllivier Robert rtprio(0,120); /* set real time priority */ 136ea906c41SOllivier Robert plock(PROCLOCK); /* lock process in memory */ 137ea906c41SOllivier Robert #endif /* __hpux */ 138ea906c41SOllivier Robert return 1; 139ea906c41SOllivier Robert } 140ea906c41SOllivier Robert 141ea906c41SOllivier Robert /* psc_shutdown: shut down the clock */ 142c0b746e5SOllivier Robert static void 143ea906c41SOllivier Robert psc_shutdown( 144c0b746e5SOllivier Robert int unit, 145a151a66cSOllivier Robert struct peer *peer 146c0b746e5SOllivier Robert ) 147c0b746e5SOllivier Robert { 1482b15cb3dSCy Schubert if (NULL != peer->procptr->unitptr) 149ea906c41SOllivier Robert free(peer->procptr->unitptr); 1502b15cb3dSCy Schubert if (fd[unit] > 0) 151ea906c41SOllivier Robert close(fd[unit]); 152c0b746e5SOllivier Robert } 153c0b746e5SOllivier Robert 154ea906c41SOllivier Robert /* psc_poll: read, decode, and record device time */ 155ea906c41SOllivier Robert static void 156ea906c41SOllivier Robert psc_poll( 157ea906c41SOllivier Robert int unit, 158ea906c41SOllivier Robert struct peer *peer 159ea906c41SOllivier Robert ) 160c0b746e5SOllivier Robert { 161ea906c41SOllivier Robert struct refclockproc *pp = peer->procptr; 162ea906c41SOllivier Robert struct psc_unit *up; 163ea906c41SOllivier Robert unsigned tlo, thi; 164ea906c41SOllivier Robert unsigned char status; 165c0b746e5SOllivier Robert 166ea906c41SOllivier Robert up = (struct psc_unit *) pp->unitptr; 167ea906c41SOllivier Robert tlo = regp[unit]->low_time; /* latch and read first 4 bytes */ 168ea906c41SOllivier Robert thi = regp[unit]->high_time; /* read 4 higher order bytes */ 169ea906c41SOllivier Robert status = regp[unit]->device_status; /* read device status byte */ 170c0b746e5SOllivier Robert 171ea906c41SOllivier Robert if (!(status & PSC_SYNC_OK)) { 172ea906c41SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 173ea906c41SOllivier Robert if (!up->msg_flag[unit]) { /* write once to system log */ 174ea906c41SOllivier Robert msyslog(LOG_WARNING, 175ea906c41SOllivier Robert "SYNCHRONIZATION LOST on unit %1d, status %02x\n", 1762b15cb3dSCy Schubert unit, status); 177ea906c41SOllivier Robert up->msg_flag[unit] = 1; 178c0b746e5SOllivier Robert } 179ea906c41SOllivier Robert return; 180ea906c41SOllivier Robert } 181ea906c41SOllivier Robert 182ea906c41SOllivier Robert get_systime(&pp->lastrec); 183ea906c41SOllivier Robert pp->polls++; 184ea906c41SOllivier Robert 185ea906c41SOllivier Robert tlo = SWAP(tlo); /* little to big endian swap on */ 186ea906c41SOllivier Robert thi = SWAP(thi); /* copy of data */ 187ea906c41SOllivier Robert /* convert the BCD time to broken down time used by refclockproc */ 188ea906c41SOllivier Robert pp->day = BCD2INT3((thi & 0x0FFF0000) >> 16); 189ea906c41SOllivier Robert pp->hour = BCD2INT2((thi & 0x0000FF00) >> 8); 190ea906c41SOllivier Robert pp->minute = BCD2INT2(thi & 0x000000FF); 191ea906c41SOllivier Robert pp->second = BCD2INT2(tlo >> 24); 192ea906c41SOllivier Robert /* ntp_process() in ntp_refclock.c appears to use usec as fraction of 193ea906c41SOllivier Robert second in microseconds if usec is nonzero. */ 194ea906c41SOllivier Robert pp->nsec = 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) + 195ea906c41SOllivier Robert BCD2INT3(tlo & 0x00000FFF); 196ea906c41SOllivier Robert 1972b15cb3dSCy Schubert snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 1982b15cb3dSCy Schubert "%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x", pp->day, 1992b15cb3dSCy Schubert pp->hour, pp->minute, pp->second, pp->nsec, status, thi, 200ea906c41SOllivier Robert tlo); 201ea906c41SOllivier Robert pp->lencode = strlen(pp->a_lastcode); 202ea906c41SOllivier Robert 203ea906c41SOllivier Robert /* compute the timecode timestamp */ 204ea906c41SOllivier Robert if (!refclock_process(pp)) { 205ea906c41SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 206ea906c41SOllivier Robert return; 207ea906c41SOllivier Robert } 208ea906c41SOllivier Robert /* simulate the NTP receive and packet procedures */ 209ea906c41SOllivier Robert refclock_receive(peer); 210ea906c41SOllivier Robert /* write clock statistics to file */ 211ea906c41SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 212ea906c41SOllivier Robert 213ea906c41SOllivier Robert /* With the first timecode beginning the day, check for a GPS 214ea906c41SOllivier Robert leap second notification. */ 215ea906c41SOllivier Robert if (pp->hour < up->last_hour) { 216ea906c41SOllivier Robert check_leap_sec(pp, unit); 217ea906c41SOllivier Robert up->msg_flag[0] = up->msg_flag[1] = 0; /* reset flags */ 218ea906c41SOllivier Robert } 219ea906c41SOllivier Robert up->last_hour = pp->hour; 220ea906c41SOllivier Robert } 221ea906c41SOllivier Robert 222ea906c41SOllivier Robert /* check_leap_sec: read the Dual Port RAM leap second day registers. The 223ea906c41SOllivier Robert onboard GPS receiver should write the hundreds digit of day of year in 224ea906c41SOllivier Robert DP_LeapSec_Day1000Day100 and the tens and ones digits in 225ea906c41SOllivier Robert DP_LeapSec_Day10Day1. If these values are nonzero and today, we have 226ea906c41SOllivier Robert a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND. 227ea906c41SOllivier Robert If the BCD data are zero or a date other than today, set pp->leap to 228ea906c41SOllivier Robert LEAP_NOWARNING. */ 229ea906c41SOllivier Robert static void 230ea906c41SOllivier Robert check_leap_sec(struct refclockproc *pp, int unit) 231ea906c41SOllivier Robert { 232ea906c41SOllivier Robert unsigned char dhi, dlo; 233ea906c41SOllivier Robert int leap_day; 234ea906c41SOllivier Robert 235ea906c41SOllivier Robert regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1; 236ea906c41SOllivier Robert usleep(DELAY); 237ea906c41SOllivier Robert dlo = regp[unit]->dp_ram_data; 238ea906c41SOllivier Robert regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100; 239ea906c41SOllivier Robert usleep(DELAY); 240ea906c41SOllivier Robert dhi = regp[unit]->dp_ram_data; 241ea906c41SOllivier Robert leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F); 242ea906c41SOllivier Robert 243ea906c41SOllivier Robert pp->leap = LEAP_NOWARNING; /* default */ 244ea906c41SOllivier Robert if (leap_day && leap_day == pp->day) { 245ea906c41SOllivier Robert pp->leap = LEAP_ADDSECOND; /* leap second today */ 246ea906c41SOllivier Robert msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).", 247ea906c41SOllivier Robert leap_day, dhi, dlo); 248ea906c41SOllivier Robert } 249c0b746e5SOllivier Robert } 250c0b746e5SOllivier Robert 251c0b746e5SOllivier Robert #else 252*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 253c0b746e5SOllivier Robert #endif /* REFCLOCK */ 254