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