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