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