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