xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_hopfser.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: refclock_hopfser.c,v 1.6 2024/08/18 20:47:18 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel /*
4abb0f93cSkardel  *
5abb0f93cSkardel  * refclock_hopfser.c
6abb0f93cSkardel  * - clock driver for hopf serial boards (GPS or DCF77)
7abb0f93cSkardel  *
8abb0f93cSkardel  * Date: 30.03.2000 Revision: 01.10
9abb0f93cSkardel  *
10abb0f93cSkardel  * latest source and further information can be found at:
11abb0f93cSkardel  * http://www.ATLSoft.de/ntp
12abb0f93cSkardel  *
13abb0f93cSkardel  */
14abb0f93cSkardel 
15abb0f93cSkardel #ifdef HAVE_CONFIG_H
16abb0f93cSkardel # include "config.h"
17abb0f93cSkardel #endif
18abb0f93cSkardel 
19abb0f93cSkardel #if defined(REFCLOCK) && (defined(CLOCK_HOPF_SERIAL))
20abb0f93cSkardel 
21abb0f93cSkardel #include "ntpd.h"
22abb0f93cSkardel #include "ntp_io.h"
23abb0f93cSkardel #include "ntp_control.h"
24abb0f93cSkardel #include "ntp_refclock.h"
25abb0f93cSkardel #include "ntp_unixtime.h"
26abb0f93cSkardel #include "ntp_stdlib.h"
27abb0f93cSkardel 
28abb0f93cSkardel #if defined HAVE_SYS_MODEM_H
29abb0f93cSkardel # include <sys/modem.h>
30abb0f93cSkardel # ifndef __QNXNTO__
31abb0f93cSkardel #  define TIOCMSET MCSETA
32abb0f93cSkardel #  define TIOCMGET MCGETA
33abb0f93cSkardel #  define TIOCM_RTS MRTS
34abb0f93cSkardel # endif
35abb0f93cSkardel #endif
36abb0f93cSkardel 
37abb0f93cSkardel #ifdef HAVE_TERMIOS_H
38abb0f93cSkardel # ifdef TERMIOS_NEEDS__SVID3
39abb0f93cSkardel #  define _SVID3
40abb0f93cSkardel # endif
41abb0f93cSkardel # include <termios.h>
42abb0f93cSkardel # ifdef TERMIOS_NEEDS__SVID3
43abb0f93cSkardel #  undef _SVID3
44abb0f93cSkardel # endif
45abb0f93cSkardel #endif
46abb0f93cSkardel 
47abb0f93cSkardel #ifdef HAVE_SYS_IOCTL_H
48abb0f93cSkardel # include <sys/ioctl.h>
49abb0f93cSkardel #endif
50abb0f93cSkardel 
51abb0f93cSkardel /*
52abb0f93cSkardel  * clock definitions
53abb0f93cSkardel  */
54abb0f93cSkardel #define	DESCRIPTION	"hopf Elektronik serial clock" /* Long name */
55abb0f93cSkardel #define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
56abb0f93cSkardel #define	REFID		"hopf\0"	/* reference ID */
57abb0f93cSkardel /*
58abb0f93cSkardel  * I/O definitions
59abb0f93cSkardel  */
60abb0f93cSkardel #define	DEVICE		"/dev/hopfclock%d" 	/* device name and unit */
61abb0f93cSkardel #define	SPEED232	B9600		    	/* uart speed (9600 baud) */
62abb0f93cSkardel 
63abb0f93cSkardel 
64abb0f93cSkardel #define STX 0x02
65abb0f93cSkardel #define ETX 0x03
66abb0f93cSkardel #define CR  0x0c
67abb0f93cSkardel #define LF  0x0a
68abb0f93cSkardel 
69abb0f93cSkardel /* parse states */
70abb0f93cSkardel #define REC_QUEUE_EMPTY       0
71abb0f93cSkardel #define REC_QUEUE_FULL        1
72abb0f93cSkardel 
73abb0f93cSkardel #define	HOPF_OPMODE	0x0C	/* operation mode mask */
74abb0f93cSkardel #define HOPF_INVALID	0x00	/* no time code available */
75abb0f93cSkardel #define HOPF_INTERNAL	0x04	/* internal clock */
76abb0f93cSkardel #define HOPF_RADIO	0x08	/* radio clock */
77abb0f93cSkardel #define HOPF_RADIOHP	0x0C	/* high precision radio clock */
78abb0f93cSkardel 
79abb0f93cSkardel /*
80abb0f93cSkardel  * hopfclock unit control structure.
81abb0f93cSkardel  */
82abb0f93cSkardel struct hopfclock_unit {
83abb0f93cSkardel 	l_fp	laststamp;	/* last receive timestamp */
84abb0f93cSkardel 	short	unit;		/* NTP refclock unit number */
85abb0f93cSkardel 	u_long	polled;		/* flag to detect noreplies */
86abb0f93cSkardel 	char	leap_status;	/* leap second flag */
87abb0f93cSkardel 	int	rpt_next;
88abb0f93cSkardel };
89abb0f93cSkardel 
90abb0f93cSkardel /*
91abb0f93cSkardel  * Function prototypes
92abb0f93cSkardel  */
93abb0f93cSkardel 
94abb0f93cSkardel static	int	hopfserial_start	(int, struct peer *);
95abb0f93cSkardel static	void	hopfserial_shutdown	(int, struct peer *);
96abb0f93cSkardel static	void	hopfserial_receive	(struct recvbuf *);
97abb0f93cSkardel static	void	hopfserial_poll		(int, struct peer *);
98abb0f93cSkardel /* static  void hopfserial_io		(struct recvbuf *); */
99abb0f93cSkardel /*
100abb0f93cSkardel  * Transfer vector
101abb0f93cSkardel  */
102abb0f93cSkardel struct refclock refclock_hopfser = {
103abb0f93cSkardel 	hopfserial_start,	/* start up driver */
104abb0f93cSkardel 	hopfserial_shutdown,	/* shut down driver */
105abb0f93cSkardel 	hopfserial_poll,	/* transmit poll message */
106abb0f93cSkardel 	noentry,		/* not used  */
107abb0f93cSkardel 	noentry,		/* initialize driver (not used) */
108abb0f93cSkardel 	noentry,		/* not used */
109abb0f93cSkardel 	NOFLAGS			/* not used */
110abb0f93cSkardel };
111abb0f93cSkardel 
112abb0f93cSkardel /*
113abb0f93cSkardel  * hopfserial_start - open the devices and initialize data for processing
114abb0f93cSkardel  */
115abb0f93cSkardel static int
116abb0f93cSkardel hopfserial_start (
117abb0f93cSkardel 	int unit,
118abb0f93cSkardel 	struct peer *peer
119abb0f93cSkardel 	)
120abb0f93cSkardel {
121abb0f93cSkardel 	register struct hopfclock_unit *up;
122abb0f93cSkardel 	struct refclockproc *pp;
123abb0f93cSkardel 	int fd;
124abb0f93cSkardel 	char gpsdev[20];
125abb0f93cSkardel 
126f003fb54Skardel 	snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
127abb0f93cSkardel 
128abb0f93cSkardel 	/* LDISC_STD, LDISC_RAW
129abb0f93cSkardel 	 * Open serial port. Use CLK line discipline, if available.
130abb0f93cSkardel 	 */
131*eabc0478Schristos 	fd = refclock_open(&peer->srcadr, gpsdev, SPEED232, LDISC_CLK);
132abb0f93cSkardel 	if (fd <= 0) {
133abb0f93cSkardel #ifdef DEBUG
134abb0f93cSkardel 		printf("hopfSerialClock(%d) start: open %s failed\n", unit, gpsdev);
135abb0f93cSkardel #endif
136abb0f93cSkardel 		return 0;
137abb0f93cSkardel 	}
138abb0f93cSkardel 
139abb0f93cSkardel 	msyslog(LOG_NOTICE, "hopfSerialClock(%d) fd: %d dev: %s", unit, fd,
140abb0f93cSkardel 		gpsdev);
141abb0f93cSkardel 
142abb0f93cSkardel 	/*
143abb0f93cSkardel 	 * Allocate and initialize unit structure
144abb0f93cSkardel 	 */
1458585484eSchristos 	up = emalloc_zero(sizeof(*up));
146abb0f93cSkardel 	pp = peer->procptr;
1478585484eSchristos 	pp->unitptr = up;
148abb0f93cSkardel 	pp->io.clock_recv = hopfserial_receive;
1498585484eSchristos 	pp->io.srcclock = peer;
150abb0f93cSkardel 	pp->io.datalen = 0;
151abb0f93cSkardel 	pp->io.fd = fd;
152abb0f93cSkardel 	if (!io_addclock(&pp->io)) {
153abb0f93cSkardel #ifdef DEBUG
154abb0f93cSkardel 		printf("hopfSerialClock(%d) io_addclock\n", unit);
155abb0f93cSkardel #endif
156f003fb54Skardel 		close(fd);
157f003fb54Skardel 		pp->io.fd = -1;
158abb0f93cSkardel 		free(up);
159f003fb54Skardel 		pp->unitptr = NULL;
160abb0f93cSkardel 		return (0);
161abb0f93cSkardel 	}
162abb0f93cSkardel 
163abb0f93cSkardel 	/*
164abb0f93cSkardel 	 * Initialize miscellaneous variables
165abb0f93cSkardel 	 */
166abb0f93cSkardel 	pp->clockdesc = DESCRIPTION;
167abb0f93cSkardel 	peer->precision = PRECISION;
168abb0f93cSkardel 	memcpy((char *)&pp->refid, REFID, 4);
169abb0f93cSkardel 
170abb0f93cSkardel 	up->leap_status = 0;
171abb0f93cSkardel 	up->unit = (short) unit;
172abb0f93cSkardel 
173abb0f93cSkardel 	return (1);
174abb0f93cSkardel }
175abb0f93cSkardel 
176abb0f93cSkardel 
177abb0f93cSkardel /*
178abb0f93cSkardel  * hopfserial_shutdown - shut down the clock
179abb0f93cSkardel  */
180abb0f93cSkardel static void
181abb0f93cSkardel hopfserial_shutdown (
182abb0f93cSkardel 	int unit,
183abb0f93cSkardel 	struct peer *peer
184abb0f93cSkardel 	)
185abb0f93cSkardel {
186abb0f93cSkardel 	register struct hopfclock_unit *up;
187abb0f93cSkardel 	struct refclockproc *pp;
188abb0f93cSkardel 
189abb0f93cSkardel 	pp = peer->procptr;
1908585484eSchristos 	up = pp->unitptr;
191f003fb54Skardel 
192f003fb54Skardel 	if (-1 != pp->io.fd)
193abb0f93cSkardel 		io_closeclock(&pp->io);
194f003fb54Skardel 	if (NULL != up)
195abb0f93cSkardel 		free(up);
196abb0f93cSkardel }
197abb0f93cSkardel 
198abb0f93cSkardel 
199abb0f93cSkardel 
200abb0f93cSkardel /*
201abb0f93cSkardel  * hopfserial_receive - receive data from the serial interface
202abb0f93cSkardel  */
203abb0f93cSkardel 
204abb0f93cSkardel static void
205abb0f93cSkardel hopfserial_receive (
206abb0f93cSkardel 	struct recvbuf *rbufp
207abb0f93cSkardel 	)
208abb0f93cSkardel {
209abb0f93cSkardel 	struct hopfclock_unit *up;
210abb0f93cSkardel 	struct refclockproc *pp;
211abb0f93cSkardel 	struct peer *peer;
212abb0f93cSkardel 
213abb0f93cSkardel 	int	synch;	/* synchhronization indicator */
2148585484eSchristos 	int	DoW;	/* Day of Week */
215abb0f93cSkardel 
216abb0f93cSkardel 	int	day, month;	/* ddd conversion */
2178585484eSchristos 	int	converted;
218abb0f93cSkardel 
219abb0f93cSkardel 	/*
220abb0f93cSkardel 	 * Initialize pointers and read the timecode and timestamp.
221abb0f93cSkardel 	 */
2228585484eSchristos 	peer = rbufp->recv_peer;
223abb0f93cSkardel 	pp = peer->procptr;
2248585484eSchristos 	up = pp->unitptr;
225abb0f93cSkardel 
226abb0f93cSkardel 	if (up->rpt_next == 0 )
227abb0f93cSkardel 		return;
228abb0f93cSkardel 
229abb0f93cSkardel 	up->rpt_next = 0; /* wait until next poll interval occur */
230abb0f93cSkardel 
2318585484eSchristos 	pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode,
2328585484eSchristos 					      sizeof(pp->a_lastcode),
2338585484eSchristos 					      &pp->lastrec);
234abb0f93cSkardel 	if (pp->lencode == 0)
235abb0f93cSkardel 		return;
236abb0f93cSkardel 
2378585484eSchristos 	converted = sscanf(pp->a_lastcode,
238abb0f93cSkardel #if 1
239abb0f93cSkardel 	       "%1x%1x%2d%2d%2d%2d%2d%2d",   /* ...cr,lf */
240abb0f93cSkardel #else
241abb0f93cSkardel 	       "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */
242abb0f93cSkardel #endif
243abb0f93cSkardel 	       &synch,
244abb0f93cSkardel 	       &DoW,
245abb0f93cSkardel 	       &pp->hour,
246abb0f93cSkardel 	       &pp->minute,
247abb0f93cSkardel 	       &pp->second,
248abb0f93cSkardel 	       &day,
249abb0f93cSkardel 	       &month,
250abb0f93cSkardel 	       &pp->year);
251abb0f93cSkardel 
252abb0f93cSkardel 
253abb0f93cSkardel 	/*
254abb0f93cSkardel 	  Validate received values at least enough to prevent internal
255abb0f93cSkardel 	  array-bounds problems, etc.
256abb0f93cSkardel 	*/
2578585484eSchristos 	if ((8 != converted) || (pp->hour < 0) || (pp->hour > 23) ||
2588585484eSchristos 	   (pp->minute < 0) || (pp->minute > 59) || (pp->second < 0) ||
2598585484eSchristos 	   (pp->second > 60) /*Allow for leap seconds.*/ ||
260abb0f93cSkardel 	   (day < 1) || (day > 31) ||
261abb0f93cSkardel 	   (month < 1) || (month > 12) ||
262abb0f93cSkardel 	   (pp->year < 0) || (pp->year > 99)) {
263abb0f93cSkardel 		/* Data out of range. */
264abb0f93cSkardel 		refclock_report(peer, CEVNT_BADREPLY);
265abb0f93cSkardel 		return;
266abb0f93cSkardel 	}
267abb0f93cSkardel 	/*
268abb0f93cSkardel 	  some preparations
269abb0f93cSkardel 	*/
270abb0f93cSkardel 	pp->day    = ymd2yd(pp->year,month,day);
271abb0f93cSkardel 	pp->leap=0;
272abb0f93cSkardel 
273abb0f93cSkardel 	/* Year-2000 check! */
274abb0f93cSkardel 	/* wrap 2-digit date into 4-digit */
275abb0f93cSkardel 
276abb0f93cSkardel 	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* < 98 */
277abb0f93cSkardel 	pp->year += 1900;
278abb0f93cSkardel 
279abb0f93cSkardel 	/* preparation for timecode ntpq rl command ! */
280abb0f93cSkardel 
281abb0f93cSkardel #if 0
282f003fb54Skardel 	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
283abb0f93cSkardel 		 "STATUS: %1X%1X, DATE: %02d.%02d.%04d  TIME: %02d:%02d:%02d",
284abb0f93cSkardel 		 synch,
285abb0f93cSkardel 		 DoW,
286abb0f93cSkardel 		 day,
287abb0f93cSkardel 		 month,
288abb0f93cSkardel 		 pp->year,
289abb0f93cSkardel 		 pp->hour,
290abb0f93cSkardel 		 pp->minute,
291abb0f93cSkardel 		 pp->second);
292abb0f93cSkardel 
293abb0f93cSkardel 	pp->lencode = strlen(pp->a_lastcode);
294abb0f93cSkardel 	if ((synch && 0xc) == 0 ){  /* time ok? */
295abb0f93cSkardel 		refclock_report(peer, CEVNT_BADTIME);
296abb0f93cSkardel 		pp->leap = LEAP_NOTINSYNC;
297abb0f93cSkardel 		return;
298abb0f93cSkardel 	}
299abb0f93cSkardel #endif
300abb0f93cSkardel 	/*
301abb0f93cSkardel 	 * If clock has no valid status then report error and exit
302abb0f93cSkardel 	 */
303abb0f93cSkardel 	if ((synch & HOPF_OPMODE) == HOPF_INVALID ){  /* time ok? */
304abb0f93cSkardel 		refclock_report(peer, CEVNT_BADTIME);
305abb0f93cSkardel 		pp->leap = LEAP_NOTINSYNC;
306abb0f93cSkardel 		return;
307abb0f93cSkardel 	}
308abb0f93cSkardel 
309abb0f93cSkardel 	/*
310abb0f93cSkardel 	 * Test if time is running on internal quarz
311abb0f93cSkardel 	 * if CLK_FLAG1 is set, sychronize even if no radio operation
312abb0f93cSkardel 	 */
313abb0f93cSkardel 
314abb0f93cSkardel 	if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){
315abb0f93cSkardel 		if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
316abb0f93cSkardel 			refclock_report(peer, CEVNT_BADTIME);
317abb0f93cSkardel 			pp->leap = LEAP_NOTINSYNC;
318abb0f93cSkardel 			return;
319abb0f93cSkardel 		}
320abb0f93cSkardel 	}
321abb0f93cSkardel 
322abb0f93cSkardel 
323abb0f93cSkardel 	if (!refclock_process(pp)) {
324abb0f93cSkardel 		refclock_report(peer, CEVNT_BADTIME);
325abb0f93cSkardel 		return;
326abb0f93cSkardel 	}
327abb0f93cSkardel 	pp->lastref = pp->lastrec;
328abb0f93cSkardel 	refclock_receive(peer);
329abb0f93cSkardel 
330abb0f93cSkardel #if 0
331abb0f93cSkardel 	msyslog(LOG_ERR, " D:%x  D:%d D:%d",synch,pp->minute,pp->second);
332abb0f93cSkardel #endif
333abb0f93cSkardel 
334abb0f93cSkardel 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
335abb0f93cSkardel 
336abb0f93cSkardel 	return;
337abb0f93cSkardel }
338abb0f93cSkardel 
339abb0f93cSkardel 
340abb0f93cSkardel /*
341abb0f93cSkardel  * hopfserial_poll - called by the transmit procedure
342abb0f93cSkardel  *
343abb0f93cSkardel  */
344abb0f93cSkardel static void
345abb0f93cSkardel hopfserial_poll (
346abb0f93cSkardel 	int unit,
347abb0f93cSkardel 	struct peer *peer
348abb0f93cSkardel 	)
349abb0f93cSkardel {
350abb0f93cSkardel 	register struct hopfclock_unit *up;
351abb0f93cSkardel 	struct refclockproc *pp;
352abb0f93cSkardel 	pp = peer->procptr;
353abb0f93cSkardel 
3548585484eSchristos 	up = pp->unitptr;
355abb0f93cSkardel 
356abb0f93cSkardel 	pp->polls++;
357abb0f93cSkardel 	up->rpt_next = 1;
358abb0f93cSkardel 
359abb0f93cSkardel #if 0
360abb0f93cSkardel 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
361abb0f93cSkardel #endif
362abb0f93cSkardel 
363abb0f93cSkardel 	return;
364abb0f93cSkardel }
365abb0f93cSkardel 
366abb0f93cSkardel #else
367*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT
368abb0f93cSkardel #endif /* REFCLOCK */
369