1 /*	$NetBSD: refclock_gpsvme.c,v 1.1.1.1 2009/12/13 16:55:48 kardel Exp $	*/
2 
3 /* refclock_psc.c:  clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */
4 
5 #ifdef	HAVE_CONFIG_H
6 #include	<config.h>
7 #endif	/* HAVE_CONFIG_H	*/
8 
9 #if defined(REFCLOCK) && defined(CLOCK_GPSVME)
10 
11 #include	"ntpd.h"
12 #include	"ntp_io.h"
13 #include	"ntp_refclock.h"
14 #include	"ntp_unixtime.h"
15 #include	"ntp_stdlib.h"
16 
17 #ifdef	__hpux
18 #include	<sys/rtprio.h>	/* may already be included above	*/
19 #include	<sys/lock.h>	/* NEEDED for PROCLOCK			*/
20 #endif	/* __hpux	*/
21 
22 #ifdef	__linux__
23 #include	<sys/ioctl.h>	/* for _IOR, ioctl			*/
24 #endif	/* __linux__	*/
25 
26 enum {				/* constants	*/
27     BUFSIZE			=	32,
28     PSC_SYNC_OK			=	0x40,	/* Sync status bit	*/
29     DP_LEAPSEC_DAY10DAY1 	= 	0x82,	/* DP RAM address	*/
30     DP_LEAPSEC_DAY1000DAY100	=	0x83,
31     DELAY			=	1,
32     NUNIT			=	2	/* max UNITS		*/
33 };
34 
35 /*	clock card registers	*/
36 struct psc_regs {
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];
58 
59 #define	PSC_REGS	_IOR('K', 0, long)     	/* ioctl argument	*/
60 
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 + \
66 ((val) & 0x0f) )
67 
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	*/
73 
74 /* clock unit control structure */
75 struct psc_unit {
76     short	unit;		/* NTP refclock unit number	*/
77     short	last_hour;	/* last hour (monitor leap sec)	*/
78     int		msg_flag[2];	/* count error messages		*/
79 };
80 int	fd[NUNIT];		/* file descriptor	*/
81 
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);
87 
88 /* Transfer vector	*/
89 struct refclock	refclock_gpsvme = {
90     psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS
91 };
92 
93 /* psc_start:  open device and initialize data for processing */
94 static int
95 psc_start(
96     int		unit,
97     struct peer	*peer
98     )
99 {
100     char			buf[BUFSIZE];
101     struct refclockproc		*pp;
102     struct psc_unit		*up = emalloc(sizeof *up);
103 
104     if (unit < 0 || unit > 1) {		/* support units 0 and 1	*/
105 	msyslog(LOG_ERR, "psc_start: bad unit: %d", unit);
106 	return 0;
107     }
108 
109     if (!up) {
110 	msyslog(LOG_ERR, "psc_start: unit: %d, emalloc: %m", unit);
111 	return 0;
112     }
113     memset(up, '\0', sizeof *up);
114 
115     sprintf(buf, DEVICE, unit);		/* dev file name	*/
116     fd[unit] = open(buf, O_RDONLY);	/* open device file	*/
117     if (fd[unit] < 0) {
118 	msyslog(LOG_ERR, "psc_start: unit: %d, open failed.  %m", unit);
119 	return 0;
120     }
121 
122     /* get the address of the mapped regs	*/
123     if (ioctl(fd[unit], PSC_REGS, &regp[unit]) < 0) {
124 	msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed.  %m", unit);
125 	return 0;
126     }
127 
128     /* initialize peer variables	*/
129     pp = peer->procptr;
130     pp->io.clock_recv = noentry;
131     pp->io.srcclock = (caddr_t) peer;
132     pp->io.datalen = 0;
133     pp->io.fd = -1;
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;
139     up->unit = unit;
140 #ifdef	__hpux
141     rtprio(0,120); 		/* set real time priority	*/
142     plock(PROCLOCK); 		/* lock process in memory	*/
143 #endif	/* __hpux	*/
144     return 1;
145 }
146 
147 /* psc_shutdown:  shut down the clock */
148 static void
149 psc_shutdown(
150     int		unit,
151     struct peer	*peer
152     )
153 {
154     free(peer->procptr->unitptr);
155     close(fd[unit]);
156 }
157 
158 /* psc_poll:  read, decode, and record device time */
159 static void
160 psc_poll(
161     int		unit,
162     struct peer	*peer
163     )
164 {
165     struct refclockproc	*pp = peer->procptr;
166     struct psc_unit		*up;
167     unsigned			tlo, thi;
168     unsigned char		status;
169 
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	*/
174 
175     if (!(status & PSC_SYNC_OK)) {
176 	refclock_report(peer, CEVNT_BADTIME);
177 	if (!up->msg_flag[unit]) {	/* write once to system log	*/
178 	    msyslog(LOG_WARNING,
179 		"SYNCHRONIZATION LOST on unit %1d, status %02x\n",
180 		status, unit);
181 	    up->msg_flag[unit] = 1;
182 	}
183 	return;
184     }
185 
186     get_systime(&pp->lastrec);
187     pp->polls++;
188 
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);
200 
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,
203 	tlo);
204     pp->lencode = strlen(pp->a_lastcode);
205 
206     /* compute the timecode timestamp	*/
207     if (!refclock_process(pp)) {
208 	refclock_report(peer, CEVNT_BADTIME);
209 	return;
210     }
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);
215 
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	*/
221     }
222     up->last_hour = pp->hour;
223 }
224 
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
231    LEAP_NOWARNING.  */
232 static void
233 check_leap_sec(struct refclockproc *pp, int unit)
234 {
235     unsigned char	dhi, dlo;
236     int			leap_day;
237 
238     regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1;
239     usleep(DELAY);
240     dlo = regp[unit]->dp_ram_data;
241     regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100;
242     usleep(DELAY);
243     dhi = regp[unit]->dp_ram_data;
244     leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F);
245 
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).",
250 	    leap_day, dhi, dlo);
251     }
252 }
253 
254 #else
255 int	refclock_gpsvme_bs;
256 #endif	/* REFCLOCK	*/
257