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