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