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