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