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