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