3 * Refclock_neoclock4x.c
4 * - NeoClock4X driver for DCF77 or FIA Timecode
6 * Date: 2006-01-11 v1.15
8 * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir
9 * for details about the NeoClock4X device
11 * Copyright (C) 2002-2004 by Linum Software GmbH <neoclock4x@linum.com>
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.
24 #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
28 #include <sys/types.h>
30 #include <sys/ioctl.h>
35 #include "ntp_control.h"
36 #include "ntp_refclock.h"
37 #include "ntp_unixtime.h"
38 #include "ntp_stdlib.h"
40 #if defined HAVE_SYS_MODEM_H
41 # include <sys/modem.h>
43 # define TIOCMSET MCSETA
44 # define TIOCMGET MCGETA
45 # define TIOCM_RTS MRTS
50 # ifdef TERMIOS_NEEDS__SVID3
54 # ifdef TERMIOS_NEEDS__SVID3
59 #ifdef HAVE_SYS_IOCTL_H
60 # include <sys/ioctl.h>
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.
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.
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!
87 #define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A
90 /* at this time only firmware version A is known */
91 #define NEOCLOCK4X_FIRMWARE_VERSION_A 'A'
93 #define NEOCLOCK4X_TIMECODELEN 37
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
112 #define NEOCLOCK4X_DRIVER_VERSION "1.15 (2006-01-11)"
114 #define NSEC_TO_MILLI 1000000
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 */
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 *));
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 *));
157 struct refclock refclock_neoclock4x = {
158 neoclock4x_start, /* start up driver */
159 neoclock4x_shutdown, /* shut down driver */
160 neoclock4x_poll, /* transmit poll message */
162 noentry, /* initialize driver (not used) */
163 noentry, /* not used */
164 NOFLAGS /* not used */
168 neoclock4x_start(int unit,
171 struct neoclock4x_unit *up;
172 struct refclockproc *pp;
176 #if defined(HAVE_TERMIOS)
177 struct termios termsettings;
179 #if !defined(NEOCLOCK4X_FIRMWARE)
183 (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit);
185 /* LDISC_STD, LDISC_RAW
186 * Open serial port. Use CLK line discipline, if available.
188 fd = refclock_open(dev, B2400, LDISC_STD);
194 #if defined(HAVE_TERMIOS)
197 if(tcgetattr(fd, &termsettings) < 0)
199 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
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);
211 if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
213 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
219 if(tcgetattr(fd, &termsettings) < 0)
221 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
227 termsettings.c_cflag &= ~PARENB;
228 termsettings.c_cflag |= CSTOPB;
229 termsettings.c_cflag &= ~CSIZE;
230 termsettings.c_cflag |= CS8;
232 if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
234 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
240 #elif defined(HAVE_SYSV_TTYS)
241 if(ioctl(fd, TCGETA, &termsettings) < 0)
243 msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit);
249 termsettings.c_cflag &= ~PARENB;
250 termsettings.c_cflag |= CSTOPB;
251 termsettings.c_cflag &= ~CSIZE;
252 termsettings.c_cflag |= CS8;
254 if(ioctl(fd, TCSETA, &termsettings) < 0)
256 msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit);
261 msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
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)
271 msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
276 sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */
278 sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */
280 if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
282 msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
287 msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
293 up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
296 msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
301 memset((char *)up, 0, sizeof(struct neoclock4x_unit));
303 pp->clockdesc = "NeoClock4X";
304 pp->unitptr = (caddr_t)up;
305 pp->io.clock_recv = neoclock4x_receive;
306 pp->io.srcclock = (caddr_t)peer;
310 * no fudge time is given by user!
311 * use 169.583333 ms to compensate the serial line delay
313 * 2400 Baud / 11 bit = 218.18 charaters per second
314 * (NeoClock4X timecode len)
316 pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
319 * Initialize miscellaneous variables
321 peer->precision = -10;
322 peer->burst = NSTAGE;
323 memcpy((char *)&pp->refid, "neol", 4);
327 strcpy(up->firmware, "?");
328 up->firmwaretag = '?';
329 strcpy(up->serial, "?");
330 strcpy(up->radiosignal, "?");
331 up->timesource = '?';
333 up->quarzstatus = '?';
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';
349 msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
358 for(tries=0; tries < 5; tries++)
361 msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
362 /* wait 3 seconds for receiver to power up */
364 if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
370 /* can I handle this firmware version? */
371 if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
381 if(!io_addclock(&pp->io))
383 msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
392 msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
398 neoclock4x_shutdown(int unit,
401 struct neoclock4x_unit *up;
402 struct refclockproc *pp;
410 up = (struct neoclock4x_unit *)pp->unitptr;
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)
420 msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
424 /* turn on RTS, and DTR for power supply */
425 sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
427 /* turn on RTS, and DTR for power supply */
428 sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
430 if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
432 msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
436 io_closeclock(&pp->io);
444 msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
447 msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
451 neoclock4x_receive(struct recvbuf *rbufp)
453 struct neoclock4x_unit *up;
454 struct refclockproc *pp;
456 unsigned long calc_utc;
458 int month; /* ddd conversion */
461 unsigned char calc_chksum;
464 peer = (struct peer *)rbufp->recv_srcclock;
466 up = (struct neoclock4x_unit *)pp->unitptr;
468 /* wait till poll interval is reached */
472 /* reset poll interval flag */
475 /* read last received timecode */
476 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
477 pp->leap = LEAP_NOWARNING;
479 if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
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);
488 neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
490 /* calculate checksum */
492 for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
494 calc_chksum += pp->a_lastcode[c];
496 if(recv_chksum != calc_chksum)
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);
505 /* Allow synchronization even is quartz clock is
507 * WARNING: This is dangerous!
509 up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
510 if(0==(pp->sloppyclockflag & CLK_FLAG2))
512 if('I' != up->quarzstatus)
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);
522 if('I' != up->quarzstatus)
524 NLOG(NLOG_CLOCKEVENT)
525 msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
526 up->unit, pp->a_lastcode);
530 * If NeoClock4X is not synchronized to a radio clock
531 * check if we're allowed to synchronize with the quartz
534 up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
535 if(0==(pp->sloppyclockflag & CLK_FLAG2))
537 if('A' != up->timesource)
539 /* not allowed to sync with quartz clock */
540 if(0==(pp->sloppyclockflag & CLK_FLAG1))
542 refclock_report(peer, CEVNT_BADTIME);
543 pp->leap = LEAP_NOTINSYNC;
549 /* this should only used when first install is done */
550 if(pp->sloppyclockflag & CLK_FLAG4)
552 msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
553 up->unit, pp->a_lastcode);
556 /* 123456789012345678901234567890123456789012345 */
557 /* S/N123456DCF1004021010001202ASX1213CR\r\n */
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 */
569 pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */
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);
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);
581 Validate received values at least enough to prevent internal
582 array-bounds problems, etc.
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);
598 /* Year-2000 check not needed anymore. Same problem
599 * will arise at 2099 but what should we do...?
601 * wrap 2-digit date into 4-digit
603 * if(pp->year < YEAR_PIVOT)
610 /* adjust NeoClock4X local time to UTC */
611 calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
613 /* adjust NeoClock4X daylight saving time if needed */
614 if('S' == up->dststatus)
616 neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
621 pp->day = ymd2yd(pp->year, month, day);
624 if(pp->sloppyclockflag & CLK_FLAG4)
626 msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
628 pp->year, month, day,
629 pp->hour, pp->minute, pp->second,
630 #if defined(NTP_PRE_420)
633 pp->nsec/NSEC_TO_MILLI
638 up->utc_year = pp->year;
639 up->utc_month = month;
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;
647 up->utc_msec = pp->nsec/NSEC_TO_MILLI;
650 if(!refclock_process(pp))
652 NLOG(NLOG_CLOCKEVENT)
653 msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
654 refclock_report(peer, CEVNT_FAULT);
657 refclock_receive(peer);
659 /* report good status */
660 refclock_report(peer, CEVNT_NOMINAL);
662 record_clock_stats(&peer->srcadr, pp->a_lastcode);
666 neoclock4x_poll(int unit,
669 struct neoclock4x_unit *up;
670 struct refclockproc *pp;
673 up = (struct neoclock4x_unit *)pp->unitptr;
680 neoclock4x_control(int unit,
681 struct refclockstat *in,
682 struct refclockstat *out,
685 struct neoclock4x_unit *up;
686 struct refclockproc *pp;
690 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
697 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
701 up = (struct neoclock4x_unit *)pp->unitptr;
704 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
710 /* check to see if a user supplied time offset is given */
711 if(in->haveflags & CLK_HAVETIME1)
713 pp->fudgetime1 = in->fudgetime1;
715 msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
716 unit, pp->fudgetime1);
720 if(pp->sloppyclockflag & CLK_FLAG1)
723 msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
728 msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
737 out->kv_list = (struct ctl_var *)0;
738 out->type = REFCLK_NEOCLOCK4X;
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,
745 tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
746 snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
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\"");
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\"");
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\"");
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);
787 neol_hexatoi_len(const char str[],
795 for(i=0; isxdigit((int)str[i]) && i < maxlen; i++)
797 hexdigit = isdigit((int)str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10;
798 n = 16 * n + hexdigit;
805 neol_atoi_len(const char str[],
813 for(i=0; isdigit((int)str[i]) && i < maxlen; i++)
815 digit = str[i] - '0';
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.
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.]
831 * This algorithm was first published by Gauss (I think).
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)
838 neol_mktime(int year,
845 if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */
846 mon += 12; /* Puts Feb last since it has leap day */
850 (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
852 )*24 + hour /* now have hours */
853 )*60 + min /* now have minutes */
854 )*60 + sec; /* finally seconds */
858 neol_localtime(unsigned long utc,
873 /* JDN Date 1/1/1970 */
874 neol_jdn_to_ymd(utc + 2440588L, year, month, day);
878 neol_jdn_to_ymd(unsigned long jdn,
883 unsigned long x, z, m, d, y;
884 unsigned long daysPer400Years = 146097UL;
885 unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
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;
895 m = m + 2UL - 12UL * x;
896 y = 100UL * (z - 49UL) + y + x;
903 #if !defined(NEOCLOCK4X_FIRMWARE)
905 neol_query_firmware(int fd,
915 int last_crlf_conv_len;
921 /* wait a little bit */
923 if(-1 != write(fd, "V", 1))
925 /* wait a little bit */
927 memset(tmpbuf, 0x00, sizeof(tmpbuf));
932 last_crlf_conv_len = 0;
940 msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
941 strcpy(tmpbuf, "unknown due to timeout");
946 msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
947 strcpy(tmpbuf, "unknown due to garbage input");
950 if(-1 == read(fd, &c, 1))
954 msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %s", unit ,strerror(errno));
970 if(0xA9 != c) /* wait for (c) char in input stream */
973 strcpy(tmpbuf, "(c)");
980 msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
983 if(0x0A == c || 0x0D == c)
988 ptr = strstr(&tmpbuf[lastsearch], "S/N");
991 tmpbuf[last_crlf_conv_len] = 0;
995 /* convert \n to / */
996 last_crlf_conv_len = len;
1002 last_c_was_crlf = 1;
1006 last_c_was_crlf = 0;
1008 tmpbuf[len++] = (char) c;
1011 if(len > sizeof(tmpbuf)-5)
1017 msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
1018 strcpy(tmpbuf, "unknown error");
1020 strncpy(firmware, tmpbuf, maxlen);
1021 firmware[maxlen] = '\0';
1025 NLOG(NLOG_CLOCKINFO)
1026 msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
1033 neol_check_firmware(int unit,
1034 const char *firmware,
1040 ptr = strstr(firmware, "NDF:");
1043 if((strlen(firmware) - strlen(ptr)) >= 7)
1045 if(':' == *(ptr+5) && '*' == *(ptr+6))
1046 *firmwaretag = *(ptr+4);
1050 if('A' != *firmwaretag)
1052 msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
1061 int refclock_neoclock4x_bs;
1062 #endif /* REFCLOCK */
1066 * refclock_neoclock4x.c
1069 * Revision 1.0 first release
1072 * preparing for bitkeeper reposity
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.
1087 * - changing xprinf to xnprinf to avoid buffer overflows
1088 * - change some logic
1089 * - fixed memory leaks if drivers can't initialize
1094 * - add code to support FreeBSD
1098 * - fix reporting of clock status
1099 * changes. previously a bad clock
1100 * status was never reset.
1104 * - open serial port in a way
1105 * AIX and some other OS can
1106 * handle much better
1110 * - remove some unsued #ifdefs
1111 * - fix nsec calculation, closes #499