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