182498Sroberto/*
282498Sroberto * refclock_hopfpci.c
382498Sroberto *
482498Sroberto * - clock driver for hopf 6039 PCI board (GPS or DCF77)
582498Sroberto * Bernd Altmeier altmeier@atlsoft.de
682498Sroberto *
782498Sroberto * latest source and further information can be found at:
882498Sroberto * http://www.ATLSoft.de/ntp
982498Sroberto *
1082498Sroberto * In order to run this driver you have to install and test
1182498Sroberto * the PCI-board driver for your system first.
1282498Sroberto *
1382498Sroberto * On Linux/UNIX
1482498Sroberto *
1582498Sroberto * The driver attempts to open the device /dev/hopf6039 .
1682498Sroberto * The device entry will be made by the installation process of
1782498Sroberto * the kernel module for the PCI-bus board. The driver sources
1882498Sroberto * belongs to the delivery equipment of the PCI-board.
1982498Sroberto *
2082498Sroberto * On Windows NT/2000
2182498Sroberto *
2282498Sroberto * The driver attempts to open the device by calling the function
2382498Sroberto * "OpenHopfDevice()". This function will be installed by the
2482498Sroberto * Device Driver for the PCI-bus board. The driver belongs to the
2582498Sroberto * delivery equipment of the PCI-board.
2682498Sroberto *
2782498Sroberto *
2882498Sroberto * Start   21.03.2000 Revision: 01.20
2982498Sroberto * changes 22.12.2000 Revision: 01.40 flag1 = 1 sync even if Quarz
3082498Sroberto *
3182498Sroberto */
3282498Sroberto
3382498Sroberto#ifdef HAVE_CONFIG_H
3482498Sroberto# include <config.h>
3582498Sroberto#endif
3682498Sroberto
3782498Sroberto#if defined(REFCLOCK) && defined(CLOCK_HOPF_PCI)
3882498Sroberto
3982498Sroberto#include "ntpd.h"
4082498Sroberto#include "ntp_io.h"
4182498Sroberto#include "ntp_refclock.h"
4282498Sroberto#include "ntp_unixtime.h"
4382498Sroberto#include "ntp_stdlib.h"
4482498Sroberto
4582498Sroberto#undef fileno
4682498Sroberto#include <ctype.h>
4782498Sroberto#undef fileno
4882498Sroberto
4982498Sroberto#ifndef SYS_WINNT
5082498Sroberto# include <sys/ipc.h>
51132451Sroberto# include <sys/ioctl.h>
5282498Sroberto# include <assert.h>
5382498Sroberto# include <unistd.h>
5482498Sroberto# include <stdio.h>
5582498Sroberto# include "hopf6039.h"
5682498Sroberto#else
5782498Sroberto# include "hopf_PCI_io.h"
5882498Sroberto#endif
5982498Sroberto
6082498Sroberto/*
6182498Sroberto * hopfpci interface definitions
6282498Sroberto */
6382498Sroberto#define PRECISION       (-10)    /* precision assumed (1 ms) */
6482498Sroberto#define REFID           "hopf"   /* reference ID */
6582498Sroberto#define DESCRIPTION     "hopf Elektronik PCI radio board"
6682498Sroberto
6782498Sroberto#define NSAMPLES        3       /* stages of median filter */
6882498Sroberto#ifndef SYS_WINNT
6982498Sroberto# define	DEVICE	"/dev/hopf6039" 	/* device name inode*/
7082498Sroberto#else
7182498Sroberto# define	DEVICE	"hopf6039" 	/* device name WinNT  */
7282498Sroberto#endif
7382498Sroberto
7482498Sroberto#define LEWAPWAR	0x20	/* leap second warning bit */
7582498Sroberto
7682498Sroberto#define	HOPF_OPMODE	0xC0	/* operation mode mask */
7782498Sroberto#define HOPF_INVALID	0x00	/* no time code available */
7882498Sroberto#define HOPF_INTERNAL	0x40	/* internal clock */
7982498Sroberto#define HOPF_RADIO	0x80	/* radio clock */
8082498Sroberto#define HOPF_RADIOHP	0xC0	/* high precision radio clock */
8182498Sroberto
8282498Sroberto
8382498Sroberto/*
8482498Sroberto * hopfclock unit control structure.
8582498Sroberto */
8682498Srobertostruct hopfclock_unit {
8782498Sroberto	short	unit;		/* NTP refclock unit number */
8882498Sroberto	char	leap_status;	/* leap second flag */
8982498Sroberto};
9082498Srobertoint	fd;			/* file descr. */
9182498Sroberto
9282498Sroberto/*
9382498Sroberto * Function prototypes
9482498Sroberto */
9582498Srobertostatic  int     hopfpci_start       (int, struct peer *);
9682498Srobertostatic  void    hopfpci_shutdown    (int, struct peer *);
9782498Srobertostatic  void    hopfpci_poll        (int unit, struct peer *);
9882498Sroberto
9982498Sroberto/*
10082498Sroberto * Transfer vector
10182498Sroberto */
10282498Srobertostruct  refclock refclock_hopfpci = {
10382498Sroberto	hopfpci_start,          /* start up driver */
10482498Sroberto	hopfpci_shutdown,       /* shut down driver */
10582498Sroberto	hopfpci_poll,           /* transmit poll message */
10682498Sroberto	noentry,                /* not used */
10782498Sroberto	noentry,                /* initialize driver (not used) */
10882498Sroberto	noentry,                /* not used */
10982498Sroberto	NOFLAGS                 /* not used */
11082498Sroberto};
11182498Sroberto
11282498Sroberto/*
11382498Sroberto * hopfpci_start - attach to hopf PCI board 6039
11482498Sroberto */
11582498Srobertostatic int
11682498Srobertohopfpci_start(
11782498Sroberto	int unit,
11882498Sroberto	struct peer *peer
11982498Sroberto	)
12082498Sroberto{
12182498Sroberto	struct refclockproc *pp;
12282498Sroberto	struct hopfclock_unit *up;
12382498Sroberto
12482498Sroberto	/*
12582498Sroberto	 * Allocate and initialize unit structure
12682498Sroberto	 */
127290001Sglebius	up = emalloc_zero(sizeof(*up));
12882498Sroberto
12982498Sroberto#ifndef SYS_WINNT
13082498Sroberto
13182498Sroberto 	fd = open(DEVICE,O_RDWR); /* try to open hopf clock device */
13282498Sroberto
13382498Sroberto#else
134290001Sglebius	if (!OpenHopfDevice()) {
135290001Sglebius		msyslog(LOG_ERR, "Start: %s unit: %d failed!", DEVICE, unit);
136290001Sglebius		free(up);
13782498Sroberto		return (0);
13882498Sroberto	}
13982498Sroberto#endif
14082498Sroberto
14182498Sroberto	pp = peer->procptr;
14282498Sroberto	pp->io.clock_recv = noentry;
143290001Sglebius	pp->io.srcclock = peer;
14482498Sroberto	pp->io.datalen = 0;
145132451Sroberto	pp->io.fd = INVALID_SOCKET;
146290001Sglebius	pp->unitptr = up;
14782498Sroberto
14882498Sroberto	get_systime(&pp->lastrec);
14982498Sroberto
15082498Sroberto	/*
15182498Sroberto	 * Initialize miscellaneous peer variables
15282498Sroberto	 */
153290001Sglebius	memcpy((char *)&pp->refid, REFID, 4);
154290001Sglebius	peer->precision = PRECISION;
155290001Sglebius	pp->clockdesc = DESCRIPTION;
156290001Sglebius	up->leap_status = 0;
157290001Sglebius	up->unit = (short) unit;
158290001Sglebius	return (1);
15982498Sroberto}
16082498Sroberto
16182498Sroberto
16282498Sroberto/*
16382498Sroberto * hopfpci_shutdown - shut down the clock
16482498Sroberto */
16582498Srobertostatic void
16682498Srobertohopfpci_shutdown(
16782498Sroberto	int unit,
16882498Sroberto	struct peer *peer
16982498Sroberto	)
17082498Sroberto{
17182498Sroberto
17282498Sroberto#ifndef SYS_WINNT
17382498Sroberto	close(fd);
17482498Sroberto#else
17582498Sroberto	CloseHopfDevice();
17682498Sroberto#endif
177290001Sglebius	if (NULL != peer->procptr->unitptr)
178290001Sglebius		free(peer->procptr->unitptr);
17982498Sroberto}
18082498Sroberto
18182498Sroberto
18282498Sroberto/*
18382498Sroberto * hopfpci_poll - called by the transmit procedure
18482498Sroberto */
18582498Srobertostatic void
18682498Srobertohopfpci_poll(
18782498Sroberto	int unit,
18882498Sroberto	struct peer *peer
18982498Sroberto	)
19082498Sroberto{
19182498Sroberto	struct refclockproc *pp;
19282498Sroberto	HOPFTIME m_time;
19382498Sroberto
19482498Sroberto	pp = peer->procptr;
19582498Sroberto
19682498Sroberto#ifndef SYS_WINNT
197290001Sglebius	if (ioctl(fd, HOPF_CLOCK_GET_UTC, &m_time) < 0)
198290001Sglebius		msyslog(LOG_ERR, "HOPF_P(%d): HOPF_CLOCK_GET_UTC: %m",
199290001Sglebius			unit);
20082498Sroberto#else
20182498Sroberto	GetHopfSystemTime(&m_time);
20282498Sroberto#endif
20382498Sroberto	pp->polls++;
20482498Sroberto
20582498Sroberto	pp->day    = ymd2yd(m_time.wYear,m_time.wMonth,m_time.wDay);
20682498Sroberto	pp->hour   = m_time.wHour;
20782498Sroberto	pp->minute = m_time.wMinute;
20882498Sroberto	pp->second = m_time.wSecond;
209132451Sroberto	pp->nsec   = m_time.wMilliseconds * 1000000;
21082498Sroberto	if (m_time.wStatus & LEWAPWAR)
21182498Sroberto		pp->leap = LEAP_ADDSECOND;
21282498Sroberto	else
21382498Sroberto		pp->leap = LEAP_NOWARNING;
21482498Sroberto
215290001Sglebius	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
216290001Sglebius		 "ST: %02X T: %02d:%02d:%02d.%03ld D: %02d.%02d.%04d",
217290001Sglebius		 m_time.wStatus, pp->hour, pp->minute, pp->second,
218290001Sglebius		 pp->nsec / 1000000, m_time.wDay, m_time.wMonth,
219290001Sglebius		 m_time.wYear);
220132451Sroberto	pp->lencode = (u_short)strlen(pp->a_lastcode);
22182498Sroberto
22282498Sroberto	get_systime(&pp->lastrec);
22382498Sroberto
22482498Sroberto	/*
22582498Sroberto	 * If clock has no valid status then report error and exit
22682498Sroberto	 */
22782498Sroberto	if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INVALID) {  /* time ok? */
22882498Sroberto		refclock_report(peer, CEVNT_BADTIME);
22982498Sroberto		pp->leap = LEAP_NOTINSYNC;
23082498Sroberto		return;
23182498Sroberto	}
23282498Sroberto
23382498Sroberto	/*
23482498Sroberto	 * Test if time is running on internal quarz
23582498Sroberto	 * if CLK_FLAG1 is set, sychronize even if no radio operation
23682498Sroberto	 */
23782498Sroberto
23882498Sroberto	if ((m_time.wStatus & HOPF_OPMODE) == HOPF_INTERNAL){
23982498Sroberto		if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
24082498Sroberto			refclock_report(peer, CEVNT_BADTIME);
24182498Sroberto			pp->leap = LEAP_NOTINSYNC;
24282498Sroberto			return;
24382498Sroberto		}
24482498Sroberto	}
24582498Sroberto
24682498Sroberto	if (!refclock_process(pp)) {
24782498Sroberto		refclock_report(peer, CEVNT_BADTIME);
24882498Sroberto		return;
24982498Sroberto	}
250132451Sroberto	pp->lastref = pp->lastrec;
25182498Sroberto	refclock_receive(peer);
25282498Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
25382498Sroberto	return;
25482498Sroberto}
25582498Sroberto
25682498Sroberto#else
25782498Srobertoint refclock_hopfpci_bs;
25882498Sroberto#endif /* REFCLOCK */
259