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