182498Sroberto/*
282498Sroberto *
382498Sroberto * refclock_hopfser.c
482498Sroberto * - clock driver for hopf serial boards (GPS or DCF77)
582498Sroberto *
682498Sroberto * Date: 30.03.2000 Revision: 01.10
782498Sroberto *
882498Sroberto * latest source and further information can be found at:
982498Sroberto * http://www.ATLSoft.de/ntp
1082498Sroberto *
1182498Sroberto */
1282498Sroberto
1382498Sroberto#ifdef HAVE_CONFIG_H
1482498Sroberto# include "config.h"
1582498Sroberto#endif
1682498Sroberto
1782498Sroberto#if defined(REFCLOCK) && (defined(CLOCK_HOPF_SERIAL))
1882498Sroberto
1982498Sroberto#include "ntpd.h"
2082498Sroberto#include "ntp_io.h"
2182498Sroberto#include "ntp_control.h"
2282498Sroberto#include "ntp_refclock.h"
2382498Sroberto#include "ntp_unixtime.h"
2482498Sroberto#include "ntp_stdlib.h"
2582498Sroberto
2682498Sroberto#if defined HAVE_SYS_MODEM_H
2782498Sroberto# include <sys/modem.h>
28182007Sroberto# ifndef __QNXNTO__
29182007Sroberto#  define TIOCMSET MCSETA
30182007Sroberto#  define TIOCMGET MCGETA
31182007Sroberto#  define TIOCM_RTS MRTS
32182007Sroberto# endif
3382498Sroberto#endif
3482498Sroberto
3582498Sroberto#ifdef HAVE_TERMIOS_H
3682498Sroberto# ifdef TERMIOS_NEEDS__SVID3
3782498Sroberto#  define _SVID3
3882498Sroberto# endif
3982498Sroberto# include <termios.h>
4082498Sroberto# ifdef TERMIOS_NEEDS__SVID3
4182498Sroberto#  undef _SVID3
4282498Sroberto# endif
4382498Sroberto#endif
4482498Sroberto
4582498Sroberto#ifdef HAVE_SYS_IOCTL_H
4682498Sroberto# include <sys/ioctl.h>
4782498Sroberto#endif
4882498Sroberto
49200576Sroberto#ifdef SYS_WINNT
50200576Srobertoextern int async_write(int, const void *, unsigned int);
51200576Sroberto#undef write
52200576Sroberto#define write(fd, data, octets)	async_write(fd, data, octets)
53200576Sroberto#endif
54200576Sroberto
5582498Sroberto/*
5682498Sroberto * clock definitions
5782498Sroberto */
5882498Sroberto#define	DESCRIPTION	"hopf Elektronik serial clock" /* Long name */
5982498Sroberto#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
6082498Sroberto#define	REFID		"hopf\0"	/* reference ID */
6182498Sroberto/*
6282498Sroberto * I/O definitions
6382498Sroberto */
6482498Sroberto#define	DEVICE		"/dev/hopfclock%d" 	/* device name and unit */
6582498Sroberto#define	SPEED232	B9600		    	/* uart speed (9600 baud) */
6682498Sroberto
6782498Sroberto
6882498Sroberto#define STX 0x02
6982498Sroberto#define ETX 0x03
7082498Sroberto#define CR  0x0c
7182498Sroberto#define LF  0x0a
7282498Sroberto
7382498Sroberto/* parse states */
7482498Sroberto#define REC_QUEUE_EMPTY       0
7582498Sroberto#define REC_QUEUE_FULL        1
7682498Sroberto
7782498Sroberto#define	HOPF_OPMODE	0x0C	/* operation mode mask */
7882498Sroberto#define HOPF_INVALID	0x00	/* no time code available */
7982498Sroberto#define HOPF_INTERNAL	0x04	/* internal clock */
8082498Sroberto#define HOPF_RADIO	0x08	/* radio clock */
8182498Sroberto#define HOPF_RADIOHP	0x0C	/* high precision radio clock */
8282498Sroberto
8382498Sroberto/*
8482498Sroberto * hopfclock unit control structure.
8582498Sroberto */
8682498Srobertostruct hopfclock_unit {
8782498Sroberto	l_fp	laststamp;	/* last receive timestamp */
8882498Sroberto	short	unit;		/* NTP refclock unit number */
8982498Sroberto	u_long	polled;		/* flag to detect noreplies */
9082498Sroberto	char	leap_status;	/* leap second flag */
9182498Sroberto	int	rpt_next;
9282498Sroberto};
9382498Sroberto
9482498Sroberto/*
9582498Sroberto * Function prototypes
9682498Sroberto */
9782498Sroberto
98290001Sglebiusstatic	int	hopfserial_start	(int, struct peer *);
99290001Sglebiusstatic	void	hopfserial_shutdown	(int, struct peer *);
100290001Sglebiusstatic	void	hopfserial_receive	(struct recvbuf *);
101290001Sglebiusstatic	void	hopfserial_poll		(int, struct peer *);
102290001Sglebius/* static  void hopfserial_io		(struct recvbuf *); */
10382498Sroberto/*
10482498Sroberto * Transfer vector
10582498Sroberto */
10682498Srobertostruct refclock refclock_hopfser = {
10782498Sroberto	hopfserial_start,	/* start up driver */
10882498Sroberto	hopfserial_shutdown,	/* shut down driver */
10982498Sroberto	hopfserial_poll,	/* transmit poll message */
11082498Sroberto	noentry,		/* not used  */
11182498Sroberto	noentry,		/* initialize driver (not used) */
11282498Sroberto	noentry,		/* not used */
11382498Sroberto	NOFLAGS			/* not used */
11482498Sroberto};
11582498Sroberto
11682498Sroberto/*
11782498Sroberto * hopfserial_start - open the devices and initialize data for processing
11882498Sroberto */
11982498Srobertostatic int
12082498Srobertohopfserial_start (
12182498Sroberto	int unit,
12282498Sroberto	struct peer *peer
12382498Sroberto	)
12482498Sroberto{
12582498Sroberto	register struct hopfclock_unit *up;
12682498Sroberto	struct refclockproc *pp;
12782498Sroberto	int fd;
12882498Sroberto	char gpsdev[20];
12982498Sroberto
130290001Sglebius	snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
131290001Sglebius
13282498Sroberto	/* LDISC_STD, LDISC_RAW
13382498Sroberto	 * Open serial port. Use CLK line discipline, if available.
13482498Sroberto	 */
13582498Sroberto	fd = refclock_open(gpsdev, SPEED232, LDISC_CLK);
13682498Sroberto	if (fd <= 0) {
13782498Sroberto#ifdef DEBUG
13882498Sroberto		printf("hopfSerialClock(%d) start: open %s failed\n", unit, gpsdev);
13982498Sroberto#endif
14082498Sroberto		return 0;
14182498Sroberto	}
14282498Sroberto
14382498Sroberto	msyslog(LOG_NOTICE, "hopfSerialClock(%d) fd: %d dev: %s", unit, fd,
14482498Sroberto		gpsdev);
14582498Sroberto
14682498Sroberto	/*
14782498Sroberto	 * Allocate and initialize unit structure
14882498Sroberto	 */
149290001Sglebius	up = emalloc_zero(sizeof(*up));
15082498Sroberto	pp = peer->procptr;
151290001Sglebius	pp->unitptr = up;
15282498Sroberto	pp->io.clock_recv = hopfserial_receive;
153290001Sglebius	pp->io.srcclock = peer;
15482498Sroberto	pp->io.datalen = 0;
15582498Sroberto	pp->io.fd = fd;
15682498Sroberto	if (!io_addclock(&pp->io)) {
15782498Sroberto#ifdef DEBUG
158290001Sglebius		printf("hopfSerialClock(%d) io_addclock\n", unit);
15982498Sroberto#endif
160290001Sglebius		close(fd);
161290001Sglebius		pp->io.fd = -1;
16282498Sroberto		free(up);
163290001Sglebius		pp->unitptr = NULL;
16482498Sroberto		return (0);
16582498Sroberto	}
16682498Sroberto
16782498Sroberto	/*
16882498Sroberto	 * Initialize miscellaneous variables
16982498Sroberto	 */
17082498Sroberto	pp->clockdesc = DESCRIPTION;
17182498Sroberto	peer->precision = PRECISION;
17282498Sroberto	memcpy((char *)&pp->refid, REFID, 4);
17382498Sroberto
17482498Sroberto	up->leap_status = 0;
17582498Sroberto	up->unit = (short) unit;
17682498Sroberto
17782498Sroberto	return (1);
17882498Sroberto}
17982498Sroberto
18082498Sroberto
18182498Sroberto/*
18282498Sroberto * hopfserial_shutdown - shut down the clock
18382498Sroberto */
18482498Srobertostatic void
18582498Srobertohopfserial_shutdown (
18682498Sroberto	int unit,
18782498Sroberto	struct peer *peer
18882498Sroberto	)
18982498Sroberto{
19082498Sroberto	register struct hopfclock_unit *up;
19182498Sroberto	struct refclockproc *pp;
19282498Sroberto
19382498Sroberto	pp = peer->procptr;
194290001Sglebius	up = pp->unitptr;
195290001Sglebius
196290001Sglebius	if (-1 != pp->io.fd)
197290001Sglebius		io_closeclock(&pp->io);
198290001Sglebius	if (NULL != up)
199290001Sglebius		free(up);
20082498Sroberto}
20182498Sroberto
20282498Sroberto
20382498Sroberto
20482498Sroberto/*
20582498Sroberto * hopfserial_receive - receive data from the serial interface
20682498Sroberto */
20782498Sroberto
20882498Srobertostatic void
20982498Srobertohopfserial_receive (
21082498Sroberto	struct recvbuf *rbufp
21182498Sroberto	)
21282498Sroberto{
21382498Sroberto	struct hopfclock_unit *up;
21482498Sroberto	struct refclockproc *pp;
21582498Sroberto	struct peer *peer;
21682498Sroberto
217290001Sglebius	int	synch;	/* synchhronization indicator */
218290001Sglebius	int	DoW;	/* Day of Week */
21982498Sroberto
22082498Sroberto	int	day, month;	/* ddd conversion */
221290001Sglebius	int	converted;
22282498Sroberto
22382498Sroberto	/*
22482498Sroberto	 * Initialize pointers and read the timecode and timestamp.
22582498Sroberto	 */
226290001Sglebius	peer = rbufp->recv_peer;
22782498Sroberto	pp = peer->procptr;
228290001Sglebius	up = pp->unitptr;
22982498Sroberto
23082498Sroberto	if (up->rpt_next == 0 )
23182498Sroberto		return;
23282498Sroberto
23382498Sroberto	up->rpt_next = 0; /* wait until next poll interval occur */
23482498Sroberto
235290001Sglebius	pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode,
236290001Sglebius					      sizeof(pp->a_lastcode),
237290001Sglebius					      &pp->lastrec);
238290001Sglebius	if (pp->lencode == 0)
23982498Sroberto		return;
24082498Sroberto
241290001Sglebius	converted = sscanf(pp->a_lastcode,
24282498Sroberto#if 1
24382498Sroberto	       "%1x%1x%2d%2d%2d%2d%2d%2d",   /* ...cr,lf */
24482498Sroberto#else
24582498Sroberto	       "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */
24682498Sroberto#endif
247132451Sroberto	       &synch,
24882498Sroberto	       &DoW,
24982498Sroberto	       &pp->hour,
25082498Sroberto	       &pp->minute,
25182498Sroberto	       &pp->second,
25282498Sroberto	       &day,
25382498Sroberto	       &month,
25482498Sroberto	       &pp->year);
25582498Sroberto
25682498Sroberto
25782498Sroberto	/*
25882498Sroberto	  Validate received values at least enough to prevent internal
25982498Sroberto	  array-bounds problems, etc.
26082498Sroberto	*/
261290001Sglebius	if ((8 != converted) || (pp->hour < 0) || (pp->hour > 23) ||
262290001Sglebius	   (pp->minute < 0) || (pp->minute > 59) || (pp->second < 0) ||
263290001Sglebius	   (pp->second > 60) /*Allow for leap seconds.*/ ||
26482498Sroberto	   (day < 1) || (day > 31) ||
26582498Sroberto	   (month < 1) || (month > 12) ||
26682498Sroberto	   (pp->year < 0) || (pp->year > 99)) {
26782498Sroberto		/* Data out of range. */
26882498Sroberto		refclock_report(peer, CEVNT_BADREPLY);
26982498Sroberto		return;
27082498Sroberto	}
27182498Sroberto	/*
27282498Sroberto	  some preparations
27382498Sroberto	*/
27482498Sroberto	pp->day    = ymd2yd(pp->year,month,day);
27582498Sroberto	pp->leap=0;
27682498Sroberto
27782498Sroberto	/* Year-2000 check! */
27882498Sroberto	/* wrap 2-digit date into 4-digit */
27982498Sroberto
28082498Sroberto	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* < 98 */
28182498Sroberto	pp->year += 1900;
28282498Sroberto
28382498Sroberto	/* preparation for timecode ntpq rl command ! */
28482498Sroberto
28582498Sroberto#if 0
286290001Sglebius	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
28782498Sroberto		 "STATUS: %1X%1X, DATE: %02d.%02d.%04d  TIME: %02d:%02d:%02d",
288132451Sroberto		 synch,
28982498Sroberto		 DoW,
29082498Sroberto		 day,
29182498Sroberto		 month,
29282498Sroberto		 pp->year,
29382498Sroberto		 pp->hour,
29482498Sroberto		 pp->minute,
29582498Sroberto		 pp->second);
29682498Sroberto
29782498Sroberto	pp->lencode = strlen(pp->a_lastcode);
298132451Sroberto	if ((synch && 0xc) == 0 ){  /* time ok? */
29982498Sroberto		refclock_report(peer, CEVNT_BADTIME);
30082498Sroberto		pp->leap = LEAP_NOTINSYNC;
30182498Sroberto		return;
30282498Sroberto	}
30382498Sroberto#endif
30482498Sroberto	/*
30582498Sroberto	 * If clock has no valid status then report error and exit
30682498Sroberto	 */
307132451Sroberto	if ((synch & HOPF_OPMODE) == HOPF_INVALID ){  /* time ok? */
30882498Sroberto		refclock_report(peer, CEVNT_BADTIME);
30982498Sroberto		pp->leap = LEAP_NOTINSYNC;
31082498Sroberto		return;
31182498Sroberto	}
31282498Sroberto
31382498Sroberto	/*
31482498Sroberto	 * Test if time is running on internal quarz
31582498Sroberto	 * if CLK_FLAG1 is set, sychronize even if no radio operation
31682498Sroberto	 */
31782498Sroberto
318132451Sroberto	if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){
31982498Sroberto		if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
32082498Sroberto			refclock_report(peer, CEVNT_BADTIME);
32182498Sroberto			pp->leap = LEAP_NOTINSYNC;
32282498Sroberto			return;
32382498Sroberto		}
32482498Sroberto	}
32582498Sroberto
32682498Sroberto
32782498Sroberto	if (!refclock_process(pp)) {
32882498Sroberto		refclock_report(peer, CEVNT_BADTIME);
32982498Sroberto		return;
33082498Sroberto	}
331132451Sroberto	pp->lastref = pp->lastrec;
33282498Sroberto	refclock_receive(peer);
33382498Sroberto
33482498Sroberto#if 0
335132451Sroberto	msyslog(LOG_ERR, " D:%x  D:%d D:%d",synch,pp->minute,pp->second);
33682498Sroberto#endif
33782498Sroberto
33882498Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
33982498Sroberto
34082498Sroberto	return;
34182498Sroberto}
34282498Sroberto
34382498Sroberto
34482498Sroberto/*
34582498Sroberto * hopfserial_poll - called by the transmit procedure
34682498Sroberto *
34782498Sroberto */
34882498Srobertostatic void
34982498Srobertohopfserial_poll (
35082498Sroberto	int unit,
35182498Sroberto	struct peer *peer
35282498Sroberto	)
35382498Sroberto{
35482498Sroberto	register struct hopfclock_unit *up;
35582498Sroberto	struct refclockproc *pp;
35682498Sroberto	pp = peer->procptr;
35782498Sroberto
358290001Sglebius	up = pp->unitptr;
35982498Sroberto
36082498Sroberto	pp->polls++;
36182498Sroberto	up->rpt_next = 1;
36282498Sroberto
36382498Sroberto#if 0
36482498Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
36582498Sroberto#endif
36682498Sroberto
36782498Sroberto	return;
36882498Sroberto}
36982498Sroberto
37082498Sroberto#else
37182498Srobertoint refclock_hopfser_bs;
37282498Sroberto#endif /* REFCLOCK */
373