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