1/*
2 * refclock_hopfpci.c
3 *
4 * - clock driver for hopf 6039 PCI board (GPS or DCF77)
5 * Bernd Altmeier altmeier@atlsoft.de
6 *
7 * latest source and further information can be found at:
8 * http://www.ATLSoft.de/ntp
9 *
10 * In order to run this driver you have to install and test
11 * the PCI-board driver for your system first.
12 *
13 * On Linux/UNIX
14 *
15 * The driver attempts to open the device /dev/hopf6039 .
16 * The device entry will be made by the installation process of
17 * the kernel module for the PCI-bus board. The driver sources
18 * belongs to the delivery equipment of the PCI-board.
19 *
20 * On Windows NT/2000
21 *
22 * The driver attempts to open the device by calling the function
23 * "OpenHopfDevice()". This function will be installed by the
24 * Device Driver for the PCI-bus board. The driver belongs to the
25 * delivery equipment of the PCI-board.
26 *
27 *
28 * Start   21.03.2000 Revision: 01.20
29 * changes 22.12.2000 Revision: 01.40 flag1 = 1 sync even if Quarz
30 *
31 */
32
33#ifdef HAVE_CONFIG_H
34# include <config.h>
35#endif
36
37#if defined(REFCLOCK) && defined(CLOCK_HOPF_PCI)
38
39#include "ntpd.h"
40#include "ntp_io.h"
41#include "ntp_refclock.h"
42#include "ntp_unixtime.h"
43#include "ntp_stdlib.h"
44
45#undef fileno
46#include <ctype.h>
47#undef fileno
48
49#ifndef SYS_WINNT
50# include <sys/ipc.h>
51# include <sys/ioctl.h>
52# include <assert.h>
53# include <unistd.h>
54# include <stdio.h>
55# include "hopf6039.h"
56#else
57# include "hopf_PCI_io.h"
58#endif
59
60/*
61 * hopfpci interface definitions
62 */
63#define PRECISION       (-10)    /* precision assumed (1 ms) */
64#define REFID           "hopf"   /* reference ID */
65#define DESCRIPTION     "hopf Elektronik PCI radio board"
66
67#define NSAMPLES        3       /* stages of median filter */
68#ifndef SYS_WINNT
69# define	DEVICE	"/dev/hopf6039" 	/* device name inode*/
70#else
71# define	DEVICE	"hopf6039" 	/* device name WinNT  */
72#endif
73
74#define LEWAPWAR	0x20	/* leap second warning bit */
75
76#define	HOPF_OPMODE	0xC0	/* operation mode mask */
77#define HOPF_INVALID	0x00	/* no time code available */
78#define HOPF_INTERNAL	0x40	/* internal clock */
79#define HOPF_RADIO	0x80	/* radio clock */
80#define HOPF_RADIOHP	0xC0	/* high precision radio clock */
81
82
83/*
84 * hopfclock unit control structure.
85 */
86struct hopfclock_unit {
87	short	unit;		/* NTP refclock unit number */
88	char	leap_status;	/* leap second flag */
89};
90int	fd;			/* file descr. */
91
92/*
93 * Function prototypes
94 */
95static  int     hopfpci_start       (int, struct peer *);
96static  void    hopfpci_shutdown    (int, struct peer *);
97static  void    hopfpci_poll        (int unit, struct peer *);
98
99/*
100 * Transfer vector
101 */
102struct  refclock refclock_hopfpci = {
103	hopfpci_start,          /* start up driver */
104	hopfpci_shutdown,       /* shut down driver */
105	hopfpci_poll,           /* transmit poll message */
106	noentry,                /* not used */
107	noentry,                /* initialize driver (not used) */
108	noentry,                /* not used */
109	NOFLAGS                 /* not used */
110};
111
112/*
113 * hopfpci_start - attach to hopf PCI board 6039
114 */
115static int
116hopfpci_start(
117	int unit,
118	struct peer *peer
119	)
120{
121	struct refclockproc *pp;
122	struct hopfclock_unit *up;
123
124	/*
125	 * Allocate and initialize unit structure
126	 */
127	up = emalloc_zero(sizeof(*up));
128
129#ifndef SYS_WINNT
130
131 	fd = open(DEVICE,O_RDWR); /* try to open hopf clock device */
132
133#else
134	if (!OpenHopfDevice()) {
135		msyslog(LOG_ERR, "Start: %s unit: %d failed!", DEVICE, unit);
136		free(up);
137		return (0);
138	}
139#endif
140
141	pp = peer->procptr;
142	pp->io.clock_recv = noentry;
143	pp->io.srcclock = peer;
144	pp->io.datalen = 0;
145	pp->io.fd = INVALID_SOCKET;
146	pp->unitptr = up;
147
148	get_systime(&pp->lastrec);
149
150	/*
151	 * Initialize miscellaneous peer variables
152	 */
153	memcpy((char *)&pp->refid, REFID, 4);
154	peer->precision = PRECISION;
155	pp->clockdesc = DESCRIPTION;
156	up->leap_status = 0;
157	up->unit = (short) unit;
158	return (1);
159}
160
161
162/*
163 * hopfpci_shutdown - shut down the clock
164 */
165static void
166hopfpci_shutdown(
167	int unit,
168	struct peer *peer
169	)
170{
171
172#ifndef SYS_WINNT
173	close(fd);
174#else
175	CloseHopfDevice();
176#endif
177	if (NULL != peer->procptr->unitptr)
178		free(peer->procptr->unitptr);
179}
180
181
182/*
183 * hopfpci_poll - called by the transmit procedure
184 */
185static void
186hopfpci_poll(
187	int unit,
188	struct peer *peer
189	)
190{
191	struct refclockproc *pp;
192	HOPFTIME m_time;
193
194	pp = peer->procptr;
195
196#ifndef SYS_WINNT
197	if (ioctl(fd, HOPF_CLOCK_GET_UTC, &m_time) < 0)
198		msyslog(LOG_ERR, "HOPF_P(%d): HOPF_CLOCK_GET_UTC: %m",
199			unit);
200#else
201	GetHopfSystemTime(&m_time);
202#endif
203	pp->polls++;
204
205	pp->day    = ymd2yd(m_time.wYear,m_time.wMonth,m_time.wDay);
206	pp->hour   = m_time.wHour;
207	pp->minute = m_time.wMinute;
208	pp->second = m_time.wSecond;
209	pp->nsec   = m_time.wMilliseconds * 1000000;
210	if (m_time.wStatus & LEWAPWAR)
211		pp->leap = LEAP_ADDSECOND;
212	else
213		pp->leap = LEAP_NOWARNING;
214
215	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
216		 "ST: %02X T: %02d:%02d:%02d.%03ld D: %02d.%02d.%04d",
217		 m_time.wStatus, pp->hour, pp->minute, pp->second,
218		 pp->nsec / 1000000, m_time.wDay, m_time.wMonth,
219		 m_time.wYear);
220	pp->lencode = (u_short)strlen(pp->a_lastcode);
221
222	get_systime(&pp->lastrec);
223
224	/*
225	 * If clock has no valid status then report error and exit
226	 */
227	if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INVALID) {  /* time ok? */
228		refclock_report(peer, CEVNT_BADTIME);
229		pp->leap = LEAP_NOTINSYNC;
230		return;
231	}
232
233	/*
234	 * Test if time is running on internal quarz
235	 * if CLK_FLAG1 is set, sychronize even if no radio operation
236	 */
237
238	if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INTERNAL){
239		if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
240			refclock_report(peer, CEVNT_BADTIME);
241			pp->leap = LEAP_NOTINSYNC;
242			return;
243		}
244	}
245
246	if (!refclock_process(pp)) {
247		refclock_report(peer, CEVNT_BADTIME);
248		return;
249	}
250	pp->lastref = pp->lastrec;
251	refclock_receive(peer);
252	record_clock_stats(&peer->srcadr, pp->a_lastcode);
253	return;
254}
255
256#else
257int refclock_hopfpci_bs;
258#endif /* REFCLOCK */
259