1106424Sroberto/*
2106424Sroberto *
3132451Sroberto * Refclock_neoclock4x.c
4106424Sroberto * - NeoClock4X driver for DCF77 or FIA Timecode
5106424Sroberto *
6285612Sdelphij * Date: 2009-12-04 v1.16
7106424Sroberto *
8106424Sroberto * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir
9106424Sroberto * for details about the NeoClock4X device
10106424Sroberto *
11106424Sroberto */
12106424Sroberto
13106424Sroberto#ifdef HAVE_CONFIG_H
14106424Sroberto# include "config.h"
15106424Sroberto#endif
16106424Sroberto
17106424Sroberto#if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
18106424Sroberto
19106424Sroberto#include <unistd.h>
20106424Sroberto#include <sys/time.h>
21106424Sroberto#include <sys/types.h>
22106424Sroberto#include <termios.h>
23106424Sroberto#include <sys/ioctl.h>
24106424Sroberto#include <ctype.h>
25106424Sroberto
26106424Sroberto#include "ntpd.h"
27106424Sroberto#include "ntp_io.h"
28106424Sroberto#include "ntp_control.h"
29106424Sroberto#include "ntp_refclock.h"
30106424Sroberto#include "ntp_unixtime.h"
31106424Sroberto#include "ntp_stdlib.h"
32106424Sroberto
33106424Sroberto#if defined HAVE_SYS_MODEM_H
34106424Sroberto# include <sys/modem.h>
35182007Sroberto# ifndef __QNXNTO__
36182007Sroberto#  define TIOCMSET MCSETA
37182007Sroberto#  define TIOCMGET MCGETA
38182007Sroberto#  define TIOCM_RTS MRTS
39182007Sroberto# endif
40106424Sroberto#endif
41106424Sroberto
42106424Sroberto#ifdef HAVE_TERMIOS_H
43106424Sroberto# ifdef TERMIOS_NEEDS__SVID3
44106424Sroberto#  define _SVID3
45106424Sroberto# endif
46106424Sroberto# include <termios.h>
47106424Sroberto# ifdef TERMIOS_NEEDS__SVID3
48106424Sroberto#  undef _SVID3
49106424Sroberto# endif
50106424Sroberto#endif
51106424Sroberto
52106424Sroberto#ifdef HAVE_SYS_IOCTL_H
53106424Sroberto# include <sys/ioctl.h>
54106424Sroberto#endif
55106424Sroberto
56132451Sroberto/*
57182007Sroberto * NTP version 4.20 change the pp->msec field to pp->nsec.
58182007Sroberto * To allow to support older ntp versions with this sourcefile
59182007Sroberto * you can define NTP_PRE_420 to allow this driver to compile
60182007Sroberto * with ntp version back to 4.1.2.
61182007Sroberto *
62182007Sroberto */
63182007Sroberto#if 0
64182007Sroberto#define NTP_PRE_420
65182007Sroberto#endif
66182007Sroberto
67182007Sroberto/*
68132451Sroberto * If you want the driver for whatever reason to not use
69132451Sroberto * the TX line to send anything to your NeoClock4X
70132451Sroberto * device you must tell the NTP refclock driver which
71132451Sroberto * firmware you NeoClock4X device uses.
72132451Sroberto *
73132451Sroberto * If you want to enable this feature change the "#if 0"
74132451Sroberto * line to "#if 1" and make sure that the defined firmware
75132451Sroberto * matches the firmware off your NeoClock4X receiver!
76132451Sroberto *
77132451Sroberto */
78132451Sroberto
79132451Sroberto#if 0
80132451Sroberto#define NEOCLOCK4X_FIRMWARE                NEOCLOCK4X_FIRMWARE_VERSION_A
81132451Sroberto#endif
82132451Sroberto
83182007Sroberto/* at this time only firmware version A is known */
84132451Sroberto#define NEOCLOCK4X_FIRMWARE_VERSION_A      'A'
85132451Sroberto
86106424Sroberto#define NEOCLOCK4X_TIMECODELEN 37
87106424Sroberto
88106424Sroberto#define NEOCLOCK4X_OFFSET_SERIAL            3
89106424Sroberto#define NEOCLOCK4X_OFFSET_RADIOSIGNAL       9
90106424Sroberto#define NEOCLOCK4X_OFFSET_DAY              12
91106424Sroberto#define NEOCLOCK4X_OFFSET_MONTH            14
92106424Sroberto#define NEOCLOCK4X_OFFSET_YEAR             16
93106424Sroberto#define NEOCLOCK4X_OFFSET_HOUR             18
94106424Sroberto#define NEOCLOCK4X_OFFSET_MINUTE           20
95106424Sroberto#define NEOCLOCK4X_OFFSET_SECOND           22
96106424Sroberto#define NEOCLOCK4X_OFFSET_HSEC             24
97106424Sroberto#define NEOCLOCK4X_OFFSET_DOW              26
98106424Sroberto#define NEOCLOCK4X_OFFSET_TIMESOURCE       28
99106424Sroberto#define NEOCLOCK4X_OFFSET_DSTSTATUS        29
100106424Sroberto#define NEOCLOCK4X_OFFSET_QUARZSTATUS      30
101106424Sroberto#define NEOCLOCK4X_OFFSET_ANTENNA1         31
102106424Sroberto#define NEOCLOCK4X_OFFSET_ANTENNA2         33
103106424Sroberto#define NEOCLOCK4X_OFFSET_CRC              35
104106424Sroberto
105285612Sdelphij#define NEOCLOCK4X_DRIVER_VERSION          "1.16 (2009-12-04)"
106132451Sroberto
107182007Sroberto#define NSEC_TO_MILLI                      1000000
108182007Sroberto
109106424Srobertostruct neoclock4x_unit {
110106424Sroberto  l_fp	laststamp;	/* last receive timestamp */
111106424Sroberto  short	unit;		/* NTP refclock unit number */
112132451Sroberto  u_long polled;	/* flag to detect noreplies */
113106424Sroberto  char	leap_status;	/* leap second flag */
114106424Sroberto  int	recvnow;
115132451Sroberto
116106424Sroberto  char  firmware[80];
117132451Sroberto  char  firmwaretag;
118106424Sroberto  char  serial[7];
119106424Sroberto  char  radiosignal[4];
120106424Sroberto  char  timesource;
121106424Sroberto  char  dststatus;
122106424Sroberto  char  quarzstatus;
123106424Sroberto  int   antenna1;
124106424Sroberto  int   antenna2;
125106424Sroberto  int   utc_year;
126106424Sroberto  int   utc_month;
127106424Sroberto  int   utc_day;
128106424Sroberto  int   utc_hour;
129106424Sroberto  int   utc_minute;
130106424Sroberto  int   utc_second;
131106424Sroberto  int   utc_msec;
132106424Sroberto};
133106424Sroberto
134285612Sdelphijstatic	int	neoclock4x_start	(int, struct peer *);
135285612Sdelphijstatic	void	neoclock4x_shutdown	(int, struct peer *);
136285612Sdelphijstatic	void	neoclock4x_receive	(struct recvbuf *);
137285612Sdelphijstatic	void	neoclock4x_poll		(int, struct peer *);
138285612Sdelphijstatic	void	neoclock4x_control	(int, const struct refclockstat *, struct refclockstat *, struct peer *);
139106424Sroberto
140285612Sdelphijstatic int	neol_atoi_len		(const char str[], int *, int);
141285612Sdelphijstatic int	neol_hexatoi_len	(const char str[], int *, int);
142285612Sdelphijstatic void	neol_jdn_to_ymd		(unsigned long, int *, int *, int *);
143285612Sdelphijstatic void	neol_localtime		(unsigned long, int* , int*, int*, int*, int*, int*);
144285612Sdelphijstatic unsigned long neol_mktime	(int, int, int, int, int, int);
145132451Sroberto#if !defined(NEOCLOCK4X_FIRMWARE)
146285612Sdelphijstatic int	neol_query_firmware	(int, int, char *, size_t);
147285612Sdelphijstatic int	neol_check_firmware	(int, const char*, char *);
148132451Sroberto#endif
149106424Sroberto
150106424Srobertostruct refclock refclock_neoclock4x = {
151106424Sroberto  neoclock4x_start,	/* start up driver */
152106424Sroberto  neoclock4x_shutdown,	/* shut down driver */
153106424Sroberto  neoclock4x_poll,	/* transmit poll message */
154106424Sroberto  neoclock4x_control,
155106424Sroberto  noentry,		/* initialize driver (not used) */
156106424Sroberto  noentry,		/* not used */
157106424Sroberto  NOFLAGS			/* not used */
158106424Sroberto};
159106424Sroberto
160106424Srobertostatic int
161106424Srobertoneoclock4x_start(int unit,
162106424Sroberto		 struct peer *peer)
163106424Sroberto{
164106424Sroberto  struct neoclock4x_unit *up;
165106424Sroberto  struct refclockproc *pp;
166106424Sroberto  int fd;
167106424Sroberto  char dev[20];
168106424Sroberto  int sl232;
169132451Sroberto#if defined(HAVE_TERMIOS)
170106424Sroberto  struct termios termsettings;
171132451Sroberto#endif
172132451Sroberto#if !defined(NEOCLOCK4X_FIRMWARE)
173106424Sroberto  int tries;
174132451Sroberto#endif
175106424Sroberto
176132451Sroberto  (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit);
177132451Sroberto
178106424Sroberto  /* LDISC_STD, LDISC_RAW
179106424Sroberto   * Open serial port. Use CLK line discipline, if available.
180106424Sroberto   */
181182007Sroberto  fd = refclock_open(dev, B2400, LDISC_STD);
182106424Sroberto  if(fd <= 0)
183106424Sroberto    {
184106424Sroberto      return (0);
185106424Sroberto    }
186132451Sroberto
187132451Sroberto#if defined(HAVE_TERMIOS)
188182007Sroberto
189182007Sroberto#if 1
190132451Sroberto  if(tcgetattr(fd, &termsettings) < 0)
191132451Sroberto    {
192132451Sroberto      msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
193132451Sroberto      (void) close(fd);
194132451Sroberto      return (0);
195132451Sroberto    }
196132451Sroberto
197132451Sroberto  /* 2400 Baud 8N2 */
198182007Sroberto  termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL;
199182007Sroberto  termsettings.c_oflag = 0;
200182007Sroberto  termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD;
201182007Sroberto  (void)cfsetispeed(&termsettings, (u_int)B2400);
202182007Sroberto  (void)cfsetospeed(&termsettings, (u_int)B2400);
203182007Sroberto
204182007Sroberto  if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
205182007Sroberto    {
206182007Sroberto      msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
207182007Sroberto      (void) close(fd);
208182007Sroberto      return (0);
209182007Sroberto    }
210182007Sroberto
211182007Sroberto#else
212182007Sroberto  if(tcgetattr(fd, &termsettings) < 0)
213182007Sroberto    {
214182007Sroberto      msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
215182007Sroberto      (void) close(fd);
216182007Sroberto      return (0);
217182007Sroberto    }
218182007Sroberto
219182007Sroberto  /* 2400 Baud 8N2 */
220132451Sroberto  termsettings.c_cflag &= ~PARENB;
221132451Sroberto  termsettings.c_cflag |= CSTOPB;
222132451Sroberto  termsettings.c_cflag &= ~CSIZE;
223132451Sroberto  termsettings.c_cflag |= CS8;
224132451Sroberto
225132451Sroberto  if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
226132451Sroberto    {
227132451Sroberto      msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
228132451Sroberto      (void) close(fd);
229132451Sroberto      return (0);
230132451Sroberto    }
231182007Sroberto#endif
232182007Sroberto
233132451Sroberto#elif defined(HAVE_SYSV_TTYS)
234132451Sroberto  if(ioctl(fd, TCGETA, &termsettings) < 0)
235132451Sroberto    {
236132451Sroberto      msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit);
237132451Sroberto      (void) close(fd);
238132451Sroberto      return (0);
239132451Sroberto    }
240132451Sroberto
241132451Sroberto  /* 2400 Baud 8N2 */
242132451Sroberto  termsettings.c_cflag &= ~PARENB;
243132451Sroberto  termsettings.c_cflag |= CSTOPB;
244132451Sroberto  termsettings.c_cflag &= ~CSIZE;
245132451Sroberto  termsettings.c_cflag |= CS8;
246132451Sroberto
247132451Sroberto  if(ioctl(fd, TCSETA, &termsettings) < 0)
248132451Sroberto    {
249132451Sroberto      msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit);
250132451Sroberto      (void) close(fd);
251132451Sroberto      return (0);
252132451Sroberto    }
253132451Sroberto#else
254132451Sroberto  msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
255132451Sroberto  (void) close(fd);
256132451Sroberto  return (0);
257132451Sroberto#endif
258132451Sroberto
259106424Sroberto#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
260106424Sroberto  /* turn on RTS, and DTR for power supply */
261106424Sroberto  /* NeoClock4x is powered from serial line */
262106424Sroberto  if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
263106424Sroberto    {
264106424Sroberto      msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
265132451Sroberto      (void) close(fd);
266132451Sroberto      return (0);
267106424Sroberto    }
268106424Sroberto#ifdef TIOCM_RTS
269106424Sroberto  sl232 = sl232 | TIOCM_DTR | TIOCM_RTS;	/* turn on RTS, and DTR for power supply */
270106424Sroberto#else
271106424Sroberto  sl232 = sl232 | CIOCM_DTR | CIOCM_RTS;	/* turn on RTS, and DTR for power supply */
272106424Sroberto#endif
273106424Sroberto  if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
274106424Sroberto    {
275106424Sroberto      msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
276132451Sroberto      (void) close(fd);
277132451Sroberto      return (0);
278106424Sroberto    }
279106424Sroberto#else
280132451Sroberto  msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
281106424Sroberto	  unit);
282132451Sroberto  (void) close(fd);
283132451Sroberto  return (0);
284106424Sroberto#endif
285132451Sroberto
286106424Sroberto  up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
287106424Sroberto  if(!(up))
288106424Sroberto    {
289106424Sroberto      msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
290106424Sroberto      (void) close(fd);
291106424Sroberto      return (0);
292106424Sroberto    }
293106424Sroberto
294106424Sroberto  memset((char *)up, 0, sizeof(struct neoclock4x_unit));
295106424Sroberto  pp = peer->procptr;
296106424Sroberto  pp->clockdesc = "NeoClock4X";
297285612Sdelphij  pp->unitptr = up;
298106424Sroberto  pp->io.clock_recv = neoclock4x_receive;
299285612Sdelphij  pp->io.srcclock = peer;
300106424Sroberto  pp->io.datalen = 0;
301106424Sroberto  pp->io.fd = fd;
302132451Sroberto  /*
303132451Sroberto   * no fudge time is given by user!
304132451Sroberto   * use 169.583333 ms to compensate the serial line delay
305106424Sroberto   * formula is:
306106424Sroberto   * 2400 Baud / 11 bit = 218.18 charaters per second
307106424Sroberto   *  (NeoClock4X timecode len)
308106424Sroberto   */
309106424Sroberto  pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
310106424Sroberto
311106424Sroberto  /*
312106424Sroberto   * Initialize miscellaneous variables
313106424Sroberto   */
314106424Sroberto  peer->precision = -10;
315106424Sroberto  memcpy((char *)&pp->refid, "neol", 4);
316132451Sroberto
317106424Sroberto  up->leap_status = 0;
318106424Sroberto  up->unit = unit;
319285612Sdelphij  strlcpy(up->firmware, "?", sizeof(up->firmware));
320132451Sroberto  up->firmwaretag = '?';
321285612Sdelphij  strlcpy(up->serial, "?", sizeof(up->serial));
322285612Sdelphij  strlcpy(up->radiosignal, "?", sizeof(up->radiosignal));
323106424Sroberto  up->timesource  = '?';
324106424Sroberto  up->dststatus   = '?';
325106424Sroberto  up->quarzstatus = '?';
326106424Sroberto  up->antenna1    = -1;
327106424Sroberto  up->antenna2    = -1;
328106424Sroberto  up->utc_year    = 0;
329106424Sroberto  up->utc_month   = 0;
330106424Sroberto  up->utc_day     = 0;
331106424Sroberto  up->utc_hour    = 0;
332106424Sroberto  up->utc_minute  = 0;
333106424Sroberto  up->utc_second  = 0;
334106424Sroberto  up->utc_msec    = 0;
335106424Sroberto
336132451Sroberto#if defined(NEOCLOCK4X_FIRMWARE)
337132451Sroberto#if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
338285612Sdelphij  strlcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)",
339285612Sdelphij	  sizeof(up->firmware));
340132451Sroberto  up->firmwaretag = 'A';
341132451Sroberto#else
342182007Sroberto  msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
343132451Sroberto	  unit);
344132451Sroberto  (void) close(fd);
345132451Sroberto  pp->io.fd = -1;
346132451Sroberto  free(pp->unitptr);
347132451Sroberto  pp->unitptr = NULL;
348132451Sroberto  return (0);
349132451Sroberto#endif
350132451Sroberto#else
351106424Sroberto  for(tries=0; tries < 5; tries++)
352106424Sroberto    {
353106424Sroberto      NLOG(NLOG_CLOCKINFO)
354132451Sroberto	msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
355132451Sroberto      /* wait 3 seconds for receiver to power up */
356106424Sroberto      sleep(3);
357106424Sroberto      if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
358106424Sroberto	{
359106424Sroberto	  break;
360106424Sroberto	}
361106424Sroberto    }
362106424Sroberto
363132451Sroberto  /* can I handle this firmware version? */
364132451Sroberto  if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
365132451Sroberto    {
366132451Sroberto      (void) close(fd);
367132451Sroberto      pp->io.fd = -1;
368132451Sroberto      free(pp->unitptr);
369132451Sroberto      pp->unitptr = NULL;
370132451Sroberto      return (0);
371132451Sroberto    }
372132451Sroberto#endif
373132451Sroberto
374132451Sroberto  if(!io_addclock(&pp->io))
375132451Sroberto    {
376132451Sroberto      msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
377132451Sroberto      (void) close(fd);
378132451Sroberto      pp->io.fd = -1;
379132451Sroberto      free(pp->unitptr);
380132451Sroberto      pp->unitptr = NULL;
381132451Sroberto      return (0);
382132451Sroberto    }
383132451Sroberto
384106424Sroberto  NLOG(NLOG_CLOCKINFO)
385106424Sroberto    msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
386132451Sroberto
387106424Sroberto  return (1);
388106424Sroberto}
389106424Sroberto
390106424Srobertostatic void
391106424Srobertoneoclock4x_shutdown(int unit,
392106424Sroberto		   struct peer *peer)
393106424Sroberto{
394106424Sroberto  struct neoclock4x_unit *up;
395106424Sroberto  struct refclockproc *pp;
396106424Sroberto  int sl232;
397106424Sroberto
398132451Sroberto  if(NULL != peer)
399132451Sroberto    {
400132451Sroberto      pp = peer->procptr;
401132451Sroberto      if(pp != NULL)
402132451Sroberto        {
403285612Sdelphij          up = pp->unitptr;
404132451Sroberto          if(up != NULL)
405132451Sroberto            {
406132451Sroberto              if(-1 !=  pp->io.fd)
407132451Sroberto                {
408106424Sroberto#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
409132451Sroberto                  /* turn on RTS, and DTR for power supply */
410132451Sroberto                  /* NeoClock4x is powered from serial line */
411132451Sroberto                  if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
412132451Sroberto                    {
413132451Sroberto                      msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
414132451Sroberto                              unit);
415132451Sroberto                    }
416106424Sroberto#ifdef TIOCM_RTS
417132451Sroberto                  /* turn on RTS, and DTR for power supply */
418132451Sroberto                  sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
419106424Sroberto#else
420132451Sroberto                  /* turn on RTS, and DTR for power supply */
421132451Sroberto                  sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
422106424Sroberto#endif
423132451Sroberto                  if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
424132451Sroberto                    {
425132451Sroberto                      msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
426132451Sroberto                              unit);
427132451Sroberto                    }
428132451Sroberto#endif
429132451Sroberto                  io_closeclock(&pp->io);
430132451Sroberto                }
431132451Sroberto              free(up);
432132451Sroberto              pp->unitptr = NULL;
433132451Sroberto            }
434132451Sroberto        }
435106424Sroberto    }
436132451Sroberto
437106424Sroberto  msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
438106424Sroberto
439106424Sroberto  NLOG(NLOG_CLOCKINFO)
440106424Sroberto    msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
441106424Sroberto}
442106424Sroberto
443106424Srobertostatic void
444106424Srobertoneoclock4x_receive(struct recvbuf *rbufp)
445106424Sroberto{
446106424Sroberto  struct neoclock4x_unit *up;
447106424Sroberto  struct refclockproc *pp;
448106424Sroberto  struct peer *peer;
449106424Sroberto  unsigned long calc_utc;
450106424Sroberto  int day;
451106424Sroberto  int month;	/* ddd conversion */
452106424Sroberto  int c;
453132451Sroberto  int dsec;
454106424Sroberto  unsigned char calc_chksum;
455106424Sroberto  int recv_chksum;
456132451Sroberto
457285612Sdelphij  peer = rbufp->recv_peer;
458106424Sroberto  pp = peer->procptr;
459285612Sdelphij  up = pp->unitptr;
460132451Sroberto
461106424Sroberto  /* wait till poll interval is reached */
462106424Sroberto  if(0 == up->recvnow)
463106424Sroberto    return;
464132451Sroberto
465106424Sroberto  /* reset poll interval flag */
466106424Sroberto  up->recvnow = 0;
467106424Sroberto
468106424Sroberto  /* read last received timecode */
469106424Sroberto  pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
470132451Sroberto  pp->leap = LEAP_NOWARNING;
471132451Sroberto
472106424Sroberto  if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
473106424Sroberto    {
474106424Sroberto      NLOG(NLOG_CLOCKEVENT)
475106424Sroberto	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
476106424Sroberto		up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
477106424Sroberto      refclock_report(peer, CEVNT_BADREPLY);
478106424Sroberto      return;
479106424Sroberto    }
480106424Sroberto
481106424Sroberto  neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
482106424Sroberto
483106424Sroberto  /* calculate checksum */
484106424Sroberto  calc_chksum = 0;
485106424Sroberto  for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
486106424Sroberto    {
487106424Sroberto      calc_chksum += pp->a_lastcode[c];
488106424Sroberto    }
489106424Sroberto  if(recv_chksum != calc_chksum)
490106424Sroberto    {
491106424Sroberto      NLOG(NLOG_CLOCKEVENT)
492106424Sroberto	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
493106424Sroberto		up->unit, pp->a_lastcode);
494106424Sroberto      refclock_report(peer, CEVNT_BADREPLY);
495106424Sroberto      return;
496106424Sroberto    }
497106424Sroberto
498106424Sroberto  /* Allow synchronization even is quartz clock is
499106424Sroberto   * never initialized.
500106424Sroberto   * WARNING: This is dangerous!
501132451Sroberto   */
502106424Sroberto  up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
503106424Sroberto  if(0==(pp->sloppyclockflag & CLK_FLAG2))
504106424Sroberto    {
505106424Sroberto      if('I' != up->quarzstatus)
506106424Sroberto	{
507106424Sroberto	  NLOG(NLOG_CLOCKEVENT)
508106424Sroberto	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
509106424Sroberto		    up->unit, pp->a_lastcode);
510106424Sroberto	  pp->leap = LEAP_NOTINSYNC;
511106424Sroberto	  refclock_report(peer, CEVNT_BADDATE);
512106424Sroberto	  return;
513106424Sroberto	}
514106424Sroberto    }
515106424Sroberto  if('I' != up->quarzstatus)
516106424Sroberto    {
517106424Sroberto      NLOG(NLOG_CLOCKEVENT)
518106424Sroberto	msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
519106424Sroberto		up->unit, pp->a_lastcode);
520106424Sroberto    }
521132451Sroberto
522106424Sroberto  /*
523132451Sroberto   * If NeoClock4X is not synchronized to a radio clock
524106424Sroberto   * check if we're allowed to synchronize with the quartz
525106424Sroberto   * clock.
526106424Sroberto   */
527106424Sroberto  up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
528106424Sroberto  if(0==(pp->sloppyclockflag & CLK_FLAG2))
529106424Sroberto    {
530106424Sroberto      if('A' != up->timesource)
531106424Sroberto	{
532106424Sroberto	  /* not allowed to sync with quartz clock */
533106424Sroberto	  if(0==(pp->sloppyclockflag & CLK_FLAG1))
534106424Sroberto	    {
535106424Sroberto	      refclock_report(peer, CEVNT_BADTIME);
536106424Sroberto	      pp->leap = LEAP_NOTINSYNC;
537106424Sroberto	      return;
538106424Sroberto	    }
539106424Sroberto	}
540106424Sroberto    }
541106424Sroberto
542106424Sroberto  /* this should only used when first install is done */
543106424Sroberto  if(pp->sloppyclockflag & CLK_FLAG4)
544106424Sroberto    {
545106424Sroberto      msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
546106424Sroberto	      up->unit, pp->a_lastcode);
547106424Sroberto    }
548106424Sroberto
549106424Sroberto  /* 123456789012345678901234567890123456789012345 */
550106424Sroberto  /* S/N123456DCF1004021010001202ASX1213CR\r\n */
551106424Sroberto
552106424Sroberto  neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
553106424Sroberto  neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
554106424Sroberto  neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
555106424Sroberto  neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
556106424Sroberto  neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
557106424Sroberto  neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
558132451Sroberto  neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2);
559182007Sroberto#if defined(NTP_PRE_420)
560182007Sroberto  pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */
561182007Sroberto#else
562182007Sroberto  pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */
563182007Sroberto#endif
564132451Sroberto
565106424Sroberto  memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
566106424Sroberto  up->radiosignal[3] = 0;
567106424Sroberto  memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
568106424Sroberto  up->serial[6] = 0;
569106424Sroberto  up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
570106424Sroberto  neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
571106424Sroberto  neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
572106424Sroberto
573106424Sroberto  /*
574106424Sroberto    Validate received values at least enough to prevent internal
575106424Sroberto    array-bounds problems, etc.
576106424Sroberto  */
577106424Sroberto  if((pp->hour < 0) || (pp->hour > 23) ||
578106424Sroberto     (pp->minute < 0) || (pp->minute > 59) ||
579106424Sroberto     (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
580106424Sroberto     (day < 1) || (day > 31) ||
581106424Sroberto     (month < 1) || (month > 12) ||
582106424Sroberto     (pp->year < 0) || (pp->year > 99)) {
583106424Sroberto    /* Data out of range. */
584106424Sroberto    NLOG(NLOG_CLOCKEVENT)
585106424Sroberto      msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
586106424Sroberto	      up->unit, pp->a_lastcode);
587106424Sroberto    refclock_report(peer, CEVNT_BADDATE);
588106424Sroberto    return;
589106424Sroberto  }
590132451Sroberto
591132451Sroberto  /* Year-2000 check not needed anymore. Same problem
592132451Sroberto   * will arise at 2099 but what should we do...?
593132451Sroberto   *
594132451Sroberto   * wrap 2-digit date into 4-digit
595132451Sroberto   *
596132451Sroberto   * if(pp->year < YEAR_PIVOT)
597132451Sroberto   * {
598132451Sroberto   *   pp->year += 100;
599132451Sroberto   * }
600132451Sroberto  */
601132451Sroberto  pp->year += 2000;
602132451Sroberto
603132451Sroberto  /* adjust NeoClock4X local time to UTC */
604106424Sroberto  calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
605106424Sroberto  calc_utc -= 3600;
606132451Sroberto  /* adjust NeoClock4X daylight saving time if needed */
607106424Sroberto  if('S' == up->dststatus)
608106424Sroberto    calc_utc -= 3600;
609106424Sroberto  neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
610106424Sroberto
611106424Sroberto  /*
612106424Sroberto    some preparations
613106424Sroberto  */
614132451Sroberto  pp->day = ymd2yd(pp->year, month, day);
615106424Sroberto  pp->leap = 0;
616106424Sroberto
617106424Sroberto  if(pp->sloppyclockflag & CLK_FLAG4)
618106424Sroberto    {
619132451Sroberto      msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
620106424Sroberto	      up->unit,
621106424Sroberto	      pp->year, month, day,
622182007Sroberto	      pp->hour, pp->minute, pp->second,
623182007Sroberto#if defined(NTP_PRE_420)
624182007Sroberto              pp->msec
625182007Sroberto#else
626182007Sroberto              pp->nsec/NSEC_TO_MILLI
627182007Sroberto#endif
628182007Sroberto              );
629106424Sroberto    }
630106424Sroberto
631106424Sroberto  up->utc_year   = pp->year;
632106424Sroberto  up->utc_month  = month;
633106424Sroberto  up->utc_day    = day;
634106424Sroberto  up->utc_hour   = pp->hour;
635106424Sroberto  up->utc_minute = pp->minute;
636106424Sroberto  up->utc_second = pp->second;
637182007Sroberto#if defined(NTP_PRE_420)
638182007Sroberto  up->utc_msec   = pp->msec;
639182007Sroberto#else
640182007Sroberto  up->utc_msec   = pp->nsec/NSEC_TO_MILLI;
641182007Sroberto#endif
642132451Sroberto
643106424Sroberto  if(!refclock_process(pp))
644106424Sroberto    {
645106424Sroberto      NLOG(NLOG_CLOCKEVENT)
646106424Sroberto	msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
647106424Sroberto      refclock_report(peer, CEVNT_FAULT);
648106424Sroberto      return;
649106424Sroberto    }
650106424Sroberto  refclock_receive(peer);
651132451Sroberto
652132451Sroberto  /* report good status */
653132451Sroberto  refclock_report(peer, CEVNT_NOMINAL);
654132451Sroberto
655106424Sroberto  record_clock_stats(&peer->srcadr, pp->a_lastcode);
656106424Sroberto}
657106424Sroberto
658106424Srobertostatic void
659106424Srobertoneoclock4x_poll(int unit,
660106424Sroberto		struct peer *peer)
661106424Sroberto{
662106424Sroberto  struct neoclock4x_unit *up;
663106424Sroberto  struct refclockproc *pp;
664106424Sroberto
665106424Sroberto  pp = peer->procptr;
666285612Sdelphij  up = pp->unitptr;
667106424Sroberto
668106424Sroberto  pp->polls++;
669106424Sroberto  up->recvnow = 1;
670106424Sroberto}
671106424Sroberto
672106424Srobertostatic void
673106424Srobertoneoclock4x_control(int unit,
674285612Sdelphij		   const struct refclockstat *in,
675106424Sroberto		   struct refclockstat *out,
676106424Sroberto		   struct peer *peer)
677106424Sroberto{
678106424Sroberto  struct neoclock4x_unit *up;
679106424Sroberto  struct refclockproc *pp;
680132451Sroberto
681106424Sroberto  if(NULL == peer)
682106424Sroberto    {
683106424Sroberto      msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
684106424Sroberto      return;
685106424Sroberto    }
686106424Sroberto
687106424Sroberto  pp = peer->procptr;
688106424Sroberto  if(NULL == pp)
689106424Sroberto    {
690106424Sroberto      msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
691106424Sroberto      return;
692106424Sroberto    }
693106424Sroberto
694285612Sdelphij  up = pp->unitptr;
695106424Sroberto  if(NULL == up)
696106424Sroberto    {
697106424Sroberto      msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
698106424Sroberto      return;
699106424Sroberto    }
700106424Sroberto
701106424Sroberto  if(NULL != in)
702106424Sroberto    {
703106424Sroberto      /* check to see if a user supplied time offset is given */
704106424Sroberto      if(in->haveflags & CLK_HAVETIME1)
705106424Sroberto	{
706106424Sroberto	  pp->fudgetime1 = in->fudgetime1;
707106424Sroberto	  NLOG(NLOG_CLOCKINFO)
708106424Sroberto	    msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
709106424Sroberto		    unit, pp->fudgetime1);
710106424Sroberto	}
711132451Sroberto
712106424Sroberto      /* notify */
713106424Sroberto      if(pp->sloppyclockflag & CLK_FLAG1)
714106424Sroberto	{
715106424Sroberto	  NLOG(NLOG_CLOCKINFO)
716106424Sroberto	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
717106424Sroberto	}
718106424Sroberto      else
719106424Sroberto	{
720106424Sroberto	  NLOG(NLOG_CLOCKINFO)
721106424Sroberto	    msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
722106424Sroberto	}
723106424Sroberto    }
724106424Sroberto
725106424Sroberto  if(NULL != out)
726106424Sroberto    {
727106424Sroberto      char *tt;
728106424Sroberto      char tmpbuf[80];
729132451Sroberto
730106424Sroberto      out->kv_list = (struct ctl_var *)0;
731106424Sroberto      out->type    = REFCLK_NEOCLOCK4X;
732132451Sroberto
733132451Sroberto      snprintf(tmpbuf, sizeof(tmpbuf)-1,
734132451Sroberto	       "%04d-%02d-%02d %02d:%02d:%02d.%03d",
735132451Sroberto	       up->utc_year, up->utc_month, up->utc_day,
736132451Sroberto	       up->utc_hour, up->utc_minute, up->utc_second,
737132451Sroberto	       up->utc_msec);
738132451Sroberto      tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
739132451Sroberto      snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
740132451Sroberto
741132451Sroberto      tt = add_var(&out->kv_list, 40, RO|DEF);
742132451Sroberto      snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal);
743132451Sroberto      tt = add_var(&out->kv_list, 40, RO|DEF);
744132451Sroberto      snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1);
745132451Sroberto      tt = add_var(&out->kv_list, 40, RO|DEF);
746132451Sroberto      snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2);
747132451Sroberto      tt = add_var(&out->kv_list, 40, RO|DEF);
748106424Sroberto      if('A' == up->timesource)
749132451Sroberto	snprintf(tt, 39, "timesource=\"radio\"");
750106424Sroberto      else if('C' == up->timesource)
751132451Sroberto	snprintf(tt, 39, "timesource=\"quartz\"");
752106424Sroberto      else
753132451Sroberto	snprintf(tt, 39, "timesource=\"unknown\"");
754132451Sroberto      tt = add_var(&out->kv_list, 40, RO|DEF);
755106424Sroberto      if('I' == up->quarzstatus)
756132451Sroberto	snprintf(tt, 39, "quartzstatus=\"synchronized\"");
757106424Sroberto      else if('X' == up->quarzstatus)
758132451Sroberto        snprintf(tt, 39, "quartzstatus=\"not synchronized\"");
759106424Sroberto      else
760132451Sroberto	snprintf(tt, 39, "quartzstatus=\"unknown\"");
761132451Sroberto      tt = add_var(&out->kv_list, 40, RO|DEF);
762106424Sroberto      if('S' == up->dststatus)
763132451Sroberto        snprintf(tt, 39, "dststatus=\"summer\"");
764106424Sroberto      else if('W' == up->dststatus)
765132451Sroberto        snprintf(tt, 39, "dststatus=\"winter\"");
766106424Sroberto      else
767132451Sroberto        snprintf(tt, 39, "dststatus=\"unknown\"");
768132451Sroberto      tt = add_var(&out->kv_list, 80, RO|DEF);
769132451Sroberto      snprintf(tt, 79, "firmware=\"%s\"", up->firmware);
770132451Sroberto      tt = add_var(&out->kv_list, 40, RO|DEF);
771132451Sroberto      snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag);
772132451Sroberto      tt = add_var(&out->kv_list, 80, RO|DEF);
773132451Sroberto      snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION);
774132451Sroberto      tt = add_var(&out->kv_list, 80, RO|DEF);
775132451Sroberto      snprintf(tt, 79, "serialnumber=\"%s\"", up->serial);
776106424Sroberto    }
777106424Sroberto}
778106424Sroberto
779132451Srobertostatic int
780132451Srobertoneol_hexatoi_len(const char str[],
781132451Sroberto		 int *result,
782132451Sroberto		 int maxlen)
783106424Sroberto{
784106424Sroberto  int hexdigit;
785106424Sroberto  int i;
786106424Sroberto  int n = 0;
787132451Sroberto
788285612Sdelphij  for(i=0; isxdigit((unsigned char)str[i]) && i < maxlen; i++)
789106424Sroberto    {
790285612Sdelphij      hexdigit = isdigit((unsigned char)str[i]) ? toupper((unsigned char)str[i]) - '0' : toupper((unsigned char)str[i]) - 'A' + 10;
791106424Sroberto      n = 16 * n + hexdigit;
792106424Sroberto    }
793106424Sroberto  *result = n;
794106424Sroberto  return (n);
795106424Sroberto}
796106424Sroberto
797132451Srobertostatic int
798132451Srobertoneol_atoi_len(const char str[],
799106424Sroberto		  int *result,
800106424Sroberto		  int maxlen)
801106424Sroberto{
802106424Sroberto  int digit;
803106424Sroberto  int i;
804106424Sroberto  int n = 0;
805132451Sroberto
806285612Sdelphij  for(i=0; isdigit((unsigned char)str[i]) && i < maxlen; i++)
807106424Sroberto    {
808106424Sroberto      digit = str[i] - '0';
809106424Sroberto      n = 10 * n + digit;
810106424Sroberto    }
811106424Sroberto  *result = n;
812106424Sroberto  return (n);
813106424Sroberto}
814106424Sroberto
815106424Sroberto/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
816106424Sroberto * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
817106424Sroberto * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
818106424Sroberto *
819106424Sroberto * [For the Julian calendar (which was used in Russia before 1917,
820106424Sroberto * Britain & colonies before 1752, anywhere else before 1582,
821106424Sroberto * and is still in use by some communities) leave out the
822106424Sroberto * -year/100+year/400 terms, and add 10.]
823106424Sroberto *
824106424Sroberto * This algorithm was first published by Gauss (I think).
825106424Sroberto *
826106424Sroberto * WARNING: this function will overflow on 2106-02-07 06:28:16 on
827106424Sroberto * machines were long is 32-bit! (However, as time_t is signed, we
828106424Sroberto * will already get problems at other places on 2038-01-19 03:14:08)
829106424Sroberto */
830132451Srobertostatic unsigned long
831132451Srobertoneol_mktime(int year,
832132451Sroberto	    int mon,
833132451Sroberto	    int day,
834132451Sroberto	    int hour,
835132451Sroberto	    int min,
836132451Sroberto	    int sec)
837106424Sroberto{
838106424Sroberto  if (0 >= (int) (mon -= 2)) {    /* 1..12 . 11,12,1..10 */
839106424Sroberto    mon += 12;      /* Puts Feb last since it has leap day */
840106424Sroberto    year -= 1;
841106424Sroberto  }
842106424Sroberto  return (((
843106424Sroberto            (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
844106424Sroberto            year*365 - 719499
845106424Sroberto            )*24 + hour /* now have hours */
846106424Sroberto           )*60 + min /* now have minutes */
847106424Sroberto          )*60 + sec; /* finally seconds */
848106424Sroberto}
849106424Sroberto
850132451Srobertostatic void
851132451Srobertoneol_localtime(unsigned long utc,
852132451Sroberto	       int* year,
853132451Sroberto	       int* month,
854132451Sroberto	       int* day,
855132451Sroberto	       int* hour,
856132451Sroberto	       int* min,
857132451Sroberto	       int* sec)
858106424Sroberto{
859132451Sroberto  *sec = utc % 60;
860132451Sroberto  utc /= 60;
861132451Sroberto  *min = utc % 60;
862132451Sroberto  utc /= 60;
863132451Sroberto  *hour = utc % 24;
864132451Sroberto  utc /= 24;
865132451Sroberto
866106424Sroberto  /*             JDN Date 1/1/1970 */
867132451Sroberto  neol_jdn_to_ymd(utc + 2440588L, year, month, day);
868106424Sroberto}
869106424Sroberto
870132451Srobertostatic void
871132451Srobertoneol_jdn_to_ymd(unsigned long jdn,
872132451Sroberto		int *yy,
873132451Sroberto		int *mm,
874132451Sroberto		int *dd)
875106424Sroberto{
876106424Sroberto  unsigned long x, z, m, d, y;
877106424Sroberto  unsigned long daysPer400Years = 146097UL;
878106424Sroberto  unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
879132451Sroberto
880106424Sroberto  x = jdn + 68569UL;
881106424Sroberto  z = 4UL * x / daysPer400Years;
882106424Sroberto  x = x - (daysPer400Years * z + 3UL) / 4UL;
883106424Sroberto  y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
884106424Sroberto  x = x - 1461UL * y / 4UL + 31UL;
885106424Sroberto  m = 80UL * x / 2447UL;
886106424Sroberto  d = x - 2447UL * m / 80UL;
887106424Sroberto  x = m / 11UL;
888106424Sroberto  m = m + 2UL - 12UL * x;
889106424Sroberto  y = 100UL * (z - 49UL) + y + x;
890132451Sroberto
891106424Sroberto  *yy = (int)y;
892106424Sroberto  *mm = (int)m;
893106424Sroberto  *dd = (int)d;
894106424Sroberto}
895106424Sroberto
896132451Sroberto#if !defined(NEOCLOCK4X_FIRMWARE)
897106424Srobertostatic int
898106424Srobertoneol_query_firmware(int fd,
899106424Sroberto		    int unit,
900106424Sroberto		    char *firmware,
901285612Sdelphij		    size_t maxlen)
902106424Sroberto{
903132451Sroberto  char tmpbuf[256];
904285612Sdelphij  size_t len;
905106424Sroberto  int lastsearch;
906106424Sroberto  unsigned char c;
907106424Sroberto  int last_c_was_crlf;
908106424Sroberto  int last_crlf_conv_len;
909106424Sroberto  int init;
910132451Sroberto  int read_errors;
911106424Sroberto  int flag = 0;
912132451Sroberto  int chars_read;
913106424Sroberto
914106424Sroberto  /* wait a little bit */
915132451Sroberto  sleep(1);
916106424Sroberto  if(-1 != write(fd, "V", 1))
917106424Sroberto    {
918106424Sroberto      /* wait a little bit */
919132451Sroberto      sleep(1);
920106424Sroberto      memset(tmpbuf, 0x00, sizeof(tmpbuf));
921132451Sroberto
922106424Sroberto      len = 0;
923106424Sroberto      lastsearch = 0;
924106424Sroberto      last_c_was_crlf = 0;
925106424Sroberto      last_crlf_conv_len = 0;
926106424Sroberto      init = 1;
927132451Sroberto      read_errors = 0;
928132451Sroberto      chars_read = 0;
929106424Sroberto      for(;;)
930106424Sroberto	{
931132451Sroberto	  if(read_errors > 5)
932106424Sroberto	    {
933106424Sroberto	      msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
934285612Sdelphij	      strlcpy(tmpbuf, "unknown due to timeout", sizeof(tmpbuf));
935106424Sroberto	      break;
936106424Sroberto	    }
937132451Sroberto          if(chars_read > 500)
938132451Sroberto            {
939132451Sroberto	      msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
940285612Sdelphij	      strlcpy(tmpbuf, "unknown due to garbage input", sizeof(tmpbuf));
941132451Sroberto	      break;
942132451Sroberto            }
943106424Sroberto	  if(-1 == read(fd, &c, 1))
944106424Sroberto	    {
945132451Sroberto              if(EAGAIN != errno)
946132451Sroberto                {
947285612Sdelphij                  msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %m", unit);
948132451Sroberto                  read_errors++;
949132451Sroberto                }
950132451Sroberto              else
951132451Sroberto                {
952132451Sroberto                  sleep(1);
953132451Sroberto                }
954106424Sroberto	      continue;
955106424Sroberto	    }
956132451Sroberto          else
957132451Sroberto            {
958132451Sroberto              chars_read++;
959132451Sroberto            }
960132451Sroberto
961106424Sroberto	  if(init)
962106424Sroberto	    {
963106424Sroberto	      if(0xA9 != c) /* wait for (c) char in input stream */
964106424Sroberto		continue;
965132451Sroberto
966285612Sdelphij	      strlcpy(tmpbuf, "(c)", sizeof(tmpbuf));
967106424Sroberto	      len = 3;
968106424Sroberto	      init = 0;
969106424Sroberto	      continue;
970106424Sroberto	    }
971132451Sroberto
972132451Sroberto#if 0
973132451Sroberto	  msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
974132451Sroberto#endif
975132451Sroberto
976106424Sroberto	  if(0x0A == c || 0x0D == c)
977106424Sroberto	    {
978106424Sroberto	      if(last_c_was_crlf)
979106424Sroberto		{
980106424Sroberto		  char *ptr;
981106424Sroberto		  ptr = strstr(&tmpbuf[lastsearch], "S/N");
982106424Sroberto		  if(NULL != ptr)
983106424Sroberto		    {
984106424Sroberto		      tmpbuf[last_crlf_conv_len] = 0;
985106424Sroberto		      flag = 1;
986106424Sroberto		      break;
987106424Sroberto		    }
988106424Sroberto		  /* convert \n to / */
989106424Sroberto		  last_crlf_conv_len = len;
990106424Sroberto		  tmpbuf[len++] = ' ';
991106424Sroberto		  tmpbuf[len++] = '/';
992106424Sroberto		  tmpbuf[len++] = ' ';
993106424Sroberto		  lastsearch = len;
994106424Sroberto		}
995106424Sroberto	      last_c_was_crlf = 1;
996106424Sroberto	    }
997106424Sroberto	  else
998106424Sroberto	    {
999106424Sroberto	      last_c_was_crlf = 0;
1000106424Sroberto	      if(0x00 != c)
1001132451Sroberto		tmpbuf[len++] = (char) c;
1002106424Sroberto	    }
1003106424Sroberto	  tmpbuf[len] = '\0';
1004285612Sdelphij	  if (len > sizeof(tmpbuf)-5)
1005106424Sroberto	    break;
1006106424Sroberto	}
1007106424Sroberto    }
1008106424Sroberto  else
1009106424Sroberto    {
1010106424Sroberto      msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
1011285612Sdelphij      strlcpy(tmpbuf, "unknown error", sizeof(tmpbuf));
1012106424Sroberto    }
1013285612Sdelphij  if (strlcpy(firmware, tmpbuf, maxlen) >= maxlen)
1014285612Sdelphij    strlcpy(firmware, "buffer too small", maxlen);
1015106424Sroberto
1016106424Sroberto  if(flag)
1017106424Sroberto    {
1018106424Sroberto      NLOG(NLOG_CLOCKINFO)
1019106424Sroberto	msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
1020285612Sdelphij
1021285612Sdelphij      if(strstr(firmware, "/R2"))
1022285612Sdelphij	{
1023285612Sdelphij	  msyslog(LOG_INFO, "NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit);
1024285612Sdelphij	}
1025285612Sdelphij
1026106424Sroberto    }
1027106424Sroberto
1028106424Sroberto  return (flag);
1029106424Sroberto}
1030132451Sroberto
1031132451Srobertostatic int
1032132451Srobertoneol_check_firmware(int unit,
1033132451Sroberto                    const char *firmware,
1034132451Sroberto                    char *firmwaretag)
1035132451Sroberto{
1036132451Sroberto  char *ptr;
1037132451Sroberto
1038132451Sroberto  *firmwaretag = '?';
1039132451Sroberto  ptr = strstr(firmware, "NDF:");
1040132451Sroberto  if(NULL != ptr)
1041132451Sroberto    {
1042132451Sroberto      if((strlen(firmware) - strlen(ptr)) >= 7)
1043132451Sroberto        {
1044132451Sroberto          if(':' == *(ptr+5) && '*' == *(ptr+6))
1045132451Sroberto            *firmwaretag = *(ptr+4);
1046132451Sroberto        }
1047132451Sroberto    }
1048132451Sroberto
1049132451Sroberto  if('A' != *firmwaretag)
1050132451Sroberto    {
1051132451Sroberto      msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
1052132451Sroberto      return (0);
1053132451Sroberto    }
1054132451Sroberto
1055132451Sroberto  return (1);
1056132451Sroberto}
1057132451Sroberto#endif
1058132451Sroberto
1059106424Sroberto#else
1060106424Srobertoint refclock_neoclock4x_bs;
1061106424Sroberto#endif /* REFCLOCK */
1062106424Sroberto
1063106424Sroberto/*
1064106424Sroberto * History:
1065106424Sroberto * refclock_neoclock4x.c
1066106424Sroberto *
1067106424Sroberto * 2002/04/27 cjh
1068106424Sroberto * Revision 1.0  first release
1069106424Sroberto *
1070132451Sroberto * 2002/07/15 cjh
1071106424Sroberto * preparing for bitkeeper reposity
1072106424Sroberto *
1073132451Sroberto * 2002/09/09 cjh
1074132451Sroberto * Revision 1.1
1075132451Sroberto * - don't assume sprintf returns an int anymore
1076132451Sroberto * - change the way the firmware version is read
1077132451Sroberto * - some customers would like to put a device called
1078132451Sroberto *   data diode to the NeoClock4X device to disable
1079132451Sroberto *   the write line. We need to now the firmware
1080132451Sroberto *   version even in this case. We made a compile time
1081132451Sroberto *   definition in this case. The code was previously
1082132451Sroberto *   only available on request.
1083132451Sroberto *
1084132451Sroberto * 2003/01/08 cjh
1085132451Sroberto * Revision 1.11
1086132451Sroberto * - changing xprinf to xnprinf to avoid buffer overflows
1087132451Sroberto * - change some logic
1088132451Sroberto * - fixed memory leaks if drivers can't initialize
1089132451Sroberto *
1090132451Sroberto * 2003/01/10 cjh
1091132451Sroberto * Revision 1.12
1092132451Sroberto * - replaced ldiv
1093132451Sroberto * - add code to support FreeBSD
1094132451Sroberto *
1095132451Sroberto * 2003/07/07 cjh
1096132451Sroberto * Revision 1.13
1097132451Sroberto * - fix reporting of clock status
1098132451Sroberto *   changes. previously a bad clock
1099132451Sroberto *   status was never reset.
1100182007Sroberto *
1101182007Sroberto * 2004/04/07 cjh
1102182007Sroberto * Revision 1.14
1103182007Sroberto * - open serial port in a way
1104182007Sroberto *   AIX and some other OS can
1105182007Sroberto *   handle much better
1106182007Sroberto *
1107182007Sroberto * 2006/01/11 cjh
1108182007Sroberto * Revision 1.15
1109182007Sroberto * - remove some unsued #ifdefs
1110182007Sroberto * - fix nsec calculation, closes #499
1111182007Sroberto *
1112285612Sdelphij * 2009/12/04 cjh
1113285612Sdelphij * Revision 1.16
1114285612Sdelphij * - change license to ntp COPYRIGHT notice. This should allow Debian
1115285612Sdelphij *   to add this refclock driver in further releases.
1116285612Sdelphij * - detect R2 hardware
1117285612Sdelphij *
1118106424Sroberto */
1119285612Sdelphij
1120285612Sdelphij
1121285612Sdelphij
1122285612Sdelphij
1123285612Sdelphij
1124285612Sdelphij
1125