1 /* $NetBSD: refclock_gpsvme.c,v 1.2 2003/12/04 16:23:37 drochner Exp $ */
3 /* refclock_psc.c: clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */
7 #endif /* HAVE_CONFIG_H */
9 #if defined(REFCLOCK) && defined(CLOCK_GPSVME)
13 #include "ntp_refclock.h"
14 #include "ntp_unixtime.h"
15 #include "ntp_stdlib.h"
18 #include <sys/rtprio.h> /* may already be included above */
19 #include <sys/lock.h> /* NEEDED for PROCLOCK */
23 #include <sys/ioctl.h> /* for _IOR, ioctl */
24 #endif /* __linux__ */
26 enum { /* constants */
28 PSC_SYNC_OK
= 0x40, /* Sync status bit */
29 DP_LEAPSEC_DAY10DAY1
= 0x82, /* DP RAM address */
30 DP_LEAPSEC_DAY1000DAY100
= 0x83,
32 NUNIT
= 2 /* max UNITS */
35 /* clock card registers */
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
];
59 #define PSC_REGS _IOR('K', 0, long) /* ioctl argument */
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 + \
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 */
74 /* clock unit control structure */
76 short unit
; /* NTP refclock unit number */
77 short last_hour
; /* last hour (monitor leap sec) */
78 int msg_flag
[2]; /* count error messages */
80 int fd
[NUNIT
]; /* file descriptor */
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);
89 struct refclock refclock_gpsvme
= {
90 psc_start
, psc_shutdown
, psc_poll
, noentry
, noentry
, noentry
, NOFLAGS
93 /* psc_start: open device and initialize data for processing */
101 struct refclockproc
*pp
;
102 struct psc_unit
*up
= emalloc(sizeof *up
);
104 if (unit
< 0 || unit
> 1) { /* support units 0 and 1 */
105 msyslog(LOG_ERR
, "psc_start: bad unit: %d", unit
);
110 msyslog(LOG_ERR
, "psc_start: unit: %d, emalloc: %m", unit
);
113 memset(up
, '\0', sizeof *up
);
115 sprintf(buf
, DEVICE
, unit
); /* dev file name */
116 fd
[unit
] = open(buf
, O_RDONLY
); /* open device file */
118 msyslog(LOG_ERR
, "psc_start: unit: %d, open failed. %m", unit
);
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
);
128 /* initialize peer variables */
130 pp
->io
.clock_recv
= noentry
;
131 pp
->io
.srcclock
= (caddr_t
) peer
;
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
;
141 rtprio(0,120); /* set real time priority */
142 plock(PROCLOCK
); /* lock process in memory */
147 /* psc_shutdown: shut down the clock */
154 free(peer
->procptr
->unitptr
);
158 /* psc_poll: read, decode, and record device time */
165 struct refclockproc
*pp
= peer
->procptr
;
168 unsigned char status
;
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 */
175 if (!(status
& PSC_SYNC_OK
)) {
176 refclock_report(peer
, CEVNT_BADTIME
);
177 if (!up
->msg_flag
[unit
]) { /* write once to system log */
179 "SYNCHRONIZATION LOST on unit %1d, status %02x\n",
181 up
->msg_flag
[unit
] = 1;
186 get_systime(&pp
->lastrec
);
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);
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
,
204 pp
->lencode
= strlen(pp
->a_lastcode
);
206 /* compute the timecode timestamp */
207 if (!refclock_process(pp
)) {
208 refclock_report(peer
, CEVNT_BADTIME
);
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
);
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 */
222 up
->last_hour
= pp
->hour
;
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
233 check_leap_sec(struct refclockproc
*pp
, int unit
)
235 unsigned char dhi
, dlo
;
238 regp
[unit
]->dp_ram_addr
= DP_LEAPSEC_DAY10DAY1
;
240 dlo
= regp
[unit
]->dp_ram_data
;
241 regp
[unit
]->dp_ram_addr
= DP_LEAPSEC_DAY1000DAY100
;
243 dhi
= regp
[unit
]->dp_ram_data
;
244 leap_day
= BCD2INT2(dlo
) + 100*(dhi
& 0x0F);
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).",
255 int refclock_gpsvme_bs
;
256 #endif /* REFCLOCK */