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