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