1*f7d97a96Schristos /*	$NetBSD: refclock_neoclock4x.c,v 1.12 2020/05/27 23:52:19 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel /*
4abb0f93cSkardel  *
5abb0f93cSkardel  * Refclock_neoclock4x.c
6abb0f93cSkardel  * - NeoClock4X driver for DCF77 or FIA Timecode
7abb0f93cSkardel  *
845530cf1Skardel  * Date: 2009-12-04 v1.16
9abb0f93cSkardel  *
10abb0f93cSkardel  * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir
11abb0f93cSkardel  * for details about the NeoClock4X device
12abb0f93cSkardel  *
13abb0f93cSkardel  */
14abb0f93cSkardel 
15abb0f93cSkardel #ifdef HAVE_CONFIG_H
16abb0f93cSkardel # include "config.h"
17abb0f93cSkardel #endif
18abb0f93cSkardel 
19abb0f93cSkardel #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
20abb0f93cSkardel 
21abb0f93cSkardel #include <unistd.h>
22abb0f93cSkardel #include <sys/time.h>
23abb0f93cSkardel #include <sys/types.h>
24abb0f93cSkardel #include <termios.h>
25abb0f93cSkardel #include <sys/ioctl.h>
26abb0f93cSkardel #include <ctype.h>
27abb0f93cSkardel 
28abb0f93cSkardel #include "ntpd.h"
29abb0f93cSkardel #include "ntp_io.h"
30abb0f93cSkardel #include "ntp_control.h"
31abb0f93cSkardel #include "ntp_refclock.h"
32abb0f93cSkardel #include "ntp_unixtime.h"
33abb0f93cSkardel #include "ntp_stdlib.h"
34abb0f93cSkardel 
35abb0f93cSkardel #if defined HAVE_SYS_MODEM_H
36abb0f93cSkardel # include <sys/modem.h>
37abb0f93cSkardel # ifndef __QNXNTO__
38abb0f93cSkardel #  define TIOCMSET MCSETA
39abb0f93cSkardel #  define TIOCMGET MCGETA
40abb0f93cSkardel #  define TIOCM_RTS MRTS
41abb0f93cSkardel # endif
42abb0f93cSkardel #endif
43abb0f93cSkardel 
44abb0f93cSkardel #ifdef HAVE_TERMIOS_H
45abb0f93cSkardel # ifdef TERMIOS_NEEDS__SVID3
46abb0f93cSkardel #  define _SVID3
47abb0f93cSkardel # endif
48abb0f93cSkardel # include <termios.h>
49abb0f93cSkardel # ifdef TERMIOS_NEEDS__SVID3
50abb0f93cSkardel #  undef _SVID3
51abb0f93cSkardel # endif
52abb0f93cSkardel #endif
53abb0f93cSkardel 
54abb0f93cSkardel #ifdef HAVE_SYS_IOCTL_H
55abb0f93cSkardel # include <sys/ioctl.h>
56abb0f93cSkardel #endif
57abb0f93cSkardel 
58abb0f93cSkardel /*
59abb0f93cSkardel  * NTP version 4.20 change the pp->msec field to pp->nsec.
60abb0f93cSkardel  * To allow to support older ntp versions with this sourcefile
61abb0f93cSkardel  * you can define NTP_PRE_420 to allow this driver to compile
62abb0f93cSkardel  * with ntp version back to 4.1.2.
63abb0f93cSkardel  *
64abb0f93cSkardel  */
65abb0f93cSkardel #if 0
66abb0f93cSkardel #define NTP_PRE_420
67abb0f93cSkardel #endif
68abb0f93cSkardel 
69abb0f93cSkardel /*
70abb0f93cSkardel  * If you want the driver for whatever reason to not use
71abb0f93cSkardel  * the TX line to send anything to your NeoClock4X
72abb0f93cSkardel  * device you must tell the NTP refclock driver which
73abb0f93cSkardel  * firmware you NeoClock4X device uses.
74abb0f93cSkardel  *
75abb0f93cSkardel  * If you want to enable this feature change the "#if 0"
76abb0f93cSkardel  * line to "#if 1" and make sure that the defined firmware
77abb0f93cSkardel  * matches the firmware off your NeoClock4X receiver!
78abb0f93cSkardel  *
79abb0f93cSkardel  */
80abb0f93cSkardel 
81abb0f93cSkardel #if 0
82abb0f93cSkardel #define NEOCLOCK4X_FIRMWARE                NEOCLOCK4X_FIRMWARE_VERSION_A
83abb0f93cSkardel #endif
84abb0f93cSkardel 
85abb0f93cSkardel /* at this time only firmware version A is known */
86abb0f93cSkardel #define NEOCLOCK4X_FIRMWARE_VERSION_A      'A'
87abb0f93cSkardel 
88abb0f93cSkardel #define NEOCLOCK4X_TIMECODELEN 37
89abb0f93cSkardel 
90abb0f93cSkardel #define NEOCLOCK4X_OFFSET_SERIAL            3
91abb0f93cSkardel #define NEOCLOCK4X_OFFSET_RADIOSIGNAL       9
92abb0f93cSkardel #define NEOCLOCK4X_OFFSET_DAY              12
93abb0f93cSkardel #define NEOCLOCK4X_OFFSET_MONTH            14
94abb0f93cSkardel #define NEOCLOCK4X_OFFSET_YEAR             16
95abb0f93cSkardel #define NEOCLOCK4X_OFFSET_HOUR             18
96abb0f93cSkardel #define NEOCLOCK4X_OFFSET_MINUTE           20
97abb0f93cSkardel #define NEOCLOCK4X_OFFSET_SECOND           22
98abb0f93cSkardel #define NEOCLOCK4X_OFFSET_HSEC             24
99abb0f93cSkardel #define NEOCLOCK4X_OFFSET_DOW              26
100abb0f93cSkardel #define NEOCLOCK4X_OFFSET_TIMESOURCE       28
101abb0f93cSkardel #define NEOCLOCK4X_OFFSET_DSTSTATUS        29
102abb0f93cSkardel #define NEOCLOCK4X_OFFSET_QUARZSTATUS      30
103abb0f93cSkardel #define NEOCLOCK4X_OFFSET_ANTENNA1         31
104abb0f93cSkardel #define NEOCLOCK4X_OFFSET_ANTENNA2         33
105abb0f93cSkardel #define NEOCLOCK4X_OFFSET_CRC              35
106abb0f93cSkardel 
10745530cf1Skardel #define NEOCLOCK4X_DRIVER_VERSION          "1.16 (2009-12-04)"
108abb0f93cSkardel 
109abb0f93cSkardel #define NSEC_TO_MILLI                      1000000
110abb0f93cSkardel 
111abb0f93cSkardel struct neoclock4x_unit {
112abb0f93cSkardel   l_fp	laststamp;	/* last receive timestamp */
113abb0f93cSkardel   short	unit;		/* NTP refclock unit number */
114abb0f93cSkardel   u_long polled;	/* flag to detect noreplies */
115abb0f93cSkardel   char	leap_status;	/* leap second flag */
116abb0f93cSkardel   int	recvnow;
117abb0f93cSkardel 
118abb0f93cSkardel   char  firmware[80];
119abb0f93cSkardel   char  firmwaretag;
120abb0f93cSkardel   char  serial[7];
121abb0f93cSkardel   char  radiosignal[4];
122abb0f93cSkardel   char  timesource;
123abb0f93cSkardel   char  dststatus;
124abb0f93cSkardel   char  quarzstatus;
125abb0f93cSkardel   int   antenna1;
126abb0f93cSkardel   int   antenna2;
127abb0f93cSkardel   int   utc_year;
128abb0f93cSkardel   int   utc_month;
129abb0f93cSkardel   int   utc_day;
130abb0f93cSkardel   int   utc_hour;
131abb0f93cSkardel   int   utc_minute;
132abb0f93cSkardel   int   utc_second;
133abb0f93cSkardel   int   utc_msec;
134abb0f93cSkardel };
135abb0f93cSkardel 
136abb0f93cSkardel static	int	neoclock4x_start	(int, struct peer *);
137abb0f93cSkardel static	void	neoclock4x_shutdown	(int, struct peer *);
138abb0f93cSkardel static	void	neoclock4x_receive	(struct recvbuf *);
139abb0f93cSkardel static	void	neoclock4x_poll		(int, struct peer *);
140b3d6264cSchristos static	void	neoclock4x_control	(int, const struct refclockstat *, struct refclockstat *, struct peer *);
141abb0f93cSkardel 
142abb0f93cSkardel static int	neol_atoi_len		(const char str[], int *, int);
143abb0f93cSkardel static int	neol_hexatoi_len	(const char str[], int *, int);
144abb0f93cSkardel static void	neol_jdn_to_ymd		(unsigned long, int *, int *, int *);
145abb0f93cSkardel static void	neol_localtime		(unsigned long, int* , int*, int*, int*, int*, int*);
146abb0f93cSkardel static unsigned long neol_mktime	(int, int, int, int, int, int);
147abb0f93cSkardel #if !defined(NEOCLOCK4X_FIRMWARE)
148b3d6264cSchristos static int	neol_query_firmware	(int, int, char *, size_t);
149abb0f93cSkardel static int	neol_check_firmware	(int, const char*, char *);
150abb0f93cSkardel #endif
151abb0f93cSkardel 
152abb0f93cSkardel struct refclock refclock_neoclock4x = {
153abb0f93cSkardel   neoclock4x_start,	/* start up driver */
154abb0f93cSkardel   neoclock4x_shutdown,	/* shut down driver */
155abb0f93cSkardel   neoclock4x_poll,	/* transmit poll message */
156abb0f93cSkardel   neoclock4x_control,
157abb0f93cSkardel   noentry,		/* initialize driver (not used) */
158abb0f93cSkardel   noentry,		/* not used */
159abb0f93cSkardel   NOFLAGS			/* not used */
160abb0f93cSkardel };
161abb0f93cSkardel 
162abb0f93cSkardel static int
neoclock4x_start(int unit,struct peer * peer)163abb0f93cSkardel neoclock4x_start(int unit,
164abb0f93cSkardel 		 struct peer *peer)
165abb0f93cSkardel {
166abb0f93cSkardel   struct neoclock4x_unit *up;
167abb0f93cSkardel   struct refclockproc *pp;
168abb0f93cSkardel   int fd;
169abb0f93cSkardel   char dev[20];
170abb0f93cSkardel   int sl232;
171abb0f93cSkardel #if defined(HAVE_TERMIOS)
172abb0f93cSkardel   struct termios termsettings;
173abb0f93cSkardel #endif
174abb0f93cSkardel #if !defined(NEOCLOCK4X_FIRMWARE)
175abb0f93cSkardel   int tries;
176abb0f93cSkardel #endif
177abb0f93cSkardel 
178abb0f93cSkardel   (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit);
179abb0f93cSkardel 
180abb0f93cSkardel   /* LDISC_STD, LDISC_RAW
181abb0f93cSkardel    * Open serial port. Use CLK line discipline, if available.
182abb0f93cSkardel    */
183abb0f93cSkardel   fd = refclock_open(dev, B2400, LDISC_STD);
184abb0f93cSkardel   if(fd <= 0)
185abb0f93cSkardel     {
186abb0f93cSkardel       return (0);
187abb0f93cSkardel     }
188abb0f93cSkardel 
189abb0f93cSkardel #if defined(HAVE_TERMIOS)
190abb0f93cSkardel 
191abb0f93cSkardel #if 1
192abb0f93cSkardel   if(tcgetattr(fd, &termsettings) < 0)
193abb0f93cSkardel     {
194abb0f93cSkardel       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
195abb0f93cSkardel       (void) close(fd);
196abb0f93cSkardel       return (0);
197abb0f93cSkardel     }
198abb0f93cSkardel 
199abb0f93cSkardel   /* 2400 Baud 8N2 */
200abb0f93cSkardel   termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL;
201abb0f93cSkardel   termsettings.c_oflag = 0;
202abb0f93cSkardel   termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD;
203abb0f93cSkardel   (void)cfsetispeed(&termsettings, (u_int)B2400);
204abb0f93cSkardel   (void)cfsetospeed(&termsettings, (u_int)B2400);
205abb0f93cSkardel 
206abb0f93cSkardel   if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
207abb0f93cSkardel     {
208abb0f93cSkardel       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
209abb0f93cSkardel       (void) close(fd);
210abb0f93cSkardel       return (0);
211abb0f93cSkardel     }
212abb0f93cSkardel 
213abb0f93cSkardel #else
214abb0f93cSkardel   if(tcgetattr(fd, &termsettings) < 0)
215abb0f93cSkardel     {
216abb0f93cSkardel       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
217abb0f93cSkardel       (void) close(fd);
218abb0f93cSkardel       return (0);
219abb0f93cSkardel     }
220abb0f93cSkardel 
221abb0f93cSkardel   /* 2400 Baud 8N2 */
222abb0f93cSkardel   termsettings.c_cflag &= ~PARENB;
223abb0f93cSkardel   termsettings.c_cflag |= CSTOPB;
224abb0f93cSkardel   termsettings.c_cflag &= ~CSIZE;
225abb0f93cSkardel   termsettings.c_cflag |= CS8;
226abb0f93cSkardel 
227abb0f93cSkardel   if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
228abb0f93cSkardel     {
229abb0f93cSkardel       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
230abb0f93cSkardel       (void) close(fd);
231abb0f93cSkardel       return (0);
232abb0f93cSkardel     }
233abb0f93cSkardel #endif
234abb0f93cSkardel 
235abb0f93cSkardel #elif defined(HAVE_SYSV_TTYS)
236abb0f93cSkardel   if(ioctl(fd, TCGETA, &termsettings) < 0)
237abb0f93cSkardel     {
238abb0f93cSkardel       msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit);
239abb0f93cSkardel       (void) close(fd);
240abb0f93cSkardel       return (0);
241abb0f93cSkardel     }
242abb0f93cSkardel 
243abb0f93cSkardel   /* 2400 Baud 8N2 */
244abb0f93cSkardel   termsettings.c_cflag &= ~PARENB;
245abb0f93cSkardel   termsettings.c_cflag |= CSTOPB;
246abb0f93cSkardel   termsettings.c_cflag &= ~CSIZE;
247abb0f93cSkardel   termsettings.c_cflag |= CS8;
248abb0f93cSkardel 
249abb0f93cSkardel   if(ioctl(fd, TCSETA, &termsettings) < 0)
250abb0f93cSkardel     {
251abb0f93cSkardel       msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit);
252abb0f93cSkardel       (void) close(fd);
253abb0f93cSkardel       return (0);
254abb0f93cSkardel     }
255abb0f93cSkardel #else
256abb0f93cSkardel   msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
257abb0f93cSkardel   (void) close(fd);
258abb0f93cSkardel   return (0);
259abb0f93cSkardel #endif
260abb0f93cSkardel 
261abb0f93cSkardel #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
262abb0f93cSkardel   /* turn on RTS, and DTR for power supply */
263abb0f93cSkardel   /* NeoClock4x is powered from serial line */
264abb0f93cSkardel   if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
265abb0f93cSkardel     {
266abb0f93cSkardel       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
267abb0f93cSkardel       (void) close(fd);
268abb0f93cSkardel       return (0);
269abb0f93cSkardel     }
270abb0f93cSkardel #ifdef TIOCM_RTS
271abb0f93cSkardel   sl232 = sl232 | TIOCM_DTR | TIOCM_RTS;	/* turn on RTS, and DTR for power supply */
272abb0f93cSkardel #else
273abb0f93cSkardel   sl232 = sl232 | CIOCM_DTR | CIOCM_RTS;	/* turn on RTS, and DTR for power supply */
274abb0f93cSkardel #endif
275abb0f93cSkardel   if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
276abb0f93cSkardel     {
277abb0f93cSkardel       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
278abb0f93cSkardel       (void) close(fd);
279abb0f93cSkardel       return (0);
280abb0f93cSkardel     }
281abb0f93cSkardel #else
282abb0f93cSkardel   msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
283abb0f93cSkardel 	  unit);
284abb0f93cSkardel   (void) close(fd);
285abb0f93cSkardel   return (0);
286abb0f93cSkardel #endif
287abb0f93cSkardel 
288abb0f93cSkardel   up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
289abb0f93cSkardel   if(!(up))
290abb0f93cSkardel     {
291abb0f93cSkardel       msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
292abb0f93cSkardel       (void) close(fd);
293abb0f93cSkardel       return (0);
294abb0f93cSkardel     }
295abb0f93cSkardel 
296abb0f93cSkardel   memset((char *)up, 0, sizeof(struct neoclock4x_unit));
297abb0f93cSkardel   pp = peer->procptr;
298abb0f93cSkardel   pp->clockdesc = "NeoClock4X";
299b3d6264cSchristos   pp->unitptr = up;
300abb0f93cSkardel   pp->io.clock_recv = neoclock4x_receive;
301b3d6264cSchristos   pp->io.srcclock = peer;
302abb0f93cSkardel   pp->io.datalen = 0;
303abb0f93cSkardel   pp->io.fd = fd;
304abb0f93cSkardel   /*
305abb0f93cSkardel    * no fudge time is given by user!
306abb0f93cSkardel    * use 169.583333 ms to compensate the serial line delay
307abb0f93cSkardel    * formula is:
308abb0f93cSkardel    * 2400 Baud / 11 bit = 218.18 charaters per second
309abb0f93cSkardel    *  (NeoClock4X timecode len)
310abb0f93cSkardel    */
311abb0f93cSkardel   pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
312abb0f93cSkardel 
313abb0f93cSkardel   /*
314abb0f93cSkardel    * Initialize miscellaneous variables
315abb0f93cSkardel    */
316abb0f93cSkardel   peer->precision = -10;
317abb0f93cSkardel   memcpy((char *)&pp->refid, "neol", 4);
318abb0f93cSkardel 
319abb0f93cSkardel   up->leap_status = 0;
320abb0f93cSkardel   up->unit = unit;
321b3d6264cSchristos   strlcpy(up->firmware, "?", sizeof(up->firmware));
322abb0f93cSkardel   up->firmwaretag = '?';
323b3d6264cSchristos   strlcpy(up->serial, "?", sizeof(up->serial));
324b3d6264cSchristos   strlcpy(up->radiosignal, "?", sizeof(up->radiosignal));
325abb0f93cSkardel   up->timesource  = '?';
326abb0f93cSkardel   up->dststatus   = '?';
327abb0f93cSkardel   up->quarzstatus = '?';
328abb0f93cSkardel   up->antenna1    = -1;
329abb0f93cSkardel   up->antenna2    = -1;
330abb0f93cSkardel   up->utc_year    = 0;
331abb0f93cSkardel   up->utc_month   = 0;
332abb0f93cSkardel   up->utc_day     = 0;
333abb0f93cSkardel   up->utc_hour    = 0;
334abb0f93cSkardel   up->utc_minute  = 0;
335abb0f93cSkardel   up->utc_second  = 0;
336abb0f93cSkardel   up->utc_msec    = 0;
337abb0f93cSkardel 
338abb0f93cSkardel #if defined(NEOCLOCK4X_FIRMWARE)
339abb0f93cSkardel #if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
340b3d6264cSchristos   strlcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)",
341b3d6264cSchristos 	  sizeof(up->firmware));
342abb0f93cSkardel   up->firmwaretag = 'A';
343abb0f93cSkardel #else
344abb0f93cSkardel   msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
345abb0f93cSkardel 	  unit);
346abb0f93cSkardel   (void) close(fd);
347abb0f93cSkardel   pp->io.fd = -1;
348abb0f93cSkardel   free(pp->unitptr);
349abb0f93cSkardel   pp->unitptr = NULL;
350abb0f93cSkardel   return (0);
351abb0f93cSkardel #endif
352abb0f93cSkardel #else
353abb0f93cSkardel   for(tries=0; tries < 5; tries++)
354abb0f93cSkardel     {
355abb0f93cSkardel       NLOG(NLOG_CLOCKINFO)
356abb0f93cSkardel 	msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
357abb0f93cSkardel       /* wait 3 seconds for receiver to power up */
358abb0f93cSkardel       sleep(3);
359abb0f93cSkardel       if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
360abb0f93cSkardel 	{
361abb0f93cSkardel 	  break;
362abb0f93cSkardel 	}
363abb0f93cSkardel     }
364abb0f93cSkardel 
365abb0f93cSkardel   /* can I handle this firmware version? */
366abb0f93cSkardel   if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
367abb0f93cSkardel     {
368abb0f93cSkardel       (void) close(fd);
369abb0f93cSkardel       pp->io.fd = -1;
370abb0f93cSkardel       free(pp->unitptr);
371abb0f93cSkardel       pp->unitptr = NULL;
372abb0f93cSkardel       return (0);
373abb0f93cSkardel     }
374abb0f93cSkardel #endif
375abb0f93cSkardel 
376abb0f93cSkardel   if(!io_addclock(&pp->io))
377abb0f93cSkardel     {
378abb0f93cSkardel       msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
379abb0f93cSkardel       (void) close(fd);
380abb0f93cSkardel       pp->io.fd = -1;
381abb0f93cSkardel       free(pp->unitptr);
382abb0f93cSkardel       pp->unitptr = NULL;
383abb0f93cSkardel       return (0);
384abb0f93cSkardel     }
385abb0f93cSkardel 
386abb0f93cSkardel   NLOG(NLOG_CLOCKINFO)
387abb0f93cSkardel     msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
388abb0f93cSkardel 
389abb0f93cSkardel   return (1);
390abb0f93cSkardel }
391abb0f93cSkardel 
392abb0f93cSkardel static void
neoclock4x_shutdown(int unit,struct peer * peer)393abb0f93cSkardel neoclock4x_shutdown(int unit,
394abb0f93cSkardel 		   struct peer *peer)
395abb0f93cSkardel {
396abb0f93cSkardel   struct neoclock4x_unit *up;
397abb0f93cSkardel   struct refclockproc *pp;
398abb0f93cSkardel   int sl232;
399abb0f93cSkardel 
400abb0f93cSkardel   if(NULL != peer)
401abb0f93cSkardel     {
402abb0f93cSkardel       pp = peer->procptr;
403abb0f93cSkardel       if(pp != NULL)
404abb0f93cSkardel         {
405b3d6264cSchristos           up = pp->unitptr;
406abb0f93cSkardel           if(up != NULL)
407abb0f93cSkardel             {
408abb0f93cSkardel               if(-1 !=  pp->io.fd)
409abb0f93cSkardel                 {
410abb0f93cSkardel #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
411abb0f93cSkardel                   /* turn on RTS, and DTR for power supply */
412abb0f93cSkardel                   /* NeoClock4x is powered from serial line */
413abb0f93cSkardel                   if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
414abb0f93cSkardel                     {
415abb0f93cSkardel                       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
416abb0f93cSkardel                               unit);
417abb0f93cSkardel                     }
418abb0f93cSkardel #ifdef TIOCM_RTS
419abb0f93cSkardel                   /* turn on RTS, and DTR for power supply */
420abb0f93cSkardel                   sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
421abb0f93cSkardel #else
422abb0f93cSkardel                   /* turn on RTS, and DTR for power supply */
423abb0f93cSkardel                   sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
424abb0f93cSkardel #endif
425abb0f93cSkardel                   if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
426abb0f93cSkardel                     {
427abb0f93cSkardel                       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
428abb0f93cSkardel                               unit);
429abb0f93cSkardel                     }
430abb0f93cSkardel #endif
431abb0f93cSkardel                   io_closeclock(&pp->io);
432abb0f93cSkardel                 }
433abb0f93cSkardel               free(up);
434abb0f93cSkardel               pp->unitptr = NULL;
435abb0f93cSkardel             }
436abb0f93cSkardel         }
437abb0f93cSkardel     }
438abb0f93cSkardel 
439abb0f93cSkardel   msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
440abb0f93cSkardel 
441abb0f93cSkardel   NLOG(NLOG_CLOCKINFO)
442abb0f93cSkardel     msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
443abb0f93cSkardel }
444abb0f93cSkardel 
445abb0f93cSkardel static void
neoclock4x_receive(struct recvbuf * rbufp)446abb0f93cSkardel neoclock4x_receive(struct recvbuf *rbufp)
447abb0f93cSkardel {
448abb0f93cSkardel   struct neoclock4x_unit *up;
449abb0f93cSkardel   struct refclockproc *pp;
450abb0f93cSkardel   struct peer *peer;
451abb0f93cSkardel   unsigned long calc_utc;
452abb0f93cSkardel   int day;
453abb0f93cSkardel   int month;	/* ddd conversion */
454abb0f93cSkardel   int c;
455abb0f93cSkardel   int dsec;
456abb0f93cSkardel   unsigned char calc_chksum;
457abb0f93cSkardel   int recv_chksum;
458abb0f93cSkardel 
459b3d6264cSchristos   peer = rbufp->recv_peer;
460abb0f93cSkardel   pp = peer->procptr;
461b3d6264cSchristos   up = pp->unitptr;
462abb0f93cSkardel 
463abb0f93cSkardel   /* wait till poll interval is reached */
464abb0f93cSkardel   if(0 == up->recvnow)
465abb0f93cSkardel     return;
466abb0f93cSkardel 
467abb0f93cSkardel   /* reset poll interval flag */
468abb0f93cSkardel   up->recvnow = 0;
469abb0f93cSkardel 
470abb0f93cSkardel   /* read last received timecode */
471abb0f93cSkardel   pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
472abb0f93cSkardel   pp->leap = LEAP_NOWARNING;
473abb0f93cSkardel 
474abb0f93cSkardel   if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
475abb0f93cSkardel     {
476abb0f93cSkardel       NLOG(NLOG_CLOCKEVENT)
477abb0f93cSkardel 	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
478abb0f93cSkardel 		up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
479abb0f93cSkardel       refclock_report(peer, CEVNT_BADREPLY);
480abb0f93cSkardel       return;
481abb0f93cSkardel     }
482abb0f93cSkardel 
483abb0f93cSkardel   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
484abb0f93cSkardel 
485abb0f93cSkardel   /* calculate checksum */
486abb0f93cSkardel   calc_chksum = 0;
487abb0f93cSkardel   for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
488abb0f93cSkardel     {
489abb0f93cSkardel       calc_chksum += pp->a_lastcode[c];
490abb0f93cSkardel     }
491abb0f93cSkardel   if(recv_chksum != calc_chksum)
492abb0f93cSkardel     {
493abb0f93cSkardel       NLOG(NLOG_CLOCKEVENT)
494abb0f93cSkardel 	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
495abb0f93cSkardel 		up->unit, pp->a_lastcode);
496abb0f93cSkardel       refclock_report(peer, CEVNT_BADREPLY);
497abb0f93cSkardel       return;
498abb0f93cSkardel     }
499abb0f93cSkardel 
500abb0f93cSkardel   /* Allow synchronization even is quartz clock is
501abb0f93cSkardel    * never initialized.
502abb0f93cSkardel    * WARNING: This is dangerous!
503abb0f93cSkardel    */
504abb0f93cSkardel   up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
505abb0f93cSkardel   if(0==(pp->sloppyclockflag & CLK_FLAG2))
506abb0f93cSkardel     {
507abb0f93cSkardel       if('I' != up->quarzstatus)
508abb0f93cSkardel 	{
509abb0f93cSkardel 	  NLOG(NLOG_CLOCKEVENT)
510abb0f93cSkardel 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
511abb0f93cSkardel 		    up->unit, pp->a_lastcode);
512abb0f93cSkardel 	  pp->leap = LEAP_NOTINSYNC;
513abb0f93cSkardel 	  refclock_report(peer, CEVNT_BADDATE);
514abb0f93cSkardel 	  return;
515abb0f93cSkardel 	}
516abb0f93cSkardel     }
517abb0f93cSkardel   if('I' != up->quarzstatus)
518abb0f93cSkardel     {
519abb0f93cSkardel       NLOG(NLOG_CLOCKEVENT)
520abb0f93cSkardel 	msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
521abb0f93cSkardel 		up->unit, pp->a_lastcode);
522abb0f93cSkardel     }
523abb0f93cSkardel 
524abb0f93cSkardel   /*
525abb0f93cSkardel    * If NeoClock4X is not synchronized to a radio clock
526abb0f93cSkardel    * check if we're allowed to synchronize with the quartz
527abb0f93cSkardel    * clock.
528abb0f93cSkardel    */
529abb0f93cSkardel   up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
530abb0f93cSkardel   if(0==(pp->sloppyclockflag & CLK_FLAG2))
531abb0f93cSkardel     {
532abb0f93cSkardel       if('A' != up->timesource)
533abb0f93cSkardel 	{
534abb0f93cSkardel 	  /* not allowed to sync with quartz clock */
535abb0f93cSkardel 	  if(0==(pp->sloppyclockflag & CLK_FLAG1))
536abb0f93cSkardel 	    {
537abb0f93cSkardel 	      refclock_report(peer, CEVNT_BADTIME);
538abb0f93cSkardel 	      pp->leap = LEAP_NOTINSYNC;
539abb0f93cSkardel 	      return;
540abb0f93cSkardel 	    }
541abb0f93cSkardel 	}
542abb0f93cSkardel     }
543abb0f93cSkardel 
544abb0f93cSkardel   /* this should only used when first install is done */
545abb0f93cSkardel   if(pp->sloppyclockflag & CLK_FLAG4)
546abb0f93cSkardel     {
547abb0f93cSkardel       msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
548abb0f93cSkardel 	      up->unit, pp->a_lastcode);
549abb0f93cSkardel     }
550abb0f93cSkardel 
551abb0f93cSkardel   /* 123456789012345678901234567890123456789012345 */
552abb0f93cSkardel   /* S/N123456DCF1004021010001202ASX1213CR\r\n */
553abb0f93cSkardel 
554abb0f93cSkardel   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
555abb0f93cSkardel   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
556abb0f93cSkardel   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
557abb0f93cSkardel   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
558abb0f93cSkardel   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
559abb0f93cSkardel   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
560abb0f93cSkardel   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2);
561abb0f93cSkardel #if defined(NTP_PRE_420)
562abb0f93cSkardel   pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */
563abb0f93cSkardel #else
564abb0f93cSkardel   pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */
565abb0f93cSkardel #endif
566abb0f93cSkardel 
567abb0f93cSkardel   memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
568abb0f93cSkardel   up->radiosignal[3] = 0;
569abb0f93cSkardel   memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
570abb0f93cSkardel   up->serial[6] = 0;
571abb0f93cSkardel   up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
572abb0f93cSkardel   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
573abb0f93cSkardel   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
574abb0f93cSkardel 
575abb0f93cSkardel   /*
576abb0f93cSkardel     Validate received values at least enough to prevent internal
577abb0f93cSkardel     array-bounds problems, etc.
578abb0f93cSkardel   */
579abb0f93cSkardel   if((pp->hour < 0) || (pp->hour > 23) ||
580abb0f93cSkardel      (pp->minute < 0) || (pp->minute > 59) ||
581abb0f93cSkardel      (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
582abb0f93cSkardel      (day < 1) || (day > 31) ||
583abb0f93cSkardel      (month < 1) || (month > 12) ||
584abb0f93cSkardel      (pp->year < 0) || (pp->year > 99)) {
585abb0f93cSkardel     /* Data out of range. */
586abb0f93cSkardel     NLOG(NLOG_CLOCKEVENT)
587abb0f93cSkardel       msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
588abb0f93cSkardel 	      up->unit, pp->a_lastcode);
589abb0f93cSkardel     refclock_report(peer, CEVNT_BADDATE);
590abb0f93cSkardel     return;
591abb0f93cSkardel   }
592abb0f93cSkardel 
593abb0f93cSkardel   /* Year-2000 check not needed anymore. Same problem
594abb0f93cSkardel    * will arise at 2099 but what should we do...?
595abb0f93cSkardel    *
596abb0f93cSkardel    * wrap 2-digit date into 4-digit
597abb0f93cSkardel    *
598abb0f93cSkardel    * if(pp->year < YEAR_PIVOT)
599abb0f93cSkardel    * {
600abb0f93cSkardel    *   pp->year += 100;
601abb0f93cSkardel    * }
602abb0f93cSkardel   */
603abb0f93cSkardel   pp->year += 2000;
604abb0f93cSkardel 
605abb0f93cSkardel   /* adjust NeoClock4X local time to UTC */
606abb0f93cSkardel   calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
607abb0f93cSkardel   calc_utc -= 3600;
608abb0f93cSkardel   /* adjust NeoClock4X daylight saving time if needed */
609abb0f93cSkardel   if('S' == up->dststatus)
610abb0f93cSkardel     calc_utc -= 3600;
611abb0f93cSkardel   neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
612abb0f93cSkardel 
613abb0f93cSkardel   /*
614abb0f93cSkardel     some preparations
615abb0f93cSkardel   */
616abb0f93cSkardel   pp->day = ymd2yd(pp->year, month, day);
617abb0f93cSkardel   pp->leap = 0;
618abb0f93cSkardel 
619abb0f93cSkardel   if(pp->sloppyclockflag & CLK_FLAG4)
620abb0f93cSkardel     {
621abb0f93cSkardel       msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
622abb0f93cSkardel 	      up->unit,
623abb0f93cSkardel 	      pp->year, month, day,
624abb0f93cSkardel 	      pp->hour, pp->minute, pp->second,
625abb0f93cSkardel #if defined(NTP_PRE_420)
626abb0f93cSkardel               pp->msec
627abb0f93cSkardel #else
628abb0f93cSkardel               pp->nsec/NSEC_TO_MILLI
629abb0f93cSkardel #endif
630abb0f93cSkardel               );
631abb0f93cSkardel     }
632abb0f93cSkardel 
633abb0f93cSkardel   up->utc_year   = pp->year;
634abb0f93cSkardel   up->utc_month  = month;
635abb0f93cSkardel   up->utc_day    = day;
636abb0f93cSkardel   up->utc_hour   = pp->hour;
637abb0f93cSkardel   up->utc_minute = pp->minute;
638abb0f93cSkardel   up->utc_second = pp->second;
639abb0f93cSkardel #if defined(NTP_PRE_420)
640abb0f93cSkardel   up->utc_msec   = pp->msec;
641abb0f93cSkardel #else
642abb0f93cSkardel   up->utc_msec   = pp->nsec/NSEC_TO_MILLI;
643abb0f93cSkardel #endif
644abb0f93cSkardel 
645abb0f93cSkardel   if(!refclock_process(pp))
646abb0f93cSkardel     {
647abb0f93cSkardel       NLOG(NLOG_CLOCKEVENT)
648abb0f93cSkardel 	msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
649abb0f93cSkardel       refclock_report(peer, CEVNT_FAULT);
650abb0f93cSkardel       return;
651abb0f93cSkardel     }
652abb0f93cSkardel   refclock_receive(peer);
653abb0f93cSkardel 
654abb0f93cSkardel   /* report good status */
655abb0f93cSkardel   refclock_report(peer, CEVNT_NOMINAL);
656abb0f93cSkardel 
657abb0f93cSkardel   record_clock_stats(&peer->srcadr, pp->a_lastcode);
658abb0f93cSkardel }
659abb0f93cSkardel 
660abb0f93cSkardel static void
neoclock4x_poll(int unit,struct peer * peer)661abb0f93cSkardel neoclock4x_poll(int unit,
662abb0f93cSkardel 		struct peer *peer)
663abb0f93cSkardel {
664abb0f93cSkardel   struct neoclock4x_unit *up;
665abb0f93cSkardel   struct refclockproc *pp;
666abb0f93cSkardel 
667abb0f93cSkardel   pp = peer->procptr;
668b3d6264cSchristos   up = pp->unitptr;
669abb0f93cSkardel 
670abb0f93cSkardel   pp->polls++;
671abb0f93cSkardel   up->recvnow = 1;
672abb0f93cSkardel }
673abb0f93cSkardel 
674abb0f93cSkardel static void
neoclock4x_control(int unit,const struct refclockstat * in,struct refclockstat * out,struct peer * peer)675abb0f93cSkardel neoclock4x_control(int unit,
676b3d6264cSchristos 		   const struct refclockstat *in,
677abb0f93cSkardel 		   struct refclockstat *out,
678abb0f93cSkardel 		   struct peer *peer)
679abb0f93cSkardel {
680abb0f93cSkardel   struct neoclock4x_unit *up;
681abb0f93cSkardel   struct refclockproc *pp;
682abb0f93cSkardel 
683abb0f93cSkardel   if(NULL == peer)
684abb0f93cSkardel     {
685abb0f93cSkardel       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
686abb0f93cSkardel       return;
687abb0f93cSkardel     }
688abb0f93cSkardel 
689abb0f93cSkardel   pp = peer->procptr;
690abb0f93cSkardel   if(NULL == pp)
691abb0f93cSkardel     {
692abb0f93cSkardel       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
693abb0f93cSkardel       return;
694abb0f93cSkardel     }
695abb0f93cSkardel 
696b3d6264cSchristos   up = pp->unitptr;
697abb0f93cSkardel   if(NULL == up)
698abb0f93cSkardel     {
699abb0f93cSkardel       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
700abb0f93cSkardel       return;
701abb0f93cSkardel     }
702abb0f93cSkardel 
703abb0f93cSkardel   if(NULL != in)
704abb0f93cSkardel     {
705abb0f93cSkardel       /* check to see if a user supplied time offset is given */
706abb0f93cSkardel       if(in->haveflags & CLK_HAVETIME1)
707abb0f93cSkardel 	{
708abb0f93cSkardel 	  pp->fudgetime1 = in->fudgetime1;
709abb0f93cSkardel 	  NLOG(NLOG_CLOCKINFO)
710abb0f93cSkardel 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
711abb0f93cSkardel 		    unit, pp->fudgetime1);
712abb0f93cSkardel 	}
713abb0f93cSkardel 
714abb0f93cSkardel       /* notify */
715abb0f93cSkardel       if(pp->sloppyclockflag & CLK_FLAG1)
716abb0f93cSkardel 	{
717abb0f93cSkardel 	  NLOG(NLOG_CLOCKINFO)
718abb0f93cSkardel 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
719abb0f93cSkardel 	}
720abb0f93cSkardel       else
721abb0f93cSkardel 	{
722abb0f93cSkardel 	  NLOG(NLOG_CLOCKINFO)
723abb0f93cSkardel 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
724abb0f93cSkardel 	}
725abb0f93cSkardel     }
726abb0f93cSkardel 
727abb0f93cSkardel   if(NULL != out)
728abb0f93cSkardel     {
729abb0f93cSkardel       char *tt;
730*f7d97a96Schristos       /* the 199 here is almost 2x the max string */
731f88c7923Smrg       char tmpbuf[199];
732abb0f93cSkardel 
733abb0f93cSkardel       out->kv_list = (struct ctl_var *)0;
734abb0f93cSkardel       out->type    = REFCLK_NEOCLOCK4X;
735abb0f93cSkardel 
736abb0f93cSkardel       snprintf(tmpbuf, sizeof(tmpbuf)-1,
737abb0f93cSkardel 	       "%04d-%02d-%02d %02d:%02d:%02d.%03d",
738abb0f93cSkardel 	       up->utc_year, up->utc_month, up->utc_day,
739abb0f93cSkardel 	       up->utc_hour, up->utc_minute, up->utc_second,
740abb0f93cSkardel 	       up->utc_msec);
741abb0f93cSkardel       tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
742abb0f93cSkardel       snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
743abb0f93cSkardel 
744abb0f93cSkardel       tt = add_var(&out->kv_list, 40, RO|DEF);
745abb0f93cSkardel       snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal);
746abb0f93cSkardel       tt = add_var(&out->kv_list, 40, RO|DEF);
747abb0f93cSkardel       snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1);
748abb0f93cSkardel       tt = add_var(&out->kv_list, 40, RO|DEF);
749abb0f93cSkardel       snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2);
750abb0f93cSkardel       tt = add_var(&out->kv_list, 40, RO|DEF);
751abb0f93cSkardel       if('A' == up->timesource)
752abb0f93cSkardel 	snprintf(tt, 39, "timesource=\"radio\"");
753abb0f93cSkardel       else if('C' == up->timesource)
754abb0f93cSkardel 	snprintf(tt, 39, "timesource=\"quartz\"");
755abb0f93cSkardel       else
756abb0f93cSkardel 	snprintf(tt, 39, "timesource=\"unknown\"");
757abb0f93cSkardel       tt = add_var(&out->kv_list, 40, RO|DEF);
758abb0f93cSkardel       if('I' == up->quarzstatus)
759abb0f93cSkardel 	snprintf(tt, 39, "quartzstatus=\"synchronized\"");
760abb0f93cSkardel       else if('X' == up->quarzstatus)
761abb0f93cSkardel         snprintf(tt, 39, "quartzstatus=\"not synchronized\"");
762abb0f93cSkardel       else
763abb0f93cSkardel 	snprintf(tt, 39, "quartzstatus=\"unknown\"");
764abb0f93cSkardel       tt = add_var(&out->kv_list, 40, RO|DEF);
765abb0f93cSkardel       if('S' == up->dststatus)
766abb0f93cSkardel         snprintf(tt, 39, "dststatus=\"summer\"");
767abb0f93cSkardel       else if('W' == up->dststatus)
768abb0f93cSkardel         snprintf(tt, 39, "dststatus=\"winter\"");
769abb0f93cSkardel       else
770abb0f93cSkardel         snprintf(tt, 39, "dststatus=\"unknown\"");
771*f7d97a96Schristos       /* the 99 below is greater than 80 the max string */
772abb0f93cSkardel       tt = add_var(&out->kv_list, 80, RO|DEF);
773f88c7923Smrg       snprintf(tt, 99, "firmware=\"%s\"", up->firmware);
774abb0f93cSkardel       tt = add_var(&out->kv_list, 40, RO|DEF);
775abb0f93cSkardel       snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag);
776abb0f93cSkardel       tt = add_var(&out->kv_list, 80, RO|DEF);
777f88c7923Smrg       snprintf(tt, 99, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION);
778abb0f93cSkardel       tt = add_var(&out->kv_list, 80, RO|DEF);
779f88c7923Smrg       snprintf(tt, 99, "serialnumber=\"%s\"", up->serial);
780abb0f93cSkardel     }
781abb0f93cSkardel }
782abb0f93cSkardel 
783abb0f93cSkardel static int
neol_hexatoi_len(const char str[],int * result,int maxlen)784abb0f93cSkardel neol_hexatoi_len(const char str[],
785abb0f93cSkardel 		 int *result,
786abb0f93cSkardel 		 int maxlen)
787abb0f93cSkardel {
788abb0f93cSkardel   int hexdigit;
789abb0f93cSkardel   int i;
790abb0f93cSkardel   int n = 0;
791abb0f93cSkardel 
79210afd409Schristos   for(i=0; isxdigit((unsigned char)str[i]) && i < maxlen; i++)
793abb0f93cSkardel     {
79410afd409Schristos       hexdigit = isdigit((unsigned char)str[i]) ? toupper((unsigned char)str[i]) - '0' : toupper((unsigned char)str[i]) - 'A' + 10;
795abb0f93cSkardel       n = 16 * n + hexdigit;
796abb0f93cSkardel     }
797abb0f93cSkardel   *result = n;
798abb0f93cSkardel   return (n);
799abb0f93cSkardel }
800abb0f93cSkardel 
801abb0f93cSkardel static int
neol_atoi_len(const char str[],int * result,int maxlen)802abb0f93cSkardel neol_atoi_len(const char str[],
803abb0f93cSkardel 		  int *result,
804abb0f93cSkardel 		  int maxlen)
805abb0f93cSkardel {
806abb0f93cSkardel   int digit;
807abb0f93cSkardel   int i;
808abb0f93cSkardel   int n = 0;
809abb0f93cSkardel 
81010afd409Schristos   for(i=0; isdigit((unsigned char)str[i]) && i < maxlen; i++)
811abb0f93cSkardel     {
812abb0f93cSkardel       digit = str[i] - '0';
813abb0f93cSkardel       n = 10 * n + digit;
814abb0f93cSkardel     }
815abb0f93cSkardel   *result = n;
816abb0f93cSkardel   return (n);
817abb0f93cSkardel }
818abb0f93cSkardel 
819abb0f93cSkardel /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
820abb0f93cSkardel  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
821abb0f93cSkardel  * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
822abb0f93cSkardel  *
823abb0f93cSkardel  * [For the Julian calendar (which was used in Russia before 1917,
824abb0f93cSkardel  * Britain & colonies before 1752, anywhere else before 1582,
825abb0f93cSkardel  * and is still in use by some communities) leave out the
826abb0f93cSkardel  * -year/100+year/400 terms, and add 10.]
827abb0f93cSkardel  *
828abb0f93cSkardel  * This algorithm was first published by Gauss (I think).
829abb0f93cSkardel  *
830abb0f93cSkardel  * WARNING: this function will overflow on 2106-02-07 06:28:16 on
831abb0f93cSkardel  * machines were long is 32-bit! (However, as time_t is signed, we
832abb0f93cSkardel  * will already get problems at other places on 2038-01-19 03:14:08)
833abb0f93cSkardel  */
834abb0f93cSkardel static unsigned long
neol_mktime(int year,int mon,int day,int hour,int min,int sec)835abb0f93cSkardel neol_mktime(int year,
836abb0f93cSkardel 	    int mon,
837abb0f93cSkardel 	    int day,
838abb0f93cSkardel 	    int hour,
839abb0f93cSkardel 	    int min,
840abb0f93cSkardel 	    int sec)
841abb0f93cSkardel {
842abb0f93cSkardel   if (0 >= (int) (mon -= 2)) {    /* 1..12 . 11,12,1..10 */
843abb0f93cSkardel     mon += 12;      /* Puts Feb last since it has leap day */
844abb0f93cSkardel     year -= 1;
845abb0f93cSkardel   }
846abb0f93cSkardel   return (((
847abb0f93cSkardel             (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
848abb0f93cSkardel             year*365 - 719499
849abb0f93cSkardel             )*24 + hour /* now have hours */
850abb0f93cSkardel            )*60 + min /* now have minutes */
851abb0f93cSkardel           )*60 + sec; /* finally seconds */
852abb0f93cSkardel }
853abb0f93cSkardel 
854abb0f93cSkardel static void
neol_localtime(unsigned long utc,int * year,int * month,int * day,int * hour,int * min,int * sec)855abb0f93cSkardel neol_localtime(unsigned long utc,
856abb0f93cSkardel 	       int* year,
857abb0f93cSkardel 	       int* month,
858abb0f93cSkardel 	       int* day,
859abb0f93cSkardel 	       int* hour,
860abb0f93cSkardel 	       int* min,
861abb0f93cSkardel 	       int* sec)
862abb0f93cSkardel {
863abb0f93cSkardel   *sec = utc % 60;
864abb0f93cSkardel   utc /= 60;
865abb0f93cSkardel   *min = utc % 60;
866abb0f93cSkardel   utc /= 60;
867abb0f93cSkardel   *hour = utc % 24;
868abb0f93cSkardel   utc /= 24;
869abb0f93cSkardel 
870abb0f93cSkardel   /*             JDN Date 1/1/1970 */
871abb0f93cSkardel   neol_jdn_to_ymd(utc + 2440588L, year, month, day);
872abb0f93cSkardel }
873abb0f93cSkardel 
874abb0f93cSkardel static void
neol_jdn_to_ymd(unsigned long jdn,int * yy,int * mm,int * dd)875abb0f93cSkardel neol_jdn_to_ymd(unsigned long jdn,
876abb0f93cSkardel 		int *yy,
877abb0f93cSkardel 		int *mm,
878abb0f93cSkardel 		int *dd)
879abb0f93cSkardel {
880abb0f93cSkardel   unsigned long x, z, m, d, y;
881abb0f93cSkardel   unsigned long daysPer400Years = 146097UL;
882abb0f93cSkardel   unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
883abb0f93cSkardel 
884abb0f93cSkardel   x = jdn + 68569UL;
885abb0f93cSkardel   z = 4UL * x / daysPer400Years;
886abb0f93cSkardel   x = x - (daysPer400Years * z + 3UL) / 4UL;
887abb0f93cSkardel   y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
888abb0f93cSkardel   x = x - 1461UL * y / 4UL + 31UL;
889abb0f93cSkardel   m = 80UL * x / 2447UL;
890abb0f93cSkardel   d = x - 2447UL * m / 80UL;
891abb0f93cSkardel   x = m / 11UL;
892abb0f93cSkardel   m = m + 2UL - 12UL * x;
893abb0f93cSkardel   y = 100UL * (z - 49UL) + y + x;
894abb0f93cSkardel 
895abb0f93cSkardel   *yy = (int)y;
896abb0f93cSkardel   *mm = (int)m;
897abb0f93cSkardel   *dd = (int)d;
898abb0f93cSkardel }
899abb0f93cSkardel 
900abb0f93cSkardel #if !defined(NEOCLOCK4X_FIRMWARE)
901abb0f93cSkardel static int
neol_query_firmware(int fd,int unit,char * firmware,size_t maxlen)902abb0f93cSkardel neol_query_firmware(int fd,
903abb0f93cSkardel 		    int unit,
904abb0f93cSkardel 		    char *firmware,
905b3d6264cSchristos 		    size_t maxlen)
906abb0f93cSkardel {
907abb0f93cSkardel   char tmpbuf[256];
908e19314b7Schristos   size_t len;
909abb0f93cSkardel   int lastsearch;
910abb0f93cSkardel   unsigned char c;
911abb0f93cSkardel   int last_c_was_crlf;
912abb0f93cSkardel   int last_crlf_conv_len;
913abb0f93cSkardel   int init;
914abb0f93cSkardel   int read_errors;
915abb0f93cSkardel   int flag = 0;
916abb0f93cSkardel   int chars_read;
917abb0f93cSkardel 
918abb0f93cSkardel   /* wait a little bit */
919abb0f93cSkardel   sleep(1);
920abb0f93cSkardel   if(-1 != write(fd, "V", 1))
921abb0f93cSkardel     {
922abb0f93cSkardel       /* wait a little bit */
923abb0f93cSkardel       sleep(1);
924abb0f93cSkardel       memset(tmpbuf, 0x00, sizeof(tmpbuf));
925abb0f93cSkardel 
926abb0f93cSkardel       len = 0;
927abb0f93cSkardel       lastsearch = 0;
928abb0f93cSkardel       last_c_was_crlf = 0;
929abb0f93cSkardel       last_crlf_conv_len = 0;
930abb0f93cSkardel       init = 1;
931abb0f93cSkardel       read_errors = 0;
932abb0f93cSkardel       chars_read = 0;
933abb0f93cSkardel       for(;;)
934abb0f93cSkardel 	{
935abb0f93cSkardel 	  if(read_errors > 5)
936abb0f93cSkardel 	    {
937abb0f93cSkardel 	      msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
938b3d6264cSchristos 	      strlcpy(tmpbuf, "unknown due to timeout", sizeof(tmpbuf));
939abb0f93cSkardel 	      break;
940abb0f93cSkardel 	    }
941abb0f93cSkardel           if(chars_read > 500)
942abb0f93cSkardel             {
943abb0f93cSkardel 	      msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
944b3d6264cSchristos 	      strlcpy(tmpbuf, "unknown due to garbage input", sizeof(tmpbuf));
945abb0f93cSkardel 	      break;
946abb0f93cSkardel             }
947abb0f93cSkardel 	  if(-1 == read(fd, &c, 1))
948abb0f93cSkardel 	    {
949abb0f93cSkardel               if(EAGAIN != errno)
950abb0f93cSkardel                 {
951b3d6264cSchristos                   msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %m", unit);
952abb0f93cSkardel                   read_errors++;
953abb0f93cSkardel                 }
954abb0f93cSkardel               else
955abb0f93cSkardel                 {
956abb0f93cSkardel                   sleep(1);
957abb0f93cSkardel                 }
958abb0f93cSkardel 	      continue;
959abb0f93cSkardel 	    }
960abb0f93cSkardel           else
961abb0f93cSkardel             {
962abb0f93cSkardel               chars_read++;
963abb0f93cSkardel             }
964abb0f93cSkardel 
965abb0f93cSkardel 	  if(init)
966abb0f93cSkardel 	    {
967abb0f93cSkardel 	      if(0xA9 != c) /* wait for (c) char in input stream */
968abb0f93cSkardel 		continue;
969abb0f93cSkardel 
970b3d6264cSchristos 	      strlcpy(tmpbuf, "(c)", sizeof(tmpbuf));
971abb0f93cSkardel 	      len = 3;
972abb0f93cSkardel 	      init = 0;
973abb0f93cSkardel 	      continue;
974abb0f93cSkardel 	    }
975abb0f93cSkardel 
976abb0f93cSkardel #if 0
977abb0f93cSkardel 	  msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
978abb0f93cSkardel #endif
979abb0f93cSkardel 
980abb0f93cSkardel 	  if(0x0A == c || 0x0D == c)
981abb0f93cSkardel 	    {
982abb0f93cSkardel 	      if(last_c_was_crlf)
983abb0f93cSkardel 		{
984abb0f93cSkardel 		  char *ptr;
985abb0f93cSkardel 		  ptr = strstr(&tmpbuf[lastsearch], "S/N");
986abb0f93cSkardel 		  if(NULL != ptr)
987abb0f93cSkardel 		    {
988abb0f93cSkardel 		      tmpbuf[last_crlf_conv_len] = 0;
989abb0f93cSkardel 		      flag = 1;
990abb0f93cSkardel 		      break;
991abb0f93cSkardel 		    }
992abb0f93cSkardel 		  /* convert \n to / */
993abb0f93cSkardel 		  last_crlf_conv_len = len;
994abb0f93cSkardel 		  tmpbuf[len++] = ' ';
995abb0f93cSkardel 		  tmpbuf[len++] = '/';
996abb0f93cSkardel 		  tmpbuf[len++] = ' ';
997abb0f93cSkardel 		  lastsearch = len;
998abb0f93cSkardel 		}
999abb0f93cSkardel 	      last_c_was_crlf = 1;
1000abb0f93cSkardel 	    }
1001abb0f93cSkardel 	  else
1002abb0f93cSkardel 	    {
1003abb0f93cSkardel 	      last_c_was_crlf = 0;
1004abb0f93cSkardel 	      if(0x00 != c)
1005abb0f93cSkardel 		tmpbuf[len++] = (char) c;
1006abb0f93cSkardel 	    }
1007abb0f93cSkardel 	  tmpbuf[len] = '\0';
1008abb0f93cSkardel 	  if (len > sizeof(tmpbuf)-5)
1009abb0f93cSkardel 	    break;
1010abb0f93cSkardel 	}
1011abb0f93cSkardel     }
1012abb0f93cSkardel   else
1013abb0f93cSkardel     {
1014abb0f93cSkardel       msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
1015b3d6264cSchristos       strlcpy(tmpbuf, "unknown error", sizeof(tmpbuf));
1016abb0f93cSkardel     }
1017b3d6264cSchristos   if (strlcpy(firmware, tmpbuf, maxlen) >= maxlen)
1018b3d6264cSchristos     strlcpy(firmware, "buffer too small", maxlen);
1019abb0f93cSkardel 
1020abb0f93cSkardel   if(flag)
1021abb0f93cSkardel     {
1022abb0f93cSkardel       NLOG(NLOG_CLOCKINFO)
1023abb0f93cSkardel 	msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
102445530cf1Skardel 
102545530cf1Skardel       if(strstr(firmware, "/R2"))
102645530cf1Skardel 	{
102745530cf1Skardel 	  msyslog(LOG_INFO, "NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit);
102845530cf1Skardel 	}
102945530cf1Skardel 
1030abb0f93cSkardel     }
1031abb0f93cSkardel 
1032abb0f93cSkardel   return (flag);
1033abb0f93cSkardel }
1034abb0f93cSkardel 
1035abb0f93cSkardel static int
neol_check_firmware(int unit,const char * firmware,char * firmwaretag)1036abb0f93cSkardel neol_check_firmware(int unit,
1037abb0f93cSkardel                     const char *firmware,
1038abb0f93cSkardel                     char *firmwaretag)
1039abb0f93cSkardel {
1040abb0f93cSkardel   char *ptr;
1041abb0f93cSkardel 
1042abb0f93cSkardel   *firmwaretag = '?';
1043abb0f93cSkardel   ptr = strstr(firmware, "NDF:");
1044abb0f93cSkardel   if(NULL != ptr)
1045abb0f93cSkardel     {
1046abb0f93cSkardel       if((strlen(firmware) - strlen(ptr)) >= 7)
1047abb0f93cSkardel         {
1048abb0f93cSkardel           if(':' == *(ptr+5) && '*' == *(ptr+6))
1049abb0f93cSkardel             *firmwaretag = *(ptr+4);
1050abb0f93cSkardel         }
1051abb0f93cSkardel     }
1052abb0f93cSkardel 
1053abb0f93cSkardel   if('A' != *firmwaretag)
1054abb0f93cSkardel     {
1055abb0f93cSkardel       msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
1056abb0f93cSkardel       return (0);
1057abb0f93cSkardel     }
1058abb0f93cSkardel 
1059abb0f93cSkardel   return (1);
1060abb0f93cSkardel }
1061abb0f93cSkardel #endif
1062abb0f93cSkardel 
1063abb0f93cSkardel #else
1064abb0f93cSkardel int refclock_neoclock4x_bs;
1065abb0f93cSkardel #endif /* REFCLOCK */
1066abb0f93cSkardel 
1067abb0f93cSkardel /*
1068abb0f93cSkardel  * History:
1069abb0f93cSkardel  * refclock_neoclock4x.c
1070abb0f93cSkardel  *
1071abb0f93cSkardel  * 2002/04/27 cjh
1072abb0f93cSkardel  * Revision 1.0  first release
1073abb0f93cSkardel  *
1074abb0f93cSkardel  * 2002/07/15 cjh
1075abb0f93cSkardel  * preparing for bitkeeper reposity
1076abb0f93cSkardel  *
1077abb0f93cSkardel  * 2002/09/09 cjh
1078abb0f93cSkardel  * Revision 1.1
1079abb0f93cSkardel  * - don't assume sprintf returns an int anymore
1080abb0f93cSkardel  * - change the way the firmware version is read
1081abb0f93cSkardel  * - some customers would like to put a device called
1082abb0f93cSkardel  *   data diode to the NeoClock4X device to disable
1083abb0f93cSkardel  *   the write line. We need to now the firmware
1084abb0f93cSkardel  *   version even in this case. We made a compile time
1085abb0f93cSkardel  *   definition in this case. The code was previously
1086abb0f93cSkardel  *   only available on request.
1087abb0f93cSkardel  *
1088abb0f93cSkardel  * 2003/01/08 cjh
1089abb0f93cSkardel  * Revision 1.11
1090abb0f93cSkardel  * - changing xprinf to xnprinf to avoid buffer overflows
1091abb0f93cSkardel  * - change some logic
1092abb0f93cSkardel  * - fixed memory leaks if drivers can't initialize
1093abb0f93cSkardel  *
1094abb0f93cSkardel  * 2003/01/10 cjh
1095abb0f93cSkardel  * Revision 1.12
1096abb0f93cSkardel  * - replaced ldiv
1097abb0f93cSkardel  * - add code to support FreeBSD
1098abb0f93cSkardel  *
1099abb0f93cSkardel  * 2003/07/07 cjh
1100abb0f93cSkardel  * Revision 1.13
1101abb0f93cSkardel  * - fix reporting of clock status
1102abb0f93cSkardel  *   changes. previously a bad clock
1103abb0f93cSkardel  *   status was never reset.
1104abb0f93cSkardel  *
1105abb0f93cSkardel  * 2004/04/07 cjh
1106abb0f93cSkardel  * Revision 1.14
1107abb0f93cSkardel  * - open serial port in a way
1108abb0f93cSkardel  *   AIX and some other OS can
1109abb0f93cSkardel  *   handle much better
1110abb0f93cSkardel  *
1111abb0f93cSkardel  * 2006/01/11 cjh
1112abb0f93cSkardel  * Revision 1.15
1113abb0f93cSkardel  * - remove some unsued #ifdefs
1114abb0f93cSkardel  * - fix nsec calculation, closes #499
1115abb0f93cSkardel  *
111645530cf1Skardel  * 2009/12/04 cjh
111745530cf1Skardel  * Revision 1.16
111845530cf1Skardel  * - change license to ntp COPYRIGHT notice. This should allow Debian
111945530cf1Skardel  *   to add this refclock driver in further releases.
112045530cf1Skardel  * - detect R2 hardware
112145530cf1Skardel  *
1122abb0f93cSkardel  */
112345530cf1Skardel 
112445530cf1Skardel 
112545530cf1Skardel 
112645530cf1Skardel 
112745530cf1Skardel 
112845530cf1Skardel 
1129