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