xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_hopfser.c (revision c7c727fae85036860d5bb848f2730ff419e2b060)
1 /*	$NetBSD: refclock_hopfser.c,v 1.1.1.2 2012/01/31 21:25:37 kardel 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(sizeof(*up));
152 	memset(up, 0, sizeof(*up));
153 	pp = peer->procptr;
154 	pp->unitptr = (caddr_t)up;
155 	pp->io.clock_recv = hopfserial_receive;
156 	pp->io.srcclock = (caddr_t)peer;
157 	pp->io.datalen = 0;
158 	pp->io.fd = fd;
159 	if (!io_addclock(&pp->io)) {
160 #ifdef DEBUG
161 		printf("hopfSerialClock(%d) io_addclock\n", unit);
162 #endif
163 		close(fd);
164 		pp->io.fd = -1;
165 		free(up);
166 		pp->unitptr = NULL;
167 		return (0);
168 	}
169 
170 	/*
171 	 * Initialize miscellaneous variables
172 	 */
173 	pp->clockdesc = DESCRIPTION;
174 	peer->precision = PRECISION;
175 	peer->burst = NSTAGE;
176 	memcpy((char *)&pp->refid, REFID, 4);
177 
178 	up->leap_status = 0;
179 	up->unit = (short) unit;
180 
181 	return (1);
182 }
183 
184 
185 /*
186  * hopfserial_shutdown - shut down the clock
187  */
188 static void
189 hopfserial_shutdown (
190 	int unit,
191 	struct peer *peer
192 	)
193 {
194 	register struct hopfclock_unit *up;
195 	struct refclockproc *pp;
196 
197 	pp = peer->procptr;
198 	up = (struct hopfclock_unit *)pp->unitptr;
199 
200 	if (-1 != pp->io.fd)
201 		io_closeclock(&pp->io);
202 	if (NULL != up)
203 		free(up);
204 }
205 
206 
207 
208 /*
209  * hopfserial_receive - receive data from the serial interface
210  */
211 
212 static void
213 hopfserial_receive (
214 	struct recvbuf *rbufp
215 	)
216 {
217 	struct hopfclock_unit *up;
218 	struct refclockproc *pp;
219 	struct peer *peer;
220 
221 	int		synch;	/* synchhronization indicator */
222 	int		DoW;	/* Dow */
223 
224 	int	day, month;	/* ddd conversion */
225 
226 	/*
227 	 * Initialize pointers and read the timecode and timestamp.
228 	 */
229 	peer = (struct peer *)rbufp->recv_srcclock;
230 	pp = peer->procptr;
231 	up = (struct hopfclock_unit *)pp->unitptr;
232 
233 	if (up->rpt_next == 0 )
234 		return;
235 
236 
237 	up->rpt_next = 0; /* wait until next poll interval occur */
238 
239 	pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
240 
241 	if (pp->lencode  == 0)
242 		return;
243 
244 	sscanf(pp->a_lastcode,
245 #if 1
246 	       "%1x%1x%2d%2d%2d%2d%2d%2d",   /* ...cr,lf */
247 #else
248 	       "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */
249 #endif
250 	       &synch,
251 	       &DoW,
252 	       &pp->hour,
253 	       &pp->minute,
254 	       &pp->second,
255 	       &day,
256 	       &month,
257 	       &pp->year);
258 
259 
260 	/*
261 	  Validate received values at least enough to prevent internal
262 	  array-bounds problems, etc.
263 	*/
264 	if((pp->hour < 0) || (pp->hour > 23) ||
265 	   (pp->minute < 0) || (pp->minute > 59) ||
266 	   (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
267 	   (day < 1) || (day > 31) ||
268 	   (month < 1) || (month > 12) ||
269 	   (pp->year < 0) || (pp->year > 99)) {
270 		/* Data out of range. */
271 		refclock_report(peer, CEVNT_BADREPLY);
272 		return;
273 	}
274 	/*
275 	  some preparations
276 	*/
277 	pp->day    = ymd2yd(pp->year,month,day);
278 	pp->leap=0;
279 
280 	/* Year-2000 check! */
281 	/* wrap 2-digit date into 4-digit */
282 
283 	if(pp->year < YEAR_PIVOT) { pp->year += 100; }		/* < 98 */
284 	pp->year += 1900;
285 
286 	/* preparation for timecode ntpq rl command ! */
287 
288 #if 0
289 	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
290 		 "STATUS: %1X%1X, DATE: %02d.%02d.%04d  TIME: %02d:%02d:%02d",
291 		 synch,
292 		 DoW,
293 		 day,
294 		 month,
295 		 pp->year,
296 		 pp->hour,
297 		 pp->minute,
298 		 pp->second);
299 
300 	pp->lencode = strlen(pp->a_lastcode);
301 	if ((synch && 0xc) == 0 ){  /* time ok? */
302 		refclock_report(peer, CEVNT_BADTIME);
303 		pp->leap = LEAP_NOTINSYNC;
304 		return;
305 	}
306 #endif
307 	/*
308 	 * If clock has no valid status then report error and exit
309 	 */
310 	if ((synch & HOPF_OPMODE) == HOPF_INVALID ){  /* time ok? */
311 		refclock_report(peer, CEVNT_BADTIME);
312 		pp->leap = LEAP_NOTINSYNC;
313 		return;
314 	}
315 
316 	/*
317 	 * Test if time is running on internal quarz
318 	 * if CLK_FLAG1 is set, sychronize even if no radio operation
319 	 */
320 
321 	if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){
322 		if ((pp->sloppyclockflag & CLK_FLAG1) == 0) {
323 			refclock_report(peer, CEVNT_BADTIME);
324 			pp->leap = LEAP_NOTINSYNC;
325 			return;
326 		}
327 	}
328 
329 
330 	if (!refclock_process(pp)) {
331 		refclock_report(peer, CEVNT_BADTIME);
332 		return;
333 	}
334 	pp->lastref = pp->lastrec;
335 	refclock_receive(peer);
336 
337 #if 0
338 	msyslog(LOG_ERR, " D:%x  D:%d D:%d",synch,pp->minute,pp->second);
339 #endif
340 
341 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
342 
343 	return;
344 }
345 
346 
347 /*
348  * hopfserial_poll - called by the transmit procedure
349  *
350  */
351 static void
352 hopfserial_poll (
353 	int unit,
354 	struct peer *peer
355 	)
356 {
357 	register struct hopfclock_unit *up;
358 	struct refclockproc *pp;
359 	pp = peer->procptr;
360 
361 	up = (struct hopfclock_unit *)pp->unitptr;
362 
363 	pp->polls++;
364 	up->rpt_next = 1;
365 
366 #if 0
367 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
368 #endif
369 
370 	return;
371 }
372 
373 #else
374 int refclock_hopfser_bs;
375 #endif /* REFCLOCK */
376