xref: /freebsd/contrib/ntp/ntpd/refclock_neoclock4x.c (revision d37ea99837e6ad50837fd9fe1771ddf1c3ba6002)
1 /*
2  *
3  * refclock_neoclock4x.c
4  * - NeoClock4X driver for DCF77 or FIA Timecode
5  *
6  * Date: 2002-04-27 1.0
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 by Linum Software GmbH <support@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 #define NEOCLOCK4X_TIMECODELEN 37
62 
63 #define NEOCLOCK4X_OFFSET_SERIAL            3
64 #define NEOCLOCK4X_OFFSET_RADIOSIGNAL       9
65 #define NEOCLOCK4X_OFFSET_DAY              12
66 #define NEOCLOCK4X_OFFSET_MONTH            14
67 #define NEOCLOCK4X_OFFSET_YEAR             16
68 #define NEOCLOCK4X_OFFSET_HOUR             18
69 #define NEOCLOCK4X_OFFSET_MINUTE           20
70 #define NEOCLOCK4X_OFFSET_SECOND           22
71 #define NEOCLOCK4X_OFFSET_HSEC             24
72 #define NEOCLOCK4X_OFFSET_DOW              26
73 #define NEOCLOCK4X_OFFSET_TIMESOURCE       28
74 #define NEOCLOCK4X_OFFSET_DSTSTATUS        29
75 #define NEOCLOCK4X_OFFSET_QUARZSTATUS      30
76 #define NEOCLOCK4X_OFFSET_ANTENNA1         31
77 #define NEOCLOCK4X_OFFSET_ANTENNA2         33
78 #define NEOCLOCK4X_OFFSET_CRC              35
79 
80 struct neoclock4x_unit {
81   l_fp	laststamp;	/* last receive timestamp */
82   short	unit;		/* NTP refclock unit number */
83   u_long	polled;		/* flag to detect noreplies */
84   char	leap_status;	/* leap second flag */
85   int	recvnow;
86 
87   char  firmware[80];
88   char  serial[7];
89   char  radiosignal[4];
90   char  timesource;
91   char  dststatus;
92   char  quarzstatus;
93   int   antenna1;
94   int   antenna2;
95   int   utc_year;
96   int   utc_month;
97   int   utc_day;
98   int   utc_hour;
99   int   utc_minute;
100   int   utc_second;
101   int   utc_msec;
102 };
103 
104 static	int	neoclock4x_start        P((int, struct peer *));
105 static	void	neoclock4x_shutdown	P((int, struct peer *));
106 static	void	neoclock4x_receive	P((struct recvbuf *));
107 static	void	neoclock4x_poll		P((int, struct peer *));
108 static	void	neoclock4x_control      P((int, struct refclockstat *, struct refclockstat *, struct peer *));
109 
110 static int      neol_atoi_len           P((const char str[], int *, int));
111 static int      neol_hexatoi_len        P((const char str[], int *, int));
112 static void     neol_jdn_to_ymd         P((unsigned long, int *, int *, int *));
113 static void     neol_localtime          P((unsigned long, int* , int*, int*, int*, int*, int*));
114 static unsigned long neol_mktime        P((int, int, int, int, int, int));
115 static void     neol_mdelay             P((int));
116 static int      neol_query_firmware     P((int, int, char *, int));
117 
118 struct refclock refclock_neoclock4x = {
119   neoclock4x_start,	/* start up driver */
120   neoclock4x_shutdown,	/* shut down driver */
121   neoclock4x_poll,	/* transmit poll message */
122   neoclock4x_control,
123   noentry,		/* initialize driver (not used) */
124   noentry,		/* not used */
125   NOFLAGS			/* not used */
126 };
127 
128 static int
129 neoclock4x_start(int unit,
130 		 struct peer *peer)
131 {
132   struct neoclock4x_unit *up;
133   struct refclockproc *pp;
134   int fd;
135   char dev[20];
136   int sl232;
137   struct termios termsettings;
138   int tries;
139 
140   (void) sprintf(dev, "/dev/neoclock4x-%d", unit);
141 
142   /* LDISC_STD, LDISC_RAW
143    * Open serial port. Use CLK line discipline, if available.
144    */
145   fd = refclock_open(dev, B2400, LDISC_CLK);
146   if(fd <= 0)
147     {
148       return (0);
149     }
150 
151 #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
152   /* turn on RTS, and DTR for power supply */
153   /* NeoClock4x is powered from serial line */
154   if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
155     {
156       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
157     }
158 #ifdef TIOCM_RTS
159   sl232 = sl232 | TIOCM_DTR | TIOCM_RTS;	/* turn on RTS, and DTR for power supply */
160 #else
161   sl232 = sl232 | CIOCM_DTR | CIOCM_RTS;	/* turn on RTS, and DTR for power supply */
162 #endif
163   if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
164     {
165       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
166     }
167 
168   if(ioctl(fd, TCGETS, (caddr_t)&termsettings) == -1)
169     {
170       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query serial port settings: %m", unit);
171     }
172 
173   /* 2400 Baud mit 8N2 */
174   termsettings.c_cflag &= ~PARENB;
175   termsettings.c_cflag |= CSTOPB;
176   termsettings.c_cflag &= ~CSIZE;
177   termsettings.c_cflag |= CS8;
178 
179   if(ioctl(fd, TCSETS, &termsettings) == -1)
180     {
181       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set serial port to 2400 8N2: %m", unit);
182     }
183 #else
184   msyslog(LOG_EMERG, "NeoClock4X(%d): OS interface is incapable of setting DTR/RTS to power NeoClock4X",
185 	  unit);
186 #endif
187 
188   up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
189   if(!(up))
190     {
191       msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
192       (void) close(fd);
193       return (0);
194     }
195 
196   memset((char *)up, 0, sizeof(struct neoclock4x_unit));
197   pp = peer->procptr;
198   pp->clockdesc = "NeoClock4X";
199   pp->unitptr = (caddr_t)up;
200   pp->io.clock_recv = neoclock4x_receive;
201   pp->io.srcclock = (caddr_t)peer;
202   pp->io.datalen = 0;
203   pp->io.fd = fd;
204   /* no time is given by user! use 169.583333 ms to compensate the serial line delay
205    * formula is:
206    * 2400 Baud / 11 bit = 218.18 charaters per second
207    *  (NeoClock4X timecode len)
208    */
209   pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
210 
211   if (!io_addclock(&pp->io))
212     {
213       msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m",unit);
214       (void) close(fd);
215       free(up);
216       return (0);
217     }
218 
219   /*
220    * Initialize miscellaneous variables
221    */
222   peer->precision = -10;
223   peer->burst = NSTAGE;
224   memcpy((char *)&pp->refid, "neol", 4);
225 
226   up->leap_status = 0;
227   up->unit = unit;
228   strcpy(up->firmware, "?");
229   strcpy(up->serial, "?");
230   strcpy(up->radiosignal, "?");
231   up->timesource  = '?';
232   up->dststatus   = '?';
233   up->quarzstatus = '?';
234   up->antenna1    = -1;
235   up->antenna2    = -1;
236   up->utc_year    = 0;
237   up->utc_month   = 0;
238   up->utc_day     = 0;
239   up->utc_hour    = 0;
240   up->utc_minute  = 0;
241   up->utc_second  = 0;
242   up->utc_msec    = 0;
243 
244   for(tries=0; tries < 5; tries++)
245     {
246       /*
247        * Wait 3 second for receiver to power up
248        */
249       NLOG(NLOG_CLOCKINFO)
250 	msyslog(LOG_INFO, "NeoClock4X(%d): try query NeoClock4X firmware version (%d/5)", unit, tries);
251       sleep(3);
252       if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
253 	{
254 	  break;
255 	}
256     }
257 
258   NLOG(NLOG_CLOCKINFO)
259     msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
260 
261   return (1);
262 }
263 
264 static void
265 neoclock4x_shutdown(int unit,
266 		   struct peer *peer)
267 {
268   struct neoclock4x_unit *up;
269   struct refclockproc *pp;
270   int sl232;
271 
272   pp = peer->procptr;
273   up = (struct neoclock4x_unit *)pp->unitptr;
274 
275 #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
276   /* turn on RTS, and DTR for power supply */
277   /* NeoClock4x is powered from serial line */
278   if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
279     {
280       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
281     }
282 #ifdef TIOCM_RTS
283   sl232 &= ~(TIOCM_DTR | TIOCM_RTS);	/* turn on RTS, and DTR for power supply */
284 #else
285   sl232 &= ~(CIOCM_DTR | CIOCM_RTS);	/* turn on RTS, and DTR for power supply */
286 #endif
287   if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
288     {
289       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
290     }
291 #endif
292   msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
293 
294   io_closeclock(&pp->io);
295   free(up);
296   NLOG(NLOG_CLOCKINFO)
297     msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
298 }
299 
300 static void
301 neoclock4x_receive(struct recvbuf *rbufp)
302 {
303   struct neoclock4x_unit *up;
304   struct refclockproc *pp;
305   struct peer *peer;
306   unsigned long calc_utc;
307   int day;
308   int month;	/* ddd conversion */
309   int c;
310   unsigned char calc_chksum;
311   int recv_chksum;
312 
313   peer = (struct peer *)rbufp->recv_srcclock;
314   pp = peer->procptr;
315   up = (struct neoclock4x_unit *)pp->unitptr;
316 
317   /* wait till poll interval is reached */
318   if(0 == up->recvnow)
319     return;
320 
321   /* reset poll interval flag */
322   up->recvnow = 0;
323 
324   /* read last received timecode */
325   pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
326 
327   if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
328     {
329       NLOG(NLOG_CLOCKEVENT)
330 	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
331 		up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
332       refclock_report(peer, CEVNT_BADREPLY);
333       return;
334     }
335 
336   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
337 
338   /* calculate checksum */
339   calc_chksum = 0;
340   for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
341     {
342       calc_chksum += pp->a_lastcode[c];
343     }
344   if(recv_chksum != calc_chksum)
345     {
346       NLOG(NLOG_CLOCKEVENT)
347 	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
348 		up->unit, pp->a_lastcode);
349       refclock_report(peer, CEVNT_BADREPLY);
350       return;
351     }
352 
353   /* Allow synchronization even is quartz clock is
354    * never initialized.
355    * WARNING: This is dangerous!
356    */
357   up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
358   if(0==(pp->sloppyclockflag & CLK_FLAG2))
359     {
360       if('I' != up->quarzstatus)
361 	{
362 	  NLOG(NLOG_CLOCKEVENT)
363 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
364 		    up->unit, pp->a_lastcode);
365 	  pp->leap = LEAP_NOTINSYNC;
366 	  refclock_report(peer, CEVNT_BADDATE);
367 	  return;
368 	}
369     }
370   if('I' != up->quarzstatus)
371     {
372       NLOG(NLOG_CLOCKEVENT)
373 	msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
374 		up->unit, pp->a_lastcode);
375     }
376 
377   /*
378    * If NeoClock4X is not synchronized to a radio clock
379    * check if we're allowed to synchronize with the quartz
380    * clock.
381    */
382   up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
383   if(0==(pp->sloppyclockflag & CLK_FLAG2))
384     {
385       if('A' != up->timesource)
386 	{
387 	  /* not allowed to sync with quartz clock */
388 	  if(0==(pp->sloppyclockflag & CLK_FLAG1))
389 	    {
390 	      refclock_report(peer, CEVNT_BADTIME);
391 	      pp->leap = LEAP_NOTINSYNC;
392 	      return;
393 	    }
394 	}
395     }
396 
397   /* this should only used when first install is done */
398   if(pp->sloppyclockflag & CLK_FLAG4)
399     {
400       msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
401 	      up->unit, pp->a_lastcode);
402     }
403 
404   /* 123456789012345678901234567890123456789012345 */
405   /* S/N123456DCF1004021010001202ASX1213CR\r\n */
406 
407   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
408   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
409   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
410   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
411   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
412   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
413   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &pp->msec, 2);
414   pp->msec *= 10; /* convert 1/100s from neoclock to real miliseconds */
415 
416   memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
417   up->radiosignal[3] = 0;
418   memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
419   up->serial[6] = 0;
420   up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
421   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
422   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
423 
424   /*
425     Validate received values at least enough to prevent internal
426     array-bounds problems, etc.
427   */
428   if((pp->hour < 0) || (pp->hour > 23) ||
429      (pp->minute < 0) || (pp->minute > 59) ||
430      (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
431      (day < 1) || (day > 31) ||
432      (month < 1) || (month > 12) ||
433      (pp->year < 0) || (pp->year > 99)) {
434     /* Data out of range. */
435     NLOG(NLOG_CLOCKEVENT)
436       msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
437 	      up->unit, pp->a_lastcode);
438     refclock_report(peer, CEVNT_BADDATE);
439     return;
440   }
441 
442   /* Year-2000 check! */
443   /* wrap 2-digit date into 4-digit */
444 
445   if(pp->year < YEAR_PIVOT) /* < 98 */
446     {
447       pp->year += 100;
448     }
449   pp->year += 1900;
450 
451   calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
452   calc_utc -= 3600;
453   if('S' == up->dststatus)
454     calc_utc -= 3600;
455   neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
456 
457   /*
458     some preparations
459   */
460   pp->day = ymd2yd(pp->year,month,day);
461   pp->leap = 0;
462 
463 
464   if(pp->sloppyclockflag & CLK_FLAG4)
465     {
466       msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03d",
467 	      up->unit,
468 	      pp->year, month, day,
469 	      pp->hour, pp->minute, pp->second, pp->msec);
470     }
471 
472   up->utc_year   = pp->year;
473   up->utc_month  = month;
474   up->utc_day    = day;
475   up->utc_hour   = pp->hour;
476   up->utc_minute = pp->minute;
477   up->utc_second = pp->second;
478   up->utc_msec   = pp->msec;
479 
480   if(!refclock_process(pp))
481     {
482       NLOG(NLOG_CLOCKEVENT)
483 	msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
484       refclock_report(peer, CEVNT_FAULT);
485       return;
486     }
487   refclock_receive(peer);
488 
489   record_clock_stats(&peer->srcadr, pp->a_lastcode);
490 }
491 
492 static void
493 neoclock4x_poll(int unit,
494 		struct peer *peer)
495 {
496   struct neoclock4x_unit *up;
497   struct refclockproc *pp;
498 
499   pp = peer->procptr;
500   up = (struct neoclock4x_unit *)pp->unitptr;
501 
502   pp->polls++;
503   up->recvnow = 1;
504 }
505 
506 static void
507 neoclock4x_control(int unit,
508 		   struct refclockstat *in,
509 		   struct refclockstat *out,
510 		   struct peer *peer)
511 {
512   struct neoclock4x_unit *up;
513   struct refclockproc *pp;
514 
515   if(NULL == peer)
516     {
517       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
518       return;
519     }
520 
521   pp = peer->procptr;
522   if(NULL == pp)
523     {
524       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
525       return;
526     }
527 
528   up = (struct neoclock4x_unit *)pp->unitptr;
529   if(NULL == up)
530     {
531       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
532       return;
533     }
534 
535   if(NULL != in)
536     {
537       /* check to see if a user supplied time offset is given */
538       if(in->haveflags & CLK_HAVETIME1)
539 	{
540 	  pp->fudgetime1 = in->fudgetime1;
541 	  NLOG(NLOG_CLOCKINFO)
542 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
543 		    unit, pp->fudgetime1);
544 	}
545 
546       /* notify */
547       if(pp->sloppyclockflag & CLK_FLAG1)
548 	{
549 	  NLOG(NLOG_CLOCKINFO)
550 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
551 	}
552       else
553 	{
554 	  NLOG(NLOG_CLOCKINFO)
555 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
556 	}
557     }
558 
559   if(NULL != out)
560     {
561       static char outstatus[800];	/* status output buffer */
562       char *tt;
563       char tmpbuf[80];
564 
565       outstatus[0] = '\0';
566       out->kv_list = (struct ctl_var *)0;
567       out->type    = REFCLK_NEOCLOCK4X;
568 
569       sprintf(tmpbuf, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
570 	      up->utc_year, up->utc_month, up->utc_day,
571 	      up->utc_hour, up->utc_minute, up->utc_second,
572 	      up->utc_msec);
573 
574       tt = add_var(&out->kv_list, 512, RO|DEF);
575       tt += sprintf(tt, "calc_utc=\"%s\"", tmpbuf);
576       tt = add_var(&out->kv_list, 512, RO|DEF);
577       tt += sprintf(tt, "radiosignal=\"%s\"", up->radiosignal);
578       tt = add_var(&out->kv_list, 512, RO|DEF);
579       tt += sprintf(tt, "antenna1=\"%d\"", up->antenna1);
580       tt = add_var(&out->kv_list, 512, RO|DEF);
581       tt += sprintf(tt, "antenna2=\"%d\"", up->antenna2);
582       tt = add_var(&out->kv_list, 512, RO|DEF);
583       if('A' == up->timesource)
584 	tt += sprintf(tt, "timesource=\"radio\"");
585       else if('C' == up->timesource)
586 	tt += sprintf(tt, "timesource=\"quartz\"");
587       else
588 	tt += sprintf(tt, "timesource=\"unknown\"");
589       tt = add_var(&out->kv_list, 512, RO|DEF);
590       if('I' == up->quarzstatus)
591 	tt += sprintf(tt, "quartzstatus=\"synchronized\"");
592       else if('X' == up->quarzstatus)
593         tt += sprintf(tt, "quartzstatus=\"not synchronized\"");
594       else
595 	tt += sprintf(tt, "quartzstatus=\"unknown\"");
596       tt = add_var(&out->kv_list, 512, RO|DEF);
597       if('S' == up->dststatus)
598         tt += sprintf(tt, "dststatus=\"summer\"");
599       else if('W' == up->dststatus)
600         tt += sprintf(tt, "dststatus=\"winter\"");
601       else
602         tt += sprintf(tt, "dststatus=\"unknown\"");
603       tt = add_var(&out->kv_list, 512, RO|DEF);
604       tt += sprintf(tt, "firmware=\"%s\"", up->firmware);
605       tt = add_var(&out->kv_list, 512, RO|DEF);
606       tt += sprintf(tt, "serialnumber=\"%s\"", up->serial);
607       tt = add_var(&out->kv_list, 512, RO|DEF);
608     }
609 }
610 
611 static int neol_hexatoi_len(const char str[],
612 			    int *result,
613 			    int maxlen)
614 {
615   int hexdigit;
616   int i;
617   int n = 0;
618 
619   for(i=0; isxdigit(str[i]) && i < maxlen; i++)
620     {
621       hexdigit = isdigit(str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10;
622       n = 16 * n + hexdigit;
623     }
624   *result = n;
625   return (n);
626 }
627 
628 int neol_atoi_len(const char str[],
629 		  int *result,
630 		  int maxlen)
631 {
632   int digit;
633   int i;
634   int n = 0;
635 
636   for(i=0; isdigit(str[i]) && i < maxlen; i++)
637     {
638       digit = str[i] - '0';
639       n = 10 * n + digit;
640     }
641   *result = n;
642   return (n);
643 }
644 
645 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
646  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
647  * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
648  *
649  * [For the Julian calendar (which was used in Russia before 1917,
650  * Britain & colonies before 1752, anywhere else before 1582,
651  * and is still in use by some communities) leave out the
652  * -year/100+year/400 terms, and add 10.]
653  *
654  * This algorithm was first published by Gauss (I think).
655  *
656  * WARNING: this function will overflow on 2106-02-07 06:28:16 on
657  * machines were long is 32-bit! (However, as time_t is signed, we
658  * will already get problems at other places on 2038-01-19 03:14:08)
659  */
660 static unsigned long neol_mktime(int year,
661 				 int mon,
662 				 int day,
663 				 int hour,
664 				 int min,
665 				 int sec)
666 {
667   if (0 >= (int) (mon -= 2)) {    /* 1..12 . 11,12,1..10 */
668     mon += 12;      /* Puts Feb last since it has leap day */
669     year -= 1;
670   }
671   return (((
672             (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
673             year*365 - 719499
674             )*24 + hour /* now have hours */
675            )*60 + min /* now have minutes */
676           )*60 + sec; /* finally seconds */
677 }
678 
679 static void neol_localtime(unsigned long utc,
680 			   int* year,
681 			   int* month,
682 			   int* day,
683 			   int* hour,
684 			   int* minute,
685 			   int* second)
686 {
687   ldiv_t d;
688 
689   /* Sekunden */
690   d  = ldiv(utc, 60);
691   *second = d.rem;
692 
693   /* Minute */
694   d  = ldiv(d.quot, 60);
695   *minute = d.rem;
696 
697   /* Stunden */
698   d  = ldiv(d.quot, 24);
699   *hour = d.rem;
700 
701   /*             JDN Date 1/1/1970 */
702   neol_jdn_to_ymd(d.quot + 2440588L, year, month, day);
703 }
704 
705 static void neol_jdn_to_ymd(unsigned long jdn,
706 			    int *yy,
707 			    int *mm,
708 			    int *dd)
709 {
710   unsigned long x, z, m, d, y;
711   unsigned long daysPer400Years = 146097UL;
712   unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
713 
714   x = jdn + 68569UL;
715   z = 4UL * x / daysPer400Years;
716   x = x - (daysPer400Years * z + 3UL) / 4UL;
717   y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
718   x = x - 1461UL * y / 4UL + 31UL;
719   m = 80UL * x / 2447UL;
720   d = x - 2447UL * m / 80UL;
721   x = m / 11UL;
722   m = m + 2UL - 12UL * x;
723   y = 100UL * (z - 49UL) + y + x;
724 
725   *yy = (int)y;
726   *mm = (int)m;
727   *dd = (int)d;
728 }
729 
730 /*
731  *  delay in milliseconds
732  */
733 static void
734 neol_mdelay(int milliseconds)
735 {
736   struct timeval        tv;
737 
738   if (milliseconds)
739     {
740       tv.tv_sec  = 0;
741       tv.tv_usec = milliseconds * 1000;
742       select(1, NULL, NULL, NULL, &tv);
743     }
744 }
745 
746 static int
747 neol_query_firmware(int fd,
748 		    int unit,
749 		    char *firmware,
750 		    int maxlen)
751 {
752   unsigned char tmpbuf[256];
753   int len;
754   int lastsearch;
755   unsigned char c;
756   int last_c_was_crlf;
757   int last_crlf_conv_len;
758   int init;
759   int read_tries;
760   int flag = 0;
761 
762   /* wait a little bit */
763   neol_mdelay(250);
764   if(-1 != write(fd, "V", 1))
765     {
766       /* wait a little bit */
767       neol_mdelay(250);
768       memset(tmpbuf, 0x00, sizeof(tmpbuf));
769 
770       len = 0;
771       lastsearch = 0;
772       last_c_was_crlf = 0;
773       last_crlf_conv_len = 0;
774       init = 1;
775       read_tries = 0;
776       for(;;)
777 	{
778 	  if(read_tries++ > 500)
779 	    {
780 	      msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
781 	      strcpy(tmpbuf, "unknown due to timeout");
782 	      break;
783 	    }
784 	  if(-1 == read(fd, &c, 1))
785 	    {
786 	      neol_mdelay(25);
787 	      continue;
788 	    }
789 	  if(init)
790 	    {
791 	      if(0xA9 != c) /* wait for (c) char in input stream */
792 		continue;
793 
794 	      strcpy(tmpbuf, "(c)");
795 	      len = 3;
796 	      init = 0;
797 	      continue;
798 	    }
799 
800 	  //msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
801 	  if(0x0A == c || 0x0D == c)
802 	    {
803 	      if(last_c_was_crlf)
804 		{
805 		  char *ptr;
806 		  ptr = strstr(&tmpbuf[lastsearch], "S/N");
807 		  if(NULL != ptr)
808 		    {
809 		      tmpbuf[last_crlf_conv_len] = 0;
810 		      flag = 1;
811 		      break;
812 		    }
813 		  /* convert \n to / */
814 		  last_crlf_conv_len = len;
815 		  tmpbuf[len++] = ' ';
816 		  tmpbuf[len++] = '/';
817 		  tmpbuf[len++] = ' ';
818 		  lastsearch = len;
819 		}
820 	      last_c_was_crlf = 1;
821 	    }
822 	  else
823 	    {
824 	      last_c_was_crlf = 0;
825 	      if(0x00 != c)
826 		tmpbuf[len++] = c;
827 	    }
828 	  tmpbuf[len] = '\0';
829 	  if(len > sizeof(tmpbuf)-5)
830 	    break;
831 	}
832     }
833   else
834     {
835       msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
836       strcpy(tmpbuf, "unknown error");
837     }
838   strncpy(firmware, tmpbuf, maxlen);
839   firmware[maxlen] = '\0';
840 
841   if(flag)
842     {
843       NLOG(NLOG_CLOCKINFO)
844 	msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
845     }
846 
847   return (flag);
848 }
849 
850 #else
851 int refclock_neoclock4x_bs;
852 #endif /* REFCLOCK */
853 
854 /*
855  * History:
856  * refclock_neoclock4x.c
857  *
858  * 2002/04/27 cjh
859  * Revision 1.0  first release
860  *
861  * 2002/0715 cjh
862  * preparing for bitkeeper reposity
863  *
864  */
865