xref: /freebsd/contrib/ntp/ntpd/refclock_hopfser.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1224ba2bdSOllivier Robert /*
2224ba2bdSOllivier Robert  *
3224ba2bdSOllivier Robert  * refclock_hopfser.c
4224ba2bdSOllivier Robert  * - clock driver for hopf serial boards (GPS or DCF77)
5224ba2bdSOllivier Robert  *
6224ba2bdSOllivier Robert  * Date: 30.03.2000 Revision: 01.10
7224ba2bdSOllivier Robert  *
8224ba2bdSOllivier Robert  * latest source and further information can be found at:
9224ba2bdSOllivier Robert  * http://www.ATLSoft.de/ntp
10224ba2bdSOllivier Robert  *
11224ba2bdSOllivier Robert  */
12224ba2bdSOllivier Robert 
13224ba2bdSOllivier Robert #ifdef HAVE_CONFIG_H
14224ba2bdSOllivier Robert # include "config.h"
15224ba2bdSOllivier Robert #endif
16224ba2bdSOllivier Robert 
17224ba2bdSOllivier Robert #if defined(REFCLOCK) && (defined(CLOCK_HOPF_SERIAL))
18224ba2bdSOllivier Robert 
19224ba2bdSOllivier Robert #include "ntpd.h"
20224ba2bdSOllivier Robert #include "ntp_io.h"
21224ba2bdSOllivier Robert #include "ntp_control.h"
22224ba2bdSOllivier Robert #include "ntp_refclock.h"
23224ba2bdSOllivier Robert #include "ntp_unixtime.h"
24224ba2bdSOllivier Robert #include "ntp_stdlib.h"
25224ba2bdSOllivier Robert 
26224ba2bdSOllivier Robert #if defined HAVE_SYS_MODEM_H
27224ba2bdSOllivier Robert # include <sys/modem.h>
28ea906c41SOllivier Robert # ifndef __QNXNTO__
29224ba2bdSOllivier Robert #  define TIOCMSET MCSETA
30224ba2bdSOllivier Robert #  define TIOCMGET MCGETA
31224ba2bdSOllivier Robert #  define TIOCM_RTS MRTS
32224ba2bdSOllivier Robert # endif
33ea906c41SOllivier Robert #endif
34224ba2bdSOllivier Robert 
35224ba2bdSOllivier Robert #ifdef HAVE_TERMIOS_H
36224ba2bdSOllivier Robert # ifdef TERMIOS_NEEDS__SVID3
37224ba2bdSOllivier Robert #  define _SVID3
38224ba2bdSOllivier Robert # endif
39224ba2bdSOllivier Robert # include <termios.h>
40224ba2bdSOllivier Robert # ifdef TERMIOS_NEEDS__SVID3
41224ba2bdSOllivier Robert #  undef _SVID3
42224ba2bdSOllivier Robert # endif
43224ba2bdSOllivier Robert #endif
44224ba2bdSOllivier Robert 
45224ba2bdSOllivier Robert #ifdef HAVE_SYS_IOCTL_H
46224ba2bdSOllivier Robert # include <sys/ioctl.h>
47224ba2bdSOllivier Robert #endif
48224ba2bdSOllivier Robert 
49224ba2bdSOllivier Robert /*
50224ba2bdSOllivier Robert  * clock definitions
51224ba2bdSOllivier Robert  */
52224ba2bdSOllivier Robert #define	DESCRIPTION	"hopf Elektronik serial clock" /* Long name */
53224ba2bdSOllivier Robert #define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
54224ba2bdSOllivier Robert #define	REFID		"hopf\0"	/* reference ID */
55224ba2bdSOllivier Robert /*
56224ba2bdSOllivier Robert  * I/O definitions
57224ba2bdSOllivier Robert  */
58224ba2bdSOllivier Robert #define	DEVICE		"/dev/hopfclock%d" 	/* device name and unit */
59224ba2bdSOllivier Robert #define	SPEED232	B9600		    	/* uart speed (9600 baud) */
60224ba2bdSOllivier Robert 
61224ba2bdSOllivier Robert 
62224ba2bdSOllivier Robert #define STX 0x02
63224ba2bdSOllivier Robert #define ETX 0x03
64224ba2bdSOllivier Robert #define CR  0x0c
65224ba2bdSOllivier Robert #define LF  0x0a
66224ba2bdSOllivier Robert 
67224ba2bdSOllivier Robert /* parse states */
68224ba2bdSOllivier Robert #define REC_QUEUE_EMPTY       0
69224ba2bdSOllivier Robert #define REC_QUEUE_FULL        1
70224ba2bdSOllivier Robert 
71224ba2bdSOllivier Robert #define	HOPF_OPMODE	0x0C	/* operation mode mask */
72224ba2bdSOllivier Robert #define HOPF_INVALID	0x00	/* no time code available */
73224ba2bdSOllivier Robert #define HOPF_INTERNAL	0x04	/* internal clock */
74224ba2bdSOllivier Robert #define HOPF_RADIO	0x08	/* radio clock */
75224ba2bdSOllivier Robert #define HOPF_RADIOHP	0x0C	/* high precision radio clock */
76224ba2bdSOllivier Robert 
77224ba2bdSOllivier Robert /*
78224ba2bdSOllivier Robert  * hopfclock unit control structure.
79224ba2bdSOllivier Robert  */
80224ba2bdSOllivier Robert struct hopfclock_unit {
81224ba2bdSOllivier Robert 	l_fp	laststamp;	/* last receive timestamp */
82224ba2bdSOllivier Robert 	short	unit;		/* NTP refclock unit number */
83224ba2bdSOllivier Robert 	u_long	polled;		/* flag to detect noreplies */
84224ba2bdSOllivier Robert 	char	leap_status;	/* leap second flag */
85224ba2bdSOllivier Robert 	int	rpt_next;
86224ba2bdSOllivier Robert };
87224ba2bdSOllivier Robert 
88224ba2bdSOllivier Robert /*
89224ba2bdSOllivier Robert  * Function prototypes
90224ba2bdSOllivier Robert  */
91224ba2bdSOllivier Robert 
922b15cb3dSCy Schubert static	int	hopfserial_start	(int, struct peer *);
932b15cb3dSCy Schubert static	void	hopfserial_shutdown	(int, struct peer *);
942b15cb3dSCy Schubert static	void	hopfserial_receive	(struct recvbuf *);
952b15cb3dSCy Schubert static	void	hopfserial_poll		(int, struct peer *);
962b15cb3dSCy Schubert /* static  void hopfserial_io		(struct recvbuf *); */
97224ba2bdSOllivier Robert /*
98224ba2bdSOllivier Robert  * Transfer vector
99224ba2bdSOllivier Robert  */
100224ba2bdSOllivier Robert struct refclock refclock_hopfser = {
101224ba2bdSOllivier Robert 	hopfserial_start,	/* start up driver */
102224ba2bdSOllivier Robert 	hopfserial_shutdown,	/* shut down driver */
103224ba2bdSOllivier Robert 	hopfserial_poll,	/* transmit poll message */
104224ba2bdSOllivier Robert 	noentry,		/* not used  */
105224ba2bdSOllivier Robert 	noentry,		/* initialize driver (not used) */
106224ba2bdSOllivier Robert 	noentry,		/* not used */
107224ba2bdSOllivier Robert 	NOFLAGS			/* not used */
108224ba2bdSOllivier Robert };
109224ba2bdSOllivier Robert 
110224ba2bdSOllivier Robert /*
111224ba2bdSOllivier Robert  * hopfserial_start - open the devices and initialize data for processing
112224ba2bdSOllivier Robert  */
113224ba2bdSOllivier Robert static int
114224ba2bdSOllivier Robert hopfserial_start (
115224ba2bdSOllivier Robert 	int unit,
116224ba2bdSOllivier Robert 	struct peer *peer
117224ba2bdSOllivier Robert 	)
118224ba2bdSOllivier Robert {
119224ba2bdSOllivier Robert 	register struct hopfclock_unit *up;
120224ba2bdSOllivier Robert 	struct refclockproc *pp;
121224ba2bdSOllivier Robert 	int fd;
122224ba2bdSOllivier Robert 	char gpsdev[20];
123224ba2bdSOllivier Robert 
1242b15cb3dSCy Schubert 	snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
1252b15cb3dSCy Schubert 
126224ba2bdSOllivier Robert 	/* LDISC_STD, LDISC_RAW
127224ba2bdSOllivier Robert 	 * Open serial port. Use CLK line discipline, if available.
128224ba2bdSOllivier Robert 	 */
129a466cc55SCy Schubert 	fd = refclock_open(&peer->srcadr, gpsdev, SPEED232, LDISC_CLK);
130224ba2bdSOllivier Robert 	if (fd <= 0) {
131224ba2bdSOllivier Robert #ifdef DEBUG
132224ba2bdSOllivier Robert 		printf("hopfSerialClock(%d) start: open %s failed\n", unit, gpsdev);
133224ba2bdSOllivier Robert #endif
134224ba2bdSOllivier Robert 		return 0;
135224ba2bdSOllivier Robert 	}
136224ba2bdSOllivier Robert 
137224ba2bdSOllivier Robert 	msyslog(LOG_NOTICE, "hopfSerialClock(%d) fd: %d dev: %s", unit, fd,
138224ba2bdSOllivier Robert 		gpsdev);
139224ba2bdSOllivier Robert 
140224ba2bdSOllivier Robert 	/*
141224ba2bdSOllivier Robert 	 * Allocate and initialize unit structure
142224ba2bdSOllivier Robert 	 */
1432b15cb3dSCy Schubert 	up = emalloc_zero(sizeof(*up));
144224ba2bdSOllivier Robert 	pp = peer->procptr;
1452b15cb3dSCy Schubert 	pp->unitptr = up;
146224ba2bdSOllivier Robert 	pp->io.clock_recv = hopfserial_receive;
1472b15cb3dSCy Schubert 	pp->io.srcclock = peer;
148224ba2bdSOllivier Robert 	pp->io.datalen = 0;
149224ba2bdSOllivier Robert 	pp->io.fd = fd;
150224ba2bdSOllivier Robert 	if (!io_addclock(&pp->io)) {
151224ba2bdSOllivier Robert #ifdef DEBUG
152224ba2bdSOllivier Robert 		printf("hopfSerialClock(%d) io_addclock\n", unit);
153224ba2bdSOllivier Robert #endif
1542b15cb3dSCy Schubert 		close(fd);
1552b15cb3dSCy Schubert 		pp->io.fd = -1;
156224ba2bdSOllivier Robert 		free(up);
1572b15cb3dSCy Schubert 		pp->unitptr = NULL;
158224ba2bdSOllivier Robert 		return (0);
159224ba2bdSOllivier Robert 	}
160224ba2bdSOllivier Robert 
161224ba2bdSOllivier Robert 	/*
162224ba2bdSOllivier Robert 	 * Initialize miscellaneous variables
163224ba2bdSOllivier Robert 	 */
164224ba2bdSOllivier Robert 	pp->clockdesc = DESCRIPTION;
165224ba2bdSOllivier Robert 	peer->precision = PRECISION;
166224ba2bdSOllivier Robert 	memcpy((char *)&pp->refid, REFID, 4);
167224ba2bdSOllivier Robert 
168224ba2bdSOllivier Robert 	up->leap_status = 0;
169224ba2bdSOllivier Robert 	up->unit = (short) unit;
170224ba2bdSOllivier Robert 
171224ba2bdSOllivier Robert 	return (1);
172224ba2bdSOllivier Robert }
173224ba2bdSOllivier Robert 
174224ba2bdSOllivier Robert 
175224ba2bdSOllivier Robert /*
176224ba2bdSOllivier Robert  * hopfserial_shutdown - shut down the clock
177224ba2bdSOllivier Robert  */
178224ba2bdSOllivier Robert static void
179224ba2bdSOllivier Robert hopfserial_shutdown (
180224ba2bdSOllivier Robert 	int unit,
181224ba2bdSOllivier Robert 	struct peer *peer
182224ba2bdSOllivier Robert 	)
183224ba2bdSOllivier Robert {
184224ba2bdSOllivier Robert 	register struct hopfclock_unit *up;
185224ba2bdSOllivier Robert 	struct refclockproc *pp;
186224ba2bdSOllivier Robert 
187224ba2bdSOllivier Robert 	pp = peer->procptr;
1882b15cb3dSCy Schubert 	up = pp->unitptr;
1892b15cb3dSCy Schubert 
1902b15cb3dSCy Schubert 	if (-1 != pp->io.fd)
191224ba2bdSOllivier Robert 		io_closeclock(&pp->io);
1922b15cb3dSCy Schubert 	if (NULL != up)
193224ba2bdSOllivier Robert 		free(up);
194224ba2bdSOllivier Robert }
195224ba2bdSOllivier Robert 
196224ba2bdSOllivier Robert 
197224ba2bdSOllivier Robert 
198224ba2bdSOllivier Robert /*
199224ba2bdSOllivier Robert  * hopfserial_receive - receive data from the serial interface
200224ba2bdSOllivier Robert  */
201224ba2bdSOllivier Robert 
202224ba2bdSOllivier Robert static void
203224ba2bdSOllivier Robert hopfserial_receive (
204224ba2bdSOllivier Robert 	struct recvbuf *rbufp
205224ba2bdSOllivier Robert 	)
206224ba2bdSOllivier Robert {
207224ba2bdSOllivier Robert 	struct hopfclock_unit *up;
208224ba2bdSOllivier Robert 	struct refclockproc *pp;
209224ba2bdSOllivier Robert 	struct peer *peer;
210224ba2bdSOllivier Robert 
2119c2daa00SOllivier Robert 	int	synch;	/* synchhronization indicator */
2122b15cb3dSCy Schubert 	int	DoW;	/* Day of Week */
213224ba2bdSOllivier Robert 
214224ba2bdSOllivier Robert 	int	day, month;	/* ddd conversion */
2152b15cb3dSCy Schubert 	int	converted;
216224ba2bdSOllivier Robert 
217224ba2bdSOllivier Robert 	/*
218224ba2bdSOllivier Robert 	 * Initialize pointers and read the timecode and timestamp.
219224ba2bdSOllivier Robert 	 */
2202b15cb3dSCy Schubert 	peer = rbufp->recv_peer;
221224ba2bdSOllivier Robert 	pp = peer->procptr;
2222b15cb3dSCy Schubert 	up = pp->unitptr;
223224ba2bdSOllivier Robert 
224224ba2bdSOllivier Robert 	if (up->rpt_next == 0 )
225224ba2bdSOllivier Robert 		return;
226224ba2bdSOllivier Robert 
227224ba2bdSOllivier Robert 	up->rpt_next = 0; /* wait until next poll interval occur */
228224ba2bdSOllivier Robert 
2292b15cb3dSCy Schubert 	pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode,
2302b15cb3dSCy Schubert 					      sizeof(pp->a_lastcode),
2312b15cb3dSCy Schubert 					      &pp->lastrec);
232224ba2bdSOllivier Robert 	if (pp->lencode == 0)
233224ba2bdSOllivier Robert 		return;
234224ba2bdSOllivier Robert 
2352b15cb3dSCy Schubert 	converted = sscanf(pp->a_lastcode,
236224ba2bdSOllivier Robert #if 1
237224ba2bdSOllivier Robert 	       "%1x%1x%2d%2d%2d%2d%2d%2d",   /* ...cr,lf */
238224ba2bdSOllivier Robert #else
239224ba2bdSOllivier Robert 	       "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */
240224ba2bdSOllivier Robert #endif
2419c2daa00SOllivier Robert 	       &synch,
242224ba2bdSOllivier Robert 	       &DoW,
243224ba2bdSOllivier Robert 	       &pp->hour,
244224ba2bdSOllivier Robert 	       &pp->minute,
245224ba2bdSOllivier Robert 	       &pp->second,
246224ba2bdSOllivier Robert 	       &day,
247224ba2bdSOllivier Robert 	       &month,
248224ba2bdSOllivier Robert 	       &pp->year);
249224ba2bdSOllivier Robert 
250224ba2bdSOllivier Robert 
251224ba2bdSOllivier Robert 	/*
252224ba2bdSOllivier Robert 	  Validate received values at least enough to prevent internal
253224ba2bdSOllivier Robert 	  array-bounds problems, etc.
254224ba2bdSOllivier Robert 	*/
2552b15cb3dSCy Schubert 	if ((8 != converted) || (pp->hour < 0) || (pp->hour > 23) ||
2562b15cb3dSCy Schubert 	   (pp->minute < 0) || (pp->minute > 59) || (pp->second < 0) ||
2572b15cb3dSCy Schubert 	   (pp->second > 60) /*Allow for leap seconds.*/ ||
258224ba2bdSOllivier Robert 	   (day < 1) || (day > 31) ||
259224ba2bdSOllivier Robert 	   (month < 1) || (month > 12) ||
260224ba2bdSOllivier Robert 	   (pp->year < 0) || (pp->year > 99)) {
261224ba2bdSOllivier Robert 		/* Data out of range. */
262224ba2bdSOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
263224ba2bdSOllivier Robert 		return;
264224ba2bdSOllivier Robert 	}
265224ba2bdSOllivier Robert 	/*
266224ba2bdSOllivier Robert 	  some preparations
267224ba2bdSOllivier Robert 	*/
268224ba2bdSOllivier Robert 	pp->day    = ymd2yd(pp->year,month,day);
269224ba2bdSOllivier Robert 	pp->leap=0;
270224ba2bdSOllivier Robert 
271224ba2bdSOllivier Robert 	/* Year-2000 check! */
272224ba2bdSOllivier Robert 	/* wrap 2-digit date into 4-digit */
273224ba2bdSOllivier Robert 
274224ba2bdSOllivier Robert 	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* < 98 */
275224ba2bdSOllivier Robert 	pp->year += 1900;
276224ba2bdSOllivier Robert 
277224ba2bdSOllivier Robert 	/* preparation for timecode ntpq rl command ! */
278224ba2bdSOllivier Robert 
279224ba2bdSOllivier Robert #if 0
2802b15cb3dSCy Schubert 	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
281224ba2bdSOllivier Robert 		 "STATUS: %1X%1X, DATE: %02d.%02d.%04d  TIME: %02d:%02d:%02d",
2829c2daa00SOllivier Robert 		 synch,
283224ba2bdSOllivier Robert 		 DoW,
284224ba2bdSOllivier Robert 		 day,
285224ba2bdSOllivier Robert 		 month,
286224ba2bdSOllivier Robert 		 pp->year,
287224ba2bdSOllivier Robert 		 pp->hour,
288224ba2bdSOllivier Robert 		 pp->minute,
289224ba2bdSOllivier Robert 		 pp->second);
290224ba2bdSOllivier Robert 
291224ba2bdSOllivier Robert 	pp->lencode = strlen(pp->a_lastcode);
2929c2daa00SOllivier Robert 	if ((synch && 0xc) == 0 ){  /* time ok? */
293224ba2bdSOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
294224ba2bdSOllivier Robert 		pp->leap = LEAP_NOTINSYNC;
295224ba2bdSOllivier Robert 		return;
296224ba2bdSOllivier Robert 	}
297224ba2bdSOllivier Robert #endif
298224ba2bdSOllivier Robert 	/*
299224ba2bdSOllivier Robert 	 * If clock has no valid status then report error and exit
300224ba2bdSOllivier Robert 	 */
3019c2daa00SOllivier Robert 	if ((synch & HOPF_OPMODE) == HOPF_INVALID ){  /* time ok? */
302224ba2bdSOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
303224ba2bdSOllivier Robert 		pp->leap = LEAP_NOTINSYNC;
304224ba2bdSOllivier Robert 		return;
305224ba2bdSOllivier Robert 	}
306224ba2bdSOllivier Robert 
307224ba2bdSOllivier Robert 	/*
308224ba2bdSOllivier Robert 	 * Test if time is running on internal quarz
309224ba2bdSOllivier Robert 	 * if CLK_FLAG1 is set, sychronize even if no radio operation
310224ba2bdSOllivier Robert 	 */
311224ba2bdSOllivier Robert 
3129c2daa00SOllivier Robert 	if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){
313224ba2bdSOllivier Robert 		if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
314224ba2bdSOllivier Robert 			refclock_report(peer, CEVNT_BADTIME);
315224ba2bdSOllivier Robert 			pp->leap = LEAP_NOTINSYNC;
316224ba2bdSOllivier Robert 			return;
317224ba2bdSOllivier Robert 		}
318224ba2bdSOllivier Robert 	}
319224ba2bdSOllivier Robert 
320224ba2bdSOllivier Robert 
321224ba2bdSOllivier Robert 	if (!refclock_process(pp)) {
322224ba2bdSOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
323224ba2bdSOllivier Robert 		return;
324224ba2bdSOllivier Robert 	}
3259c2daa00SOllivier Robert 	pp->lastref = pp->lastrec;
326224ba2bdSOllivier Robert 	refclock_receive(peer);
327224ba2bdSOllivier Robert 
328224ba2bdSOllivier Robert #if 0
3299c2daa00SOllivier Robert 	msyslog(LOG_ERR, " D:%x  D:%d D:%d",synch,pp->minute,pp->second);
330224ba2bdSOllivier Robert #endif
331224ba2bdSOllivier Robert 
332224ba2bdSOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
333224ba2bdSOllivier Robert 
334224ba2bdSOllivier Robert 	return;
335224ba2bdSOllivier Robert }
336224ba2bdSOllivier Robert 
337224ba2bdSOllivier Robert 
338224ba2bdSOllivier Robert /*
339224ba2bdSOllivier Robert  * hopfserial_poll - called by the transmit procedure
340224ba2bdSOllivier Robert  *
341224ba2bdSOllivier Robert  */
342224ba2bdSOllivier Robert static void
343224ba2bdSOllivier Robert hopfserial_poll (
344224ba2bdSOllivier Robert 	int unit,
345224ba2bdSOllivier Robert 	struct peer *peer
346224ba2bdSOllivier Robert 	)
347224ba2bdSOllivier Robert {
348224ba2bdSOllivier Robert 	register struct hopfclock_unit *up;
349224ba2bdSOllivier Robert 	struct refclockproc *pp;
350224ba2bdSOllivier Robert 	pp = peer->procptr;
351224ba2bdSOllivier Robert 
3522b15cb3dSCy Schubert 	up = pp->unitptr;
353224ba2bdSOllivier Robert 
354224ba2bdSOllivier Robert 	pp->polls++;
355224ba2bdSOllivier Robert 	up->rpt_next = 1;
356224ba2bdSOllivier Robert 
357224ba2bdSOllivier Robert #if 0
358224ba2bdSOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
359224ba2bdSOllivier Robert #endif
360224ba2bdSOllivier Robert 
361224ba2bdSOllivier Robert 	return;
362224ba2bdSOllivier Robert }
363224ba2bdSOllivier Robert 
364224ba2bdSOllivier Robert #else
365*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT
366224ba2bdSOllivier Robert #endif /* REFCLOCK */
367