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
psc_start(int unit,struct peer * peer)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 memset(up, '\0', sizeof *up);
108
109 snprintf(buf, sizeof(buf), DEVICE, unit); /* dev file name */
110 fd[unit] = open(buf, O_RDONLY); /* open device file */
111 if (fd[unit] < 0) {
112 msyslog(LOG_ERR, "psc_start: unit: %d, open failed. %m", unit);
113 return 0;
114 }
115
116 /* get the address of the mapped regs */
117 if (ioctl(fd[unit], PSC_REGS, ®p[unit]) < 0) {
118 msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed. %m", unit);
119 return 0;
120 }
121
122 /* initialize peer variables */
123 pp = peer->procptr;
124 pp->io.clock_recv = noentry;
125 pp->io.srcclock = peer;
126 pp->io.datalen = 0;
127 pp->io.fd = -1;
128 pp->unitptr = up;
129 get_systime(&pp->lastrec);
130 memcpy(&pp->refid, REFID, 4);
131 peer->precision = PRECISION;
132 pp->clockdesc = DESCRIPTION;
133 up->unit = unit;
134 #ifdef __hpux
135 rtprio(0,120); /* set real time priority */
136 plock(PROCLOCK); /* lock process in memory */
137 #endif /* __hpux */
138 return 1;
139 }
140
141 /* psc_shutdown: shut down the clock */
142 static void
psc_shutdown(int unit,struct peer * peer)143 psc_shutdown(
144 int unit,
145 struct peer *peer
146 )
147 {
148 if (NULL != peer->procptr->unitptr)
149 free(peer->procptr->unitptr);
150 if (fd[unit] > 0)
151 close(fd[unit]);
152 }
153
154 /* psc_poll: read, decode, and record device time */
155 static void
psc_poll(int unit,struct peer * peer)156 psc_poll(
157 int unit,
158 struct peer *peer
159 )
160 {
161 struct refclockproc *pp = peer->procptr;
162 struct psc_unit *up;
163 unsigned tlo, thi;
164 unsigned char status;
165
166 up = (struct psc_unit *) pp->unitptr;
167 tlo = regp[unit]->low_time; /* latch and read first 4 bytes */
168 thi = regp[unit]->high_time; /* read 4 higher order bytes */
169 status = regp[unit]->device_status; /* read device status byte */
170
171 if (!(status & PSC_SYNC_OK)) {
172 refclock_report(peer, CEVNT_BADTIME);
173 if (!up->msg_flag[unit]) { /* write once to system log */
174 msyslog(LOG_WARNING,
175 "SYNCHRONIZATION LOST on unit %1d, status %02x\n",
176 unit, status);
177 up->msg_flag[unit] = 1;
178 }
179 return;
180 }
181
182 get_systime(&pp->lastrec);
183 pp->polls++;
184
185 tlo = SWAP(tlo); /* little to big endian swap on */
186 thi = SWAP(thi); /* copy of data */
187 /* convert the BCD time to broken down time used by refclockproc */
188 pp->day = BCD2INT3((thi & 0x0FFF0000) >> 16);
189 pp->hour = BCD2INT2((thi & 0x0000FF00) >> 8);
190 pp->minute = BCD2INT2(thi & 0x000000FF);
191 pp->second = BCD2INT2(tlo >> 24);
192 /* ntp_process() in ntp_refclock.c appears to use usec as fraction of
193 second in microseconds if usec is nonzero. */
194 pp->nsec = 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) +
195 BCD2INT3(tlo & 0x00000FFF);
196
197 snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
198 "%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x", pp->day,
199 pp->hour, pp->minute, pp->second, pp->nsec, status, thi,
200 tlo);
201 pp->lencode = strlen(pp->a_lastcode);
202
203 /* compute the timecode timestamp */
204 if (!refclock_process(pp)) {
205 refclock_report(peer, CEVNT_BADTIME);
206 return;
207 }
208 /* simulate the NTP receive and packet procedures */
209 refclock_receive(peer);
210 /* write clock statistics to file */
211 record_clock_stats(&peer->srcadr, pp->a_lastcode);
212
213 /* With the first timecode beginning the day, check for a GPS
214 leap second notification. */
215 if (pp->hour < up->last_hour) {
216 check_leap_sec(pp, unit);
217 up->msg_flag[0] = up->msg_flag[1] = 0; /* reset flags */
218 }
219 up->last_hour = pp->hour;
220 }
221
222 /* check_leap_sec: read the Dual Port RAM leap second day registers. The
223 onboard GPS receiver should write the hundreds digit of day of year in
224 DP_LeapSec_Day1000Day100 and the tens and ones digits in
225 DP_LeapSec_Day10Day1. If these values are nonzero and today, we have
226 a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND.
227 If the BCD data are zero or a date other than today, set pp->leap to
228 LEAP_NOWARNING. */
229 static void
check_leap_sec(struct refclockproc * pp,int unit)230 check_leap_sec(struct refclockproc *pp, int unit)
231 {
232 unsigned char dhi, dlo;
233 int leap_day;
234
235 regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1;
236 usleep(DELAY);
237 dlo = regp[unit]->dp_ram_data;
238 regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100;
239 usleep(DELAY);
240 dhi = regp[unit]->dp_ram_data;
241 leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F);
242
243 pp->leap = LEAP_NOWARNING; /* default */
244 if (leap_day && leap_day == pp->day) {
245 pp->leap = LEAP_ADDSECOND; /* leap second today */
246 msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).",
247 leap_day, dhi, dlo);
248 }
249 }
250
251 #else
252 NONEMPTY_TRANSLATION_UNIT
253 #endif /* REFCLOCK */
254