summaryrefslogtreecommitdiff
path: root/ntpd/refclock_gpsvme.c
blob: 66ccc9a3ec9008494212a1fefd2c3af3f3256ffd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/* refclock_psc.c:  clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */

#ifdef	HAVE_CONFIG_H
#include	<config.h>
#endif	/* HAVE_CONFIG_H	*/

#if defined(REFCLOCK) && defined(CLOCK_GPSVME)

#include	"ntpd.h"
#include	"ntp_io.h"
#include	"ntp_refclock.h"
#include	"ntp_unixtime.h"
#include	"ntp_stdlib.h"

#ifdef	__hpux
#include	<sys/rtprio.h>	/* may already be included above	*/
#include	<sys/lock.h>	/* NEEDED for PROCLOCK			*/
#endif	/* __hpux	*/

#ifdef	__linux__
#include	<sys/ioctl.h>	/* for _IOR, ioctl			*/
#endif	/* __linux__	*/

enum {				/* constants	*/
    BUFSIZE			=	32,
    PSC_SYNC_OK			=	0x40,	/* Sync status bit	*/
    DP_LEAPSEC_DAY10DAY1 	= 	0x82,	/* DP RAM address	*/
    DP_LEAPSEC_DAY1000DAY100	=	0x83,
    DELAY			=	1,
    NUNIT			=	2	/* max UNITS		*/
};

/*	clock card registers	*/
struct psc_regs {
    uint32_t		low_time;	/* card base + 0x00	*/
    uint32_t		high_time;	/* card base + 0x04	*/
    uint32_t		ext_low_time;	/* card base + 0x08	*/
    uint32_t		ext_high_time;	/* card base + 0x0C	*/
    uint8_t		device_status;	/* card base + 0x10	*/
    uint8_t		device_control;	/* card base + 0x11	*/
    uint8_t		reserved0;	/* card base + 0x12	*/
    uint8_t		ext_100ns;	/* card base + 0x13	*/
    uint8_t		match_usec;	/* card base + 0x14	*/
    uint8_t		match_msec;	/* card base + 0x15	*/
    uint8_t		reserved1;	/* card base + 0x16	*/
    uint8_t		reserved2;	/* card base + 0x17	*/
    uint8_t		reserved3;	/* card base + 0x18	*/
    uint8_t		reserved4;	/* card base + 0x19	*/
    uint8_t		dp_ram_addr;	/* card base + 0x1A	*/
    uint8_t		reserved5;	/* card base + 0x1B	*/
    uint8_t		reserved6;	/* card base + 0x1C	*/
    uint8_t		reserved7;	/* card base + 0x1D	*/
    uint8_t		dp_ram_data;	/* card base + 0x1E	*/
    uint8_t		reserved8;	/* card base + 0x1F	*/
} *volatile regp[NUNIT];

#define	PSC_REGS	_IOR('K', 0, long)     	/* ioctl argument	*/

/* Macros to swap byte order and convert BCD to binary	*/
#define SWAP(val) ( ((val) >> 24) | (((val) & 0x00ff0000) >> 8) | \
(((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24) )
#define BCD2INT2(val)  ( ((val) >> 4 & 0x0f)*10 + ((val) & 0x0f) )
#define BCD2INT3(val)  ( ((val) >> 8 & 0x0f)*100 + ((val) >> 4 & 0x0f)*10 + \
((val) & 0x0f) )

/* PSC interface definitions */
#define PRECISION	(-20)	/* precision assumed (1 us)	*/
#define REFID		"USNO"	/* reference ID	*/
#define DESCRIPTION	"Brandywine PCI-SyncClock32"
#define DEVICE		"/dev/refclock%1d"	/* device file	*/

/* clock unit control structure */
struct psc_unit {
    short	unit;		/* NTP refclock unit number	*/
    short	last_hour;	/* last hour (monitor leap sec)	*/
    int		msg_flag[2];	/* count error messages		*/
};
int	fd[NUNIT];		/* file descriptor	*/

/* Local function prototypes */
static int		psc_start(int, struct peer *);
static void		psc_shutdown(int, struct peer *);
static void		psc_poll(int, struct peer *);
static void		check_leap_sec(struct refclockproc *, int);

/* Transfer vector	*/
struct refclock	refclock_gpsvme = {
    psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS
};

/* psc_start:  open device and initialize data for processing */
static int
psc_start(
    int		unit,
    struct peer	*peer
    )
{
    char			buf[BUFSIZE];
    struct refclockproc		*pp;
    struct psc_unit		*up = emalloc(sizeof *up);

    if (unit < 0 || unit > 1) {		/* support units 0 and 1	*/
	msyslog(LOG_ERR, "psc_start: bad unit: %d", unit);
	return 0;
    }

    memset(up, '\0', sizeof *up);

    snprintf(buf, sizeof(buf), DEVICE, unit);	/* dev file name	*/
    fd[unit] = open(buf, O_RDONLY);	/* open device file	*/
    if (fd[unit] < 0) {
	msyslog(LOG_ERR, "psc_start: unit: %d, open failed.  %m", unit);
	return 0;
    }
     
    /* get the address of the mapped regs	*/
    if (ioctl(fd[unit], PSC_REGS, &regp[unit]) < 0) {
	msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed.  %m", unit);
	return 0;
    }

    /* initialize peer variables	*/
    pp = peer->procptr;
    pp->io.clock_recv = noentry;
    pp->io.srcclock = peer;
    pp->io.datalen = 0;
    pp->io.fd = -1;
    pp->unitptr = up;
    get_systime(&pp->lastrec);
    memcpy(&pp->refid, REFID, 4);
    peer->precision = PRECISION;
    pp->clockdesc = DESCRIPTION;
    up->unit = unit;
#ifdef	__hpux     
    rtprio(0,120); 		/* set real time priority	*/
    plock(PROCLOCK); 		/* lock process in memory	*/
#endif	/* __hpux	*/     
    return 1;
}

/* psc_shutdown:  shut down the clock */
static void
psc_shutdown(
    int		unit,
    struct peer	*peer
    )
{
    if (NULL != peer->procptr->unitptr)
	free(peer->procptr->unitptr);
    if (fd[unit] > 0)
	close(fd[unit]);
}

/* psc_poll:  read, decode, and record device time */
static void
psc_poll(
    int		unit,
    struct peer	*peer
    )
{
    struct refclockproc	*pp = peer->procptr;
    struct psc_unit		*up;
    unsigned			tlo, thi;
    unsigned char		status;

    up = (struct psc_unit *) pp->unitptr;
    tlo = regp[unit]->low_time;		/* latch and read first 4 bytes	*/
    thi = regp[unit]->high_time;	/* read 4 higher order bytes	*/
    status = regp[unit]->device_status;	/* read device status byte	*/

    if (!(status & PSC_SYNC_OK)) {
	refclock_report(peer, CEVNT_BADTIME);
	if (!up->msg_flag[unit]) {	/* write once to system log	*/
	    msyslog(LOG_WARNING,
		"SYNCHRONIZATION LOST on unit %1d, status %02x\n",
		unit, status);
	    up->msg_flag[unit] = 1;
	}
	return;
    }

    get_systime(&pp->lastrec);
    pp->polls++;
     
    tlo = SWAP(tlo);			/* little to big endian swap on	*/
    thi = SWAP(thi);			/* copy of data			*/
    /* convert the BCD time to broken down time used by refclockproc	*/
    pp->day	= BCD2INT3((thi & 0x0FFF0000) >> 16);
    pp->hour	= BCD2INT2((thi & 0x0000FF00) >> 8);
    pp->minute	= BCD2INT2(thi & 0x000000FF);
    pp->second	= BCD2INT2(tlo >> 24);
    /* ntp_process() in ntp_refclock.c appears to use usec as fraction of
       second in microseconds if usec is nonzero. */
    pp->nsec	= 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) +
	BCD2INT3(tlo & 0x00000FFF);

    snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
	     "%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x", pp->day,
	     pp->hour, pp->minute, pp->second, pp->nsec, status, thi,
	     tlo);
    pp->lencode = strlen(pp->a_lastcode);

    /* compute the timecode timestamp	*/
    if (!refclock_process(pp)) {
	refclock_report(peer, CEVNT_BADTIME);
	return;
    }
    /* simulate the NTP receive and packet procedures	*/
    refclock_receive(peer);
    /* write clock statistics to file	*/
    record_clock_stats(&peer->srcadr, pp->a_lastcode);	

    /* With the first timecode beginning the day, check for a GPS
       leap second notification.      */
    if (pp->hour < up->last_hour) {
	check_leap_sec(pp, unit);
	up->msg_flag[0] = up->msg_flag[1] = 0;	/* reset flags	*/
    }
    up->last_hour = pp->hour;
}

/* check_leap_sec:  read the Dual Port RAM leap second day registers.  The
   onboard GPS receiver should write the hundreds digit of day of year in
   DP_LeapSec_Day1000Day100 and the tens and ones digits in
   DP_LeapSec_Day10Day1.  If these values are nonzero and today, we have
   a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND.
   If the BCD data are zero or a date other than today, set pp->leap to
   LEAP_NOWARNING.  */
static void
check_leap_sec(struct refclockproc *pp, int unit)
{
    unsigned char	dhi, dlo;
    int			leap_day;
     
    regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1;
    usleep(DELAY);
    dlo = regp[unit]->dp_ram_data;
    regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100;
    usleep(DELAY);
    dhi = regp[unit]->dp_ram_data;
    leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F);

    pp->leap = LEAP_NOWARNING;			/* default	*/
    if (leap_day && leap_day == pp->day) {
	pp->leap = LEAP_ADDSECOND;		/* leap second today	*/
	msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).",
	    leap_day, dhi, dlo);
    }
}

#else
int	refclock_gpsvme_bs;
#endif	/* REFCLOCK	*/