1182007Sroberto/* refclock_psc.c:  clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */
254359Sroberto
3182007Sroberto#ifdef	HAVE_CONFIG_H
4182007Sroberto#include	<config.h>
5182007Sroberto#endif	/* HAVE_CONFIG_H	*/
654359Sroberto
7182007Sroberto#if defined(REFCLOCK) && defined(CLOCK_GPSVME)
882498Sroberto
9182007Sroberto#include	"ntpd.h"
10182007Sroberto#include	"ntp_io.h"
11182007Sroberto#include	"ntp_refclock.h"
12182007Sroberto#include	"ntp_unixtime.h"
13182007Sroberto#include	"ntp_stdlib.h"
1482498Sroberto
15182007Sroberto#ifdef	__hpux
16182007Sroberto#include	<sys/rtprio.h>	/* may already be included above	*/
17182007Sroberto#include	<sys/lock.h>	/* NEEDED for PROCLOCK			*/
18182007Sroberto#endif	/* __hpux	*/
1954359Sroberto
20182007Sroberto#ifdef	__linux__
21182007Sroberto#include	<sys/ioctl.h>	/* for _IOR, ioctl			*/
22182007Sroberto#endif	/* __linux__	*/
2354359Sroberto
24182007Srobertoenum {				/* constants	*/
25182007Sroberto    BUFSIZE			=	32,
26182007Sroberto    PSC_SYNC_OK			=	0x40,	/* Sync status bit	*/
27182007Sroberto    DP_LEAPSEC_DAY10DAY1 	= 	0x82,	/* DP RAM address	*/
28182007Sroberto    DP_LEAPSEC_DAY1000DAY100	=	0x83,
29182007Sroberto    DELAY			=	1,
30182007Sroberto    NUNIT			=	2	/* max UNITS		*/
3154359Sroberto};
3254359Sroberto
33182007Sroberto/*	clock card registers	*/
34182007Srobertostruct psc_regs {
35182007Sroberto    uint32_t		low_time;	/* card base + 0x00	*/
36182007Sroberto    uint32_t		high_time;	/* card base + 0x04	*/
37182007Sroberto    uint32_t		ext_low_time;	/* card base + 0x08	*/
38182007Sroberto    uint32_t		ext_high_time;	/* card base + 0x0C	*/
39182007Sroberto    uint8_t		device_status;	/* card base + 0x10	*/
40182007Sroberto    uint8_t		device_control;	/* card base + 0x11	*/
41182007Sroberto    uint8_t		reserved0;	/* card base + 0x12	*/
42182007Sroberto    uint8_t		ext_100ns;	/* card base + 0x13	*/
43182007Sroberto    uint8_t		match_usec;	/* card base + 0x14	*/
44182007Sroberto    uint8_t		match_msec;	/* card base + 0x15	*/
45182007Sroberto    uint8_t		reserved1;	/* card base + 0x16	*/
46182007Sroberto    uint8_t		reserved2;	/* card base + 0x17	*/
47182007Sroberto    uint8_t		reserved3;	/* card base + 0x18	*/
48182007Sroberto    uint8_t		reserved4;	/* card base + 0x19	*/
49182007Sroberto    uint8_t		dp_ram_addr;	/* card base + 0x1A	*/
50182007Sroberto    uint8_t		reserved5;	/* card base + 0x1B	*/
51182007Sroberto    uint8_t		reserved6;	/* card base + 0x1C	*/
52182007Sroberto    uint8_t		reserved7;	/* card base + 0x1D	*/
53182007Sroberto    uint8_t		dp_ram_data;	/* card base + 0x1E	*/
54182007Sroberto    uint8_t		reserved8;	/* card base + 0x1F	*/
55182007Sroberto} *volatile regp[NUNIT];
5654359Sroberto
57182007Sroberto#define	PSC_REGS	_IOR('K', 0, long)     	/* ioctl argument	*/
5854359Sroberto
59182007Sroberto/* Macros to swap byte order and convert BCD to binary	*/
60182007Sroberto#define SWAP(val) ( ((val) >> 24) | (((val) & 0x00ff0000) >> 8) | \
61182007Sroberto(((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24) )
62182007Sroberto#define BCD2INT2(val)  ( ((val) >> 4 & 0x0f)*10 + ((val) & 0x0f) )
63182007Sroberto#define BCD2INT3(val)  ( ((val) >> 8 & 0x0f)*100 + ((val) >> 4 & 0x0f)*10 + \
64182007Sroberto((val) & 0x0f) )
6554359Sroberto
66182007Sroberto/* PSC interface definitions */
67182007Sroberto#define PRECISION	(-20)	/* precision assumed (1 us)	*/
68182007Sroberto#define REFID		"USNO"	/* reference ID	*/
69182007Sroberto#define DESCRIPTION	"Brandywine PCI-SyncClock32"
70182007Sroberto#define DEVICE		"/dev/refclock%1d"	/* device file	*/
7154359Sroberto
72182007Sroberto/* clock unit control structure */
73182007Srobertostruct psc_unit {
74182007Sroberto    short	unit;		/* NTP refclock unit number	*/
75182007Sroberto    short	last_hour;	/* last hour (monitor leap sec)	*/
76182007Sroberto    int		msg_flag[2];	/* count error messages		*/
7754359Sroberto};
78182007Srobertoint	fd[NUNIT];		/* file descriptor	*/
7954359Sroberto
80182007Sroberto/* Local function prototypes */
81182007Srobertostatic int		psc_start(int, struct peer *);
82182007Srobertostatic void		psc_shutdown(int, struct peer *);
83182007Srobertostatic void		psc_poll(int, struct peer *);
84182007Srobertostatic void		check_leap_sec(struct refclockproc *, int);
8554359Sroberto
86182007Sroberto/* Transfer vector	*/
87182007Srobertostruct refclock	refclock_gpsvme = {
88182007Sroberto    psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS
8954359Sroberto};
9054359Sroberto
91182007Sroberto/* psc_start:  open device and initialize data for processing */
9254359Srobertostatic int
93182007Srobertopsc_start(
94182007Sroberto    int		unit,
95182007Sroberto    struct peer	*peer
96182007Sroberto    )
9754359Sroberto{
98182007Sroberto    char			buf[BUFSIZE];
99182007Sroberto    struct refclockproc		*pp;
100182007Sroberto    struct psc_unit		*up = emalloc(sizeof *up);
10154359Sroberto
102182007Sroberto    if (unit < 0 || unit > 1) {		/* support units 0 and 1	*/
103182007Sroberto	msyslog(LOG_ERR, "psc_start: bad unit: %d", unit);
104182007Sroberto	return 0;
105182007Sroberto    }
10654359Sroberto
107182007Sroberto    memset(up, '\0', sizeof *up);
10854359Sroberto
109290001Sglebius    snprintf(buf, sizeof(buf), DEVICE, unit);	/* dev file name	*/
110182007Sroberto    fd[unit] = open(buf, O_RDONLY);	/* open device file	*/
111182007Sroberto    if (fd[unit] < 0) {
112182007Sroberto	msyslog(LOG_ERR, "psc_start: unit: %d, open failed.  %m", unit);
113182007Sroberto	return 0;
114182007Sroberto    }
115182007Sroberto
116182007Sroberto    /* get the address of the mapped regs	*/
117182007Sroberto    if (ioctl(fd[unit], PSC_REGS, &regp[unit]) < 0) {
118182007Sroberto	msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed.  %m", unit);
119182007Sroberto	return 0;
120182007Sroberto    }
12154359Sroberto
122182007Sroberto    /* initialize peer variables	*/
123182007Sroberto    pp = peer->procptr;
124182007Sroberto    pp->io.clock_recv = noentry;
125290001Sglebius    pp->io.srcclock = peer;
126182007Sroberto    pp->io.datalen = 0;
127182007Sroberto    pp->io.fd = -1;
128290001Sglebius    pp->unitptr = up;
129182007Sroberto    get_systime(&pp->lastrec);
130290001Sglebius    memcpy(&pp->refid, REFID, 4);
131182007Sroberto    peer->precision = PRECISION;
132182007Sroberto    pp->clockdesc = DESCRIPTION;
133182007Sroberto    up->unit = unit;
134182007Sroberto#ifdef	__hpux
135182007Sroberto    rtprio(0,120); 		/* set real time priority	*/
136182007Sroberto    plock(PROCLOCK); 		/* lock process in memory	*/
137182007Sroberto#endif	/* __hpux	*/
138182007Sroberto    return 1;
13954359Sroberto}
14054359Sroberto
141182007Sroberto/* psc_shutdown:  shut down the clock */
14254359Srobertostatic void
143182007Srobertopsc_shutdown(
144182007Sroberto    int		unit,
145182007Sroberto    struct peer	*peer
146182007Sroberto    )
14754359Sroberto{
148290001Sglebius    if (NULL != peer->procptr->unitptr)
149290001Sglebius	free(peer->procptr->unitptr);
150290001Sglebius    if (fd[unit] > 0)
151290001Sglebius	close(fd[unit]);
15254359Sroberto}
15354359Sroberto
154182007Sroberto/* psc_poll:  read, decode, and record device time */
15554359Srobertostatic void
156182007Srobertopsc_poll(
157182007Sroberto    int		unit,
158182007Sroberto    struct peer	*peer
159182007Sroberto    )
16054359Sroberto{
161182007Sroberto    struct refclockproc	*pp = peer->procptr;
162182007Sroberto    struct psc_unit		*up;
163182007Sroberto    unsigned			tlo, thi;
164182007Sroberto    unsigned char		status;
16554359Sroberto
166182007Sroberto    up = (struct psc_unit *) pp->unitptr;
167182007Sroberto    tlo = regp[unit]->low_time;		/* latch and read first 4 bytes	*/
168182007Sroberto    thi = regp[unit]->high_time;	/* read 4 higher order bytes	*/
169182007Sroberto    status = regp[unit]->device_status;	/* read device status byte	*/
17054359Sroberto
171182007Sroberto    if (!(status & PSC_SYNC_OK)) {
172182007Sroberto	refclock_report(peer, CEVNT_BADTIME);
173182007Sroberto	if (!up->msg_flag[unit]) {	/* write once to system log	*/
174182007Sroberto	    msyslog(LOG_WARNING,
175182007Sroberto		"SYNCHRONIZATION LOST on unit %1d, status %02x\n",
176290001Sglebius		unit, status);
177182007Sroberto	    up->msg_flag[unit] = 1;
17854359Sroberto	}
179182007Sroberto	return;
180182007Sroberto    }
18154359Sroberto
182182007Sroberto    get_systime(&pp->lastrec);
183182007Sroberto    pp->polls++;
184182007Sroberto
185182007Sroberto    tlo = SWAP(tlo);			/* little to big endian swap on	*/
186182007Sroberto    thi = SWAP(thi);			/* copy of data			*/
187182007Sroberto    /* convert the BCD time to broken down time used by refclockproc	*/
188182007Sroberto    pp->day	= BCD2INT3((thi & 0x0FFF0000) >> 16);
189182007Sroberto    pp->hour	= BCD2INT2((thi & 0x0000FF00) >> 8);
190182007Sroberto    pp->minute	= BCD2INT2(thi & 0x000000FF);
191182007Sroberto    pp->second	= BCD2INT2(tlo >> 24);
192182007Sroberto    /* ntp_process() in ntp_refclock.c appears to use usec as fraction of
193182007Sroberto       second in microseconds if usec is nonzero. */
194182007Sroberto    pp->nsec	= 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) +
195182007Sroberto	BCD2INT3(tlo & 0x00000FFF);
19654359Sroberto
197290001Sglebius    snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
198290001Sglebius	     "%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x", pp->day,
199290001Sglebius	     pp->hour, pp->minute, pp->second, pp->nsec, status, thi,
200290001Sglebius	     tlo);
201182007Sroberto    pp->lencode = strlen(pp->a_lastcode);
20254359Sroberto
203182007Sroberto    /* compute the timecode timestamp	*/
204182007Sroberto    if (!refclock_process(pp)) {
205182007Sroberto	refclock_report(peer, CEVNT_BADTIME);
206182007Sroberto	return;
207182007Sroberto    }
208182007Sroberto    /* simulate the NTP receive and packet procedures	*/
209182007Sroberto    refclock_receive(peer);
210182007Sroberto    /* write clock statistics to file	*/
211182007Sroberto    record_clock_stats(&peer->srcadr, pp->a_lastcode);
21254359Sroberto
213182007Sroberto    /* With the first timecode beginning the day, check for a GPS
214182007Sroberto       leap second notification.      */
215182007Sroberto    if (pp->hour < up->last_hour) {
216182007Sroberto	check_leap_sec(pp, unit);
217182007Sroberto	up->msg_flag[0] = up->msg_flag[1] = 0;	/* reset flags	*/
218182007Sroberto    }
219182007Sroberto    up->last_hour = pp->hour;
22054359Sroberto}
22154359Sroberto
222182007Sroberto/* check_leap_sec:  read the Dual Port RAM leap second day registers.  The
223182007Sroberto   onboard GPS receiver should write the hundreds digit of day of year in
224182007Sroberto   DP_LeapSec_Day1000Day100 and the tens and ones digits in
225182007Sroberto   DP_LeapSec_Day10Day1.  If these values are nonzero and today, we have
226182007Sroberto   a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND.
227182007Sroberto   If the BCD data are zero or a date other than today, set pp->leap to
228182007Sroberto   LEAP_NOWARNING.  */
22954359Srobertostatic void
230182007Srobertocheck_leap_sec(struct refclockproc *pp, int unit)
23154359Sroberto{
232182007Sroberto    unsigned char	dhi, dlo;
233182007Sroberto    int			leap_day;
234182007Sroberto
235182007Sroberto    regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1;
236182007Sroberto    usleep(DELAY);
237182007Sroberto    dlo = regp[unit]->dp_ram_data;
238182007Sroberto    regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100;
239182007Sroberto    usleep(DELAY);
240182007Sroberto    dhi = regp[unit]->dp_ram_data;
241182007Sroberto    leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F);
24254359Sroberto
243182007Sroberto    pp->leap = LEAP_NOWARNING;			/* default	*/
244182007Sroberto    if (leap_day && leap_day == pp->day) {
245182007Sroberto	pp->leap = LEAP_ADDSECOND;		/* leap second today	*/
246182007Sroberto	msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).",
247182007Sroberto	    leap_day, dhi, dlo);
248182007Sroberto    }
24954359Sroberto}
25054359Sroberto
25154359Sroberto#else
252182007Srobertoint	refclock_gpsvme_bs;
253182007Sroberto#endif	/* REFCLOCK	*/
254