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