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
9882498Srobertostatic	int	hopfserial_start	P((int, struct peer *));
9982498Srobertostatic	void	hopfserial_shutdown	P((int, struct peer *));
10082498Srobertostatic	void	hopfserial_receive	P((struct recvbuf *));
10182498Srobertostatic	void	hopfserial_poll		P((int, struct peer *));
10282498Sroberto/* static  void hopfserial_io		P((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
13082498Sroberto#ifdef SYS_WINNT
13182498Sroberto	(void) sprintf(gpsdev, "COM%d:", unit);
13282498Sroberto#else
13382498Sroberto	(void) sprintf(gpsdev, DEVICE, unit);
13482498Sroberto#endif
13582498Sroberto	/* LDISC_STD, LDISC_RAW
13682498Sroberto	 * Open serial port. Use CLK line discipline, if available.
13782498Sroberto	 */
13882498Sroberto	fd = refclock_open(gpsdev, SPEED232, LDISC_CLK);
13982498Sroberto	if (fd <= 0) {
14082498Sroberto#ifdef DEBUG
14182498Sroberto		printf("hopfSerialClock(%d) start: open %s failed\n", unit, gpsdev);
14282498Sroberto#endif
14382498Sroberto		return 0;
14482498Sroberto	}
14582498Sroberto
14682498Sroberto	msyslog(LOG_NOTICE, "hopfSerialClock(%d) fd: %d dev: %s", unit, fd,
14782498Sroberto		gpsdev);
14882498Sroberto
14982498Sroberto	/*
15082498Sroberto	 * Allocate and initialize unit structure
15182498Sroberto	 */
15282498Sroberto	up = (struct hopfclock_unit *) emalloc(sizeof(struct hopfclock_unit));
15382498Sroberto
15482498Sroberto	if (!(up)) {
15582498Sroberto                msyslog(LOG_ERR, "hopfSerialClock(%d) emalloc: %m",unit);
15682498Sroberto#ifdef DEBUG
15782498Sroberto                printf("hopfSerialClock(%d) emalloc\n",unit);
15882498Sroberto#endif
15982498Sroberto		(void) close(fd);
16082498Sroberto		return (0);
16182498Sroberto	}
16282498Sroberto
16382498Sroberto	memset((char *)up, 0, sizeof(struct hopfclock_unit));
16482498Sroberto	pp = peer->procptr;
16582498Sroberto	pp->unitptr = (caddr_t)up;
16682498Sroberto	pp->io.clock_recv = hopfserial_receive;
16782498Sroberto	pp->io.srcclock = (caddr_t)peer;
16882498Sroberto	pp->io.datalen = 0;
16982498Sroberto	pp->io.fd = fd;
17082498Sroberto	if (!io_addclock(&pp->io)) {
17182498Sroberto#ifdef DEBUG
17282498Sroberto                printf("hopfSerialClock(%d) io_addclock\n",unit);
17382498Sroberto#endif
17482498Sroberto		(void) close(fd);
17582498Sroberto		free(up);
17682498Sroberto		return (0);
17782498Sroberto	}
17882498Sroberto
17982498Sroberto	/*
18082498Sroberto	 * Initialize miscellaneous variables
18182498Sroberto	 */
18282498Sroberto	pp->clockdesc = DESCRIPTION;
18382498Sroberto	peer->precision = PRECISION;
18482498Sroberto	peer->burst = NSTAGE;
18582498Sroberto	memcpy((char *)&pp->refid, REFID, 4);
18682498Sroberto
18782498Sroberto	up->leap_status = 0;
18882498Sroberto	up->unit = (short) unit;
18982498Sroberto
19082498Sroberto	return (1);
19182498Sroberto}
19282498Sroberto
19382498Sroberto
19482498Sroberto/*
19582498Sroberto * hopfserial_shutdown - shut down the clock
19682498Sroberto */
19782498Srobertostatic void
19882498Srobertohopfserial_shutdown (
19982498Sroberto	int unit,
20082498Sroberto	struct peer *peer
20182498Sroberto	)
20282498Sroberto{
20382498Sroberto	register struct hopfclock_unit *up;
20482498Sroberto	struct refclockproc *pp;
20582498Sroberto
20682498Sroberto	pp = peer->procptr;
20782498Sroberto	up = (struct hopfclock_unit *)pp->unitptr;
20882498Sroberto	io_closeclock(&pp->io);
20982498Sroberto	free(up);
21082498Sroberto}
21182498Sroberto
21282498Sroberto
21382498Sroberto
21482498Sroberto/*
21582498Sroberto * hopfserial_receive - receive data from the serial interface
21682498Sroberto */
21782498Sroberto
21882498Srobertostatic void
21982498Srobertohopfserial_receive (
22082498Sroberto	struct recvbuf *rbufp
22182498Sroberto	)
22282498Sroberto{
22382498Sroberto	struct hopfclock_unit *up;
22482498Sroberto	struct refclockproc *pp;
22582498Sroberto	struct peer *peer;
22682498Sroberto
227132451Sroberto	int		synch;	/* synchhronization indicator */
22882498Sroberto	int		DoW;	/* Dow */
22982498Sroberto
23082498Sroberto	int	day, month;	/* ddd conversion */
23182498Sroberto
23282498Sroberto	/*
23382498Sroberto	 * Initialize pointers and read the timecode and timestamp.
23482498Sroberto	 */
23582498Sroberto	peer = (struct peer *)rbufp->recv_srcclock;
23682498Sroberto	pp = peer->procptr;
23782498Sroberto	up = (struct hopfclock_unit *)pp->unitptr;
23882498Sroberto
23982498Sroberto	if (up->rpt_next == 0 )
24082498Sroberto		return;
24182498Sroberto
24282498Sroberto
24382498Sroberto	up->rpt_next = 0; /* wait until next poll interval occur */
24482498Sroberto
245132451Sroberto	pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
24682498Sroberto
24782498Sroberto	if (pp->lencode  == 0)
24882498Sroberto		return;
24982498Sroberto
25082498Sroberto	sscanf(pp->a_lastcode,
25182498Sroberto#if 1
25282498Sroberto	       "%1x%1x%2d%2d%2d%2d%2d%2d",   /* ...cr,lf */
25382498Sroberto#else
25482498Sroberto	       "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */
25582498Sroberto#endif
256132451Sroberto	       &synch,
25782498Sroberto	       &DoW,
25882498Sroberto	       &pp->hour,
25982498Sroberto	       &pp->minute,
26082498Sroberto	       &pp->second,
26182498Sroberto	       &day,
26282498Sroberto	       &month,
26382498Sroberto	       &pp->year);
26482498Sroberto
26582498Sroberto
26682498Sroberto	/*
26782498Sroberto	  Validate received values at least enough to prevent internal
26882498Sroberto	  array-bounds problems, etc.
26982498Sroberto	*/
27082498Sroberto	if((pp->hour < 0) || (pp->hour > 23) ||
27182498Sroberto	   (pp->minute < 0) || (pp->minute > 59) ||
27282498Sroberto	   (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
27382498Sroberto	   (day < 1) || (day > 31) ||
27482498Sroberto	   (month < 1) || (month > 12) ||
27582498Sroberto	   (pp->year < 0) || (pp->year > 99)) {
27682498Sroberto		/* Data out of range. */
27782498Sroberto		refclock_report(peer, CEVNT_BADREPLY);
27882498Sroberto		return;
27982498Sroberto	}
28082498Sroberto	/*
28182498Sroberto	  some preparations
28282498Sroberto	*/
28382498Sroberto	pp->day    = ymd2yd(pp->year,month,day);
28482498Sroberto	pp->leap=0;
28582498Sroberto
28682498Sroberto	/* Year-2000 check! */
28782498Sroberto	/* wrap 2-digit date into 4-digit */
28882498Sroberto
28982498Sroberto	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* < 98 */
29082498Sroberto	pp->year += 1900;
29182498Sroberto
29282498Sroberto	/* preparation for timecode ntpq rl command ! */
29382498Sroberto
29482498Sroberto#if 0
29582498Sroberto	wsprintf(pp->a_lastcode,
29682498Sroberto		 "STATUS: %1X%1X, DATE: %02d.%02d.%04d  TIME: %02d:%02d:%02d",
297132451Sroberto		 synch,
29882498Sroberto		 DoW,
29982498Sroberto		 day,
30082498Sroberto		 month,
30182498Sroberto		 pp->year,
30282498Sroberto		 pp->hour,
30382498Sroberto		 pp->minute,
30482498Sroberto		 pp->second);
30582498Sroberto
30682498Sroberto	pp->lencode = strlen(pp->a_lastcode);
307132451Sroberto	if ((synch && 0xc) == 0 ){  /* time ok? */
30882498Sroberto		refclock_report(peer, CEVNT_BADTIME);
30982498Sroberto		pp->leap = LEAP_NOTINSYNC;
31082498Sroberto		return;
31182498Sroberto	}
31282498Sroberto#endif
31382498Sroberto	/*
31482498Sroberto	 * If clock has no valid status then report error and exit
31582498Sroberto	 */
316132451Sroberto	if ((synch & HOPF_OPMODE) == HOPF_INVALID ){  /* time ok? */
31782498Sroberto		refclock_report(peer, CEVNT_BADTIME);
31882498Sroberto		pp->leap = LEAP_NOTINSYNC;
31982498Sroberto		return;
32082498Sroberto	}
32182498Sroberto
32282498Sroberto	/*
32382498Sroberto	 * Test if time is running on internal quarz
32482498Sroberto	 * if CLK_FLAG1 is set, sychronize even if no radio operation
32582498Sroberto	 */
32682498Sroberto
327132451Sroberto	if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){
32882498Sroberto		if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
32982498Sroberto			refclock_report(peer, CEVNT_BADTIME);
33082498Sroberto			pp->leap = LEAP_NOTINSYNC;
33182498Sroberto			return;
33282498Sroberto		}
33382498Sroberto	}
33482498Sroberto
33582498Sroberto
33682498Sroberto	if (!refclock_process(pp)) {
33782498Sroberto		refclock_report(peer, CEVNT_BADTIME);
33882498Sroberto		return;
33982498Sroberto	}
340132451Sroberto	pp->lastref = pp->lastrec;
34182498Sroberto	refclock_receive(peer);
34282498Sroberto
34382498Sroberto#if 0
344132451Sroberto	msyslog(LOG_ERR, " D:%x  D:%d D:%d",synch,pp->minute,pp->second);
34582498Sroberto#endif
34682498Sroberto
34782498Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
34882498Sroberto
34982498Sroberto	return;
35082498Sroberto}
35182498Sroberto
35282498Sroberto
35382498Sroberto/*
35482498Sroberto * hopfserial_poll - called by the transmit procedure
35582498Sroberto *
35682498Sroberto */
35782498Srobertostatic void
35882498Srobertohopfserial_poll (
35982498Sroberto	int unit,
36082498Sroberto	struct peer *peer
36182498Sroberto	)
36282498Sroberto{
36382498Sroberto	register struct hopfclock_unit *up;
36482498Sroberto	struct refclockproc *pp;
36582498Sroberto	pp = peer->procptr;
36682498Sroberto
36782498Sroberto	up = (struct hopfclock_unit *)pp->unitptr;
36882498Sroberto
36982498Sroberto	pp->polls++;
37082498Sroberto	up->rpt_next = 1;
37182498Sroberto
37282498Sroberto#if 0
37382498Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
37482498Sroberto#endif
37582498Sroberto
37682498Sroberto	return;
37782498Sroberto}
37882498Sroberto
37982498Sroberto#else
38082498Srobertoint refclock_hopfser_bs;
38182498Sroberto#endif /* REFCLOCK */
382