refclock_hopfser.c revision 290001
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
50extern 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 */
86struct 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
98static	int	hopfserial_start	(int, struct peer *);
99static	void	hopfserial_shutdown	(int, struct peer *);
100static	void	hopfserial_receive	(struct recvbuf *);
101static	void	hopfserial_poll		(int, struct peer *);
102/* static  void hopfserial_io		(struct recvbuf *); */
103/*
104 * Transfer vector
105 */
106struct 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 */
119static int
120hopfserial_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 */
184static void
185hopfserial_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
208static void
209hopfserial_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 */
348static void
349hopfserial_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
371int refclock_hopfser_bs;
372#endif /* REFCLOCK */
373