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