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