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