1 /* $NetBSD: refclock_neoclock4x.c,v 1.3 2004/10/30 15:01:32 dsl Exp $ */
5 * Refclock_neoclock4x.c
6 * - NeoClock4X driver for DCF77 or FIA Timecode
8 * Date: 2006-01-11 v1.15
10 * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir
11 * for details about the NeoClock4X device
13 * Copyright (C) 2002-2004 by Linum Software GmbH <neoclock4x@linum.com>
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
26 #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
30 #include <sys/types.h>
32 #include <sys/ioctl.h>
37 #include "ntp_control.h"
38 #include "ntp_refclock.h"
39 #include "ntp_unixtime.h"
40 #include "ntp_stdlib.h"
42 #if defined HAVE_SYS_MODEM_H
43 # include <sys/modem.h>
45 # define TIOCMSET MCSETA
46 # define TIOCMGET MCGETA
47 # define TIOCM_RTS MRTS
52 # ifdef TERMIOS_NEEDS__SVID3
56 # ifdef TERMIOS_NEEDS__SVID3
61 #ifdef HAVE_SYS_IOCTL_H
62 # include <sys/ioctl.h>
66 * NTP version 4.20 change the pp->msec field to pp->nsec.
67 * To allow to support older ntp versions with this sourcefile
68 * you can define NTP_PRE_420 to allow this driver to compile
69 * with ntp version back to 4.1.2.
77 * If you want the driver for whatever reason to not use
78 * the TX line to send anything to your NeoClock4X
79 * device you must tell the NTP refclock driver which
80 * firmware you NeoClock4X device uses.
82 * If you want to enable this feature change the "#if 0"
83 * line to "#if 1" and make sure that the defined firmware
84 * matches the firmware off your NeoClock4X receiver!
89 #define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A
92 /* at this time only firmware version A is known */
93 #define NEOCLOCK4X_FIRMWARE_VERSION_A 'A'
95 #define NEOCLOCK4X_TIMECODELEN 37
97 #define NEOCLOCK4X_OFFSET_SERIAL 3
98 #define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9
99 #define NEOCLOCK4X_OFFSET_DAY 12
100 #define NEOCLOCK4X_OFFSET_MONTH 14
101 #define NEOCLOCK4X_OFFSET_YEAR 16
102 #define NEOCLOCK4X_OFFSET_HOUR 18
103 #define NEOCLOCK4X_OFFSET_MINUTE 20
104 #define NEOCLOCK4X_OFFSET_SECOND 22
105 #define NEOCLOCK4X_OFFSET_HSEC 24
106 #define NEOCLOCK4X_OFFSET_DOW 26
107 #define NEOCLOCK4X_OFFSET_TIMESOURCE 28
108 #define NEOCLOCK4X_OFFSET_DSTSTATUS 29
109 #define NEOCLOCK4X_OFFSET_QUARZSTATUS 30
110 #define NEOCLOCK4X_OFFSET_ANTENNA1 31
111 #define NEOCLOCK4X_OFFSET_ANTENNA2 33
112 #define NEOCLOCK4X_OFFSET_CRC 35
114 #define NEOCLOCK4X_DRIVER_VERSION "1.15 (2006-01-11)"
116 #define NSEC_TO_MILLI 1000000
118 struct neoclock4x_unit
{
119 l_fp laststamp
; /* last receive timestamp */
120 short unit
; /* NTP refclock unit number */
121 u_long polled
; /* flag to detect noreplies */
122 char leap_status
; /* leap second flag */
143 static int neoclock4x_start
P((int, struct peer
*));
144 static void neoclock4x_shutdown
P((int, struct peer
*));
145 static void neoclock4x_receive
P((struct recvbuf
*));
146 static void neoclock4x_poll
P((int, struct peer
*));
147 static void neoclock4x_control
P((int, struct refclockstat
*, struct refclockstat
*, struct peer
*));
149 static int neol_atoi_len
P((const char str
[], int *, int));
150 static int neol_hexatoi_len
P((const char str
[], int *, int));
151 static void neol_jdn_to_ymd
P((unsigned long, int *, int *, int *));
152 static void neol_localtime
P((unsigned long, int* , int*, int*, int*, int*, int*));
153 static unsigned long neol_mktime
P((int, int, int, int, int, int));
154 #if !defined(NEOCLOCK4X_FIRMWARE)
155 static int neol_query_firmware
P((int, int, char *, int));
156 static int neol_check_firmware
P((int, const char*, char *));
159 struct refclock refclock_neoclock4x
= {
160 neoclock4x_start
, /* start up driver */
161 neoclock4x_shutdown
, /* shut down driver */
162 neoclock4x_poll
, /* transmit poll message */
164 noentry
, /* initialize driver (not used) */
165 noentry
, /* not used */
166 NOFLAGS
/* not used */
170 neoclock4x_start(int unit
,
173 struct neoclock4x_unit
*up
;
174 struct refclockproc
*pp
;
178 #if defined(HAVE_TERMIOS)
179 struct termios termsettings
;
181 #if !defined(NEOCLOCK4X_FIRMWARE)
185 (void) snprintf(dev
, sizeof(dev
)-1, "/dev/neoclock4x-%d", unit
);
187 /* LDISC_STD, LDISC_RAW
188 * Open serial port. Use CLK line discipline, if available.
190 fd
= refclock_open(dev
, B2400
, LDISC_STD
);
196 #if defined(HAVE_TERMIOS)
199 if(tcgetattr(fd
, &termsettings
) < 0)
201 msyslog(LOG_CRIT
, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit
);
207 termsettings
.c_iflag
= IGNBRK
| IGNPAR
| ICRNL
;
208 termsettings
.c_oflag
= 0;
209 termsettings
.c_cflag
= CS8
| CSTOPB
| CLOCAL
| CREAD
;
210 (void)cfsetispeed(&termsettings
, (u_int
)B2400
);
211 (void)cfsetospeed(&termsettings
, (u_int
)B2400
);
213 if(tcsetattr(fd
, TCSANOW
, &termsettings
) < 0)
215 msyslog(LOG_CRIT
, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit
);
221 if(tcgetattr(fd
, &termsettings
) < 0)
223 msyslog(LOG_CRIT
, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit
);
229 termsettings
.c_cflag
&= ~PARENB
;
230 termsettings
.c_cflag
|= CSTOPB
;
231 termsettings
.c_cflag
&= ~CSIZE
;
232 termsettings
.c_cflag
|= CS8
;
234 if(tcsetattr(fd
, TCSANOW
, &termsettings
) < 0)
236 msyslog(LOG_CRIT
, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit
);
242 #elif defined(HAVE_SYSV_TTYS)
243 if(ioctl(fd
, TCGETA
, &termsettings
) < 0)
245 msyslog(LOG_CRIT
, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit
);
251 termsettings
.c_cflag
&= ~PARENB
;
252 termsettings
.c_cflag
|= CSTOPB
;
253 termsettings
.c_cflag
&= ~CSIZE
;
254 termsettings
.c_cflag
|= CS8
;
256 if(ioctl(fd
, TCSETA
, &termsettings
) < 0)
258 msyslog(LOG_CRIT
, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit
);
263 msyslog(LOG_EMERG
, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit
);
268 #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
269 /* turn on RTS, and DTR for power supply */
270 /* NeoClock4x is powered from serial line */
271 if(ioctl(fd
, TIOCMGET
, (caddr_t
)&sl232
) == -1)
273 msyslog(LOG_CRIT
, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit
);
278 sl232
= sl232
| TIOCM_DTR
| TIOCM_RTS
; /* turn on RTS, and DTR for power supply */
280 sl232
= sl232
| CIOCM_DTR
| CIOCM_RTS
; /* turn on RTS, and DTR for power supply */
282 if(ioctl(fd
, TIOCMSET
, (caddr_t
)&sl232
) == -1)
284 msyslog(LOG_CRIT
, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit
);
289 msyslog(LOG_EMERG
, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
295 up
= (struct neoclock4x_unit
*) emalloc(sizeof(struct neoclock4x_unit
));
298 msyslog(LOG_ERR
, "NeoClock4X(%d): can't allocate memory for: %m",unit
);
303 memset((char *)up
, 0, sizeof(struct neoclock4x_unit
));
305 pp
->clockdesc
= "NeoClock4X";
306 pp
->unitptr
= (caddr_t
)up
;
307 pp
->io
.clock_recv
= neoclock4x_receive
;
308 pp
->io
.srcclock
= (caddr_t
)peer
;
312 * no fudge time is given by user!
313 * use 169.583333 ms to compensate the serial line delay
315 * 2400 Baud / 11 bit = 218.18 charaters per second
316 * (NeoClock4X timecode len)
318 pp
->fudgetime1
= (NEOCLOCK4X_TIMECODELEN
* 11) / 2400.0;
321 * Initialize miscellaneous variables
323 peer
->precision
= -10;
324 peer
->burst
= NSTAGE
;
325 memcpy((char *)&pp
->refid
, "neol", 4);
329 strcpy(up
->firmware
, "?");
330 up
->firmwaretag
= '?';
331 strcpy(up
->serial
, "?");
332 strcpy(up
->radiosignal
, "?");
333 up
->timesource
= '?';
335 up
->quarzstatus
= '?';
346 #if defined(NEOCLOCK4X_FIRMWARE)
347 #if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
348 strcpy(up
->firmware
, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)");
349 up
->firmwaretag
= 'A';
351 msyslog(LOG_EMERG
, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
360 for(tries
=0; tries
< 5; tries
++)
363 msyslog(LOG_INFO
, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit
, tries
);
364 /* wait 3 seconds for receiver to power up */
366 if(neol_query_firmware(pp
->io
.fd
, up
->unit
, up
->firmware
, sizeof(up
->firmware
)))
372 /* can I handle this firmware version? */
373 if(!neol_check_firmware(up
->unit
, up
->firmware
, &up
->firmwaretag
))
383 if(!io_addclock(&pp
->io
))
385 msyslog(LOG_ERR
, "NeoClock4X(%d): error add peer to ntpd: %m", unit
);
394 msyslog(LOG_INFO
, "NeoClock4X(%d): receiver setup successful done", unit
);
400 neoclock4x_shutdown(int unit
,
403 struct neoclock4x_unit
*up
;
404 struct refclockproc
*pp
;
412 up
= (struct neoclock4x_unit
*)pp
->unitptr
;
417 #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
418 /* turn on RTS, and DTR for power supply */
419 /* NeoClock4x is powered from serial line */
420 if(ioctl(pp
->io
.fd
, TIOCMGET
, (caddr_t
)&sl232
) == -1)
422 msyslog(LOG_CRIT
, "NeoClock4X(%d): can't query RTS/DTR state: %m",
426 /* turn on RTS, and DTR for power supply */
427 sl232
&= ~(TIOCM_DTR
| TIOCM_RTS
);
429 /* turn on RTS, and DTR for power supply */
430 sl232
&= ~(CIOCM_DTR
| CIOCM_RTS
);
432 if(ioctl(pp
->io
.fd
, TIOCMSET
, (caddr_t
)&sl232
) == -1)
434 msyslog(LOG_CRIT
, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
438 io_closeclock(&pp
->io
);
446 msyslog(LOG_ERR
, "NeoClock4X(%d): shutdown", unit
);
449 msyslog(LOG_INFO
, "NeoClock4X(%d): receiver shutdown done", unit
);
453 neoclock4x_receive(struct recvbuf
*rbufp
)
455 struct neoclock4x_unit
*up
;
456 struct refclockproc
*pp
;
458 unsigned long calc_utc
;
460 int month
; /* ddd conversion */
463 unsigned char calc_chksum
;
466 peer
= (struct peer
*)rbufp
->recv_srcclock
;
468 up
= (struct neoclock4x_unit
*)pp
->unitptr
;
470 /* wait till poll interval is reached */
474 /* reset poll interval flag */
477 /* read last received timecode */
478 pp
->lencode
= refclock_gtlin(rbufp
, pp
->a_lastcode
, BMAX
, &pp
->lastrec
);
479 pp
->leap
= LEAP_NOWARNING
;
481 if(NEOCLOCK4X_TIMECODELEN
!= pp
->lencode
)
483 NLOG(NLOG_CLOCKEVENT
)
484 msyslog(LOG_WARNING
, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
485 up
->unit
, NEOCLOCK4X_TIMECODELEN
, pp
->lencode
, pp
->a_lastcode
);
486 refclock_report(peer
, CEVNT_BADREPLY
);
490 neol_hexatoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_CRC
], &recv_chksum
, 2);
492 /* calculate checksum */
494 for(c
=0; c
< NEOCLOCK4X_OFFSET_CRC
; c
++)
496 calc_chksum
+= pp
->a_lastcode
[c
];
498 if(recv_chksum
!= calc_chksum
)
500 NLOG(NLOG_CLOCKEVENT
)
501 msyslog(LOG_WARNING
, "NeoClock4X(%d): received data has invalid chksum: %s",
502 up
->unit
, pp
->a_lastcode
);
503 refclock_report(peer
, CEVNT_BADREPLY
);
507 /* Allow synchronization even is quartz clock is
509 * WARNING: This is dangerous!
511 up
->quarzstatus
= pp
->a_lastcode
[NEOCLOCK4X_OFFSET_QUARZSTATUS
];
512 if(0==(pp
->sloppyclockflag
& CLK_FLAG2
))
514 if('I' != up
->quarzstatus
)
516 NLOG(NLOG_CLOCKEVENT
)
517 msyslog(LOG_NOTICE
, "NeoClock4X(%d): quartz clock is not initialized: %s",
518 up
->unit
, pp
->a_lastcode
);
519 pp
->leap
= LEAP_NOTINSYNC
;
520 refclock_report(peer
, CEVNT_BADDATE
);
524 if('I' != up
->quarzstatus
)
526 NLOG(NLOG_CLOCKEVENT
)
527 msyslog(LOG_NOTICE
, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
528 up
->unit
, pp
->a_lastcode
);
532 * If NeoClock4X is not synchronized to a radio clock
533 * check if we're allowed to synchronize with the quartz
536 up
->timesource
= pp
->a_lastcode
[NEOCLOCK4X_OFFSET_TIMESOURCE
];
537 if(0==(pp
->sloppyclockflag
& CLK_FLAG2
))
539 if('A' != up
->timesource
)
541 /* not allowed to sync with quartz clock */
542 if(0==(pp
->sloppyclockflag
& CLK_FLAG1
))
544 refclock_report(peer
, CEVNT_BADTIME
);
545 pp
->leap
= LEAP_NOTINSYNC
;
551 /* this should only used when first install is done */
552 if(pp
->sloppyclockflag
& CLK_FLAG4
)
554 msyslog(LOG_DEBUG
, "NeoClock4X(%d): received data: %s",
555 up
->unit
, pp
->a_lastcode
);
558 /* 123456789012345678901234567890123456789012345 */
559 /* S/N123456DCF1004021010001202ASX1213CR\r\n */
561 neol_atoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_YEAR
], &pp
->year
, 2);
562 neol_atoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_MONTH
], &month
, 2);
563 neol_atoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_DAY
], &day
, 2);
564 neol_atoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_HOUR
], &pp
->hour
, 2);
565 neol_atoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_MINUTE
], &pp
->minute
, 2);
566 neol_atoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_SECOND
], &pp
->second
, 2);
567 neol_atoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_HSEC
], &dsec
, 2);
568 #if defined(NTP_PRE_420)
569 pp
->msec
= dsec
* 10; /* convert 1/100s from neoclock to real miliseconds */
571 pp
->nsec
= dsec
* 10 * NSEC_TO_MILLI
; /* convert 1/100s from neoclock to nanoseconds */
574 memcpy(up
->radiosignal
, &pp
->a_lastcode
[NEOCLOCK4X_OFFSET_RADIOSIGNAL
], 3);
575 up
->radiosignal
[3] = 0;
576 memcpy(up
->serial
, &pp
->a_lastcode
[NEOCLOCK4X_OFFSET_SERIAL
], 6);
578 up
->dststatus
= pp
->a_lastcode
[NEOCLOCK4X_OFFSET_DSTSTATUS
];
579 neol_hexatoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_ANTENNA1
], &up
->antenna1
, 2);
580 neol_hexatoi_len(&pp
->a_lastcode
[NEOCLOCK4X_OFFSET_ANTENNA2
], &up
->antenna2
, 2);
583 Validate received values at least enough to prevent internal
584 array-bounds problems, etc.
586 if((pp
->hour
< 0) || (pp
->hour
> 23) ||
587 (pp
->minute
< 0) || (pp
->minute
> 59) ||
588 (pp
->second
< 0) || (pp
->second
> 60) /*Allow for leap seconds.*/ ||
589 (day
< 1) || (day
> 31) ||
590 (month
< 1) || (month
> 12) ||
591 (pp
->year
< 0) || (pp
->year
> 99)) {
592 /* Data out of range. */
593 NLOG(NLOG_CLOCKEVENT
)
594 msyslog(LOG_WARNING
, "NeoClock4X(%d): date/time out of range: %s",
595 up
->unit
, pp
->a_lastcode
);
596 refclock_report(peer
, CEVNT_BADDATE
);
600 /* Year-2000 check not needed anymore. Same problem
601 * will arise at 2099 but what should we do...?
603 * wrap 2-digit date into 4-digit
605 * if(pp->year < YEAR_PIVOT)
612 /* adjust NeoClock4X local time to UTC */
613 calc_utc
= neol_mktime(pp
->year
, month
, day
, pp
->hour
, pp
->minute
, pp
->second
);
615 /* adjust NeoClock4X daylight saving time if needed */
616 if('S' == up
->dststatus
)
618 neol_localtime(calc_utc
, &pp
->year
, &month
, &day
, &pp
->hour
, &pp
->minute
, &pp
->second
);
623 pp
->day
= ymd2yd(pp
->year
, month
, day
);
626 if(pp
->sloppyclockflag
& CLK_FLAG4
)
628 msyslog(LOG_DEBUG
, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
630 pp
->year
, month
, day
,
631 pp
->hour
, pp
->minute
, pp
->second
,
632 #if defined(NTP_PRE_420)
635 pp
->nsec
/NSEC_TO_MILLI
640 up
->utc_year
= pp
->year
;
641 up
->utc_month
= month
;
643 up
->utc_hour
= pp
->hour
;
644 up
->utc_minute
= pp
->minute
;
645 up
->utc_second
= pp
->second
;
646 #if defined(NTP_PRE_420)
647 up
->utc_msec
= pp
->msec
;
649 up
->utc_msec
= pp
->nsec
/NSEC_TO_MILLI
;
652 if(!refclock_process(pp
))
654 NLOG(NLOG_CLOCKEVENT
)
655 msyslog(LOG_WARNING
, "NeoClock4X(%d): refclock_process failed!", up
->unit
);
656 refclock_report(peer
, CEVNT_FAULT
);
659 refclock_receive(peer
);
661 /* report good status */
662 refclock_report(peer
, CEVNT_NOMINAL
);
664 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
668 neoclock4x_poll(int unit
,
671 struct neoclock4x_unit
*up
;
672 struct refclockproc
*pp
;
675 up
= (struct neoclock4x_unit
*)pp
->unitptr
;
682 neoclock4x_control(int unit
,
683 struct refclockstat
*in
,
684 struct refclockstat
*out
,
687 struct neoclock4x_unit
*up
;
688 struct refclockproc
*pp
;
692 msyslog(LOG_ERR
, "NeoClock4X(%d): control: unit invalid/inactive", unit
);
699 msyslog(LOG_ERR
, "NeoClock4X(%d): control: unit invalid/inactive", unit
);
703 up
= (struct neoclock4x_unit
*)pp
->unitptr
;
706 msyslog(LOG_ERR
, "NeoClock4X(%d): control: unit invalid/inactive", unit
);
712 /* check to see if a user supplied time offset is given */
713 if(in
->haveflags
& CLK_HAVETIME1
)
715 pp
->fudgetime1
= in
->fudgetime1
;
717 msyslog(LOG_NOTICE
, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
718 unit
, pp
->fudgetime1
);
722 if(pp
->sloppyclockflag
& CLK_FLAG1
)
725 msyslog(LOG_NOTICE
, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit
);
730 msyslog(LOG_NOTICE
, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit
);
739 out
->kv_list
= (struct ctl_var
*)0;
740 out
->type
= REFCLK_NEOCLOCK4X
;
742 snprintf(tmpbuf
, sizeof(tmpbuf
)-1,
743 "%04d-%02d-%02d %02d:%02d:%02d.%03d",
744 up
->utc_year
, up
->utc_month
, up
->utc_day
,
745 up
->utc_hour
, up
->utc_minute
, up
->utc_second
,
747 tt
= add_var(&out
->kv_list
, sizeof(tmpbuf
)-1, RO
|DEF
);
748 snprintf(tt
, sizeof(tmpbuf
)-1, "calc_utc=\"%s\"", tmpbuf
);
750 tt
= add_var(&out
->kv_list
, 40, RO
|DEF
);
751 snprintf(tt
, 39, "radiosignal=\"%s\"", up
->radiosignal
);
752 tt
= add_var(&out
->kv_list
, 40, RO
|DEF
);
753 snprintf(tt
, 39, "antenna1=\"%d\"", up
->antenna1
);
754 tt
= add_var(&out
->kv_list
, 40, RO
|DEF
);
755 snprintf(tt
, 39, "antenna2=\"%d\"", up
->antenna2
);
756 tt
= add_var(&out
->kv_list
, 40, RO
|DEF
);
757 if('A' == up
->timesource
)
758 snprintf(tt
, 39, "timesource=\"radio\"");
759 else if('C' == up
->timesource
)
760 snprintf(tt
, 39, "timesource=\"quartz\"");
762 snprintf(tt
, 39, "timesource=\"unknown\"");
763 tt
= add_var(&out
->kv_list
, 40, RO
|DEF
);
764 if('I' == up
->quarzstatus
)
765 snprintf(tt
, 39, "quartzstatus=\"synchronized\"");
766 else if('X' == up
->quarzstatus
)
767 snprintf(tt
, 39, "quartzstatus=\"not synchronized\"");
769 snprintf(tt
, 39, "quartzstatus=\"unknown\"");
770 tt
= add_var(&out
->kv_list
, 40, RO
|DEF
);
771 if('S' == up
->dststatus
)
772 snprintf(tt
, 39, "dststatus=\"summer\"");
773 else if('W' == up
->dststatus
)
774 snprintf(tt
, 39, "dststatus=\"winter\"");
776 snprintf(tt
, 39, "dststatus=\"unknown\"");
777 tt
= add_var(&out
->kv_list
, 80, RO
|DEF
);
778 snprintf(tt
, 79, "firmware=\"%s\"", up
->firmware
);
779 tt
= add_var(&out
->kv_list
, 40, RO
|DEF
);
780 snprintf(tt
, 39, "firmwaretag=\"%c\"", up
->firmwaretag
);
781 tt
= add_var(&out
->kv_list
, 80, RO
|DEF
);
782 snprintf(tt
, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION
);
783 tt
= add_var(&out
->kv_list
, 80, RO
|DEF
);
784 snprintf(tt
, 79, "serialnumber=\"%s\"", up
->serial
);
789 neol_hexatoi_len(const char str
[],
797 for(i
=0; isxdigit((int)str
[i
]) && i
< maxlen
; i
++)
799 hexdigit
= isdigit((int)str
[i
]) ? toupper((int)str
[i
]) - '0' : toupper((int)str
[i
]) - 'A' + 10;
800 n
= 16 * n
+ hexdigit
;
807 neol_atoi_len(const char str
[],
815 for(i
=0; isdigit((int)str
[i
]) && i
< maxlen
; i
++)
817 digit
= str
[i
] - '0';
824 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
825 * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
826 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
828 * [For the Julian calendar (which was used in Russia before 1917,
829 * Britain & colonies before 1752, anywhere else before 1582,
830 * and is still in use by some communities) leave out the
831 * -year/100+year/400 terms, and add 10.]
833 * This algorithm was first published by Gauss (I think).
835 * WARNING: this function will overflow on 2106-02-07 06:28:16 on
836 * machines were long is 32-bit! (However, as time_t is signed, we
837 * will already get problems at other places on 2038-01-19 03:14:08)
840 neol_mktime(int year
,
847 if (0 >= (int) (mon
-= 2)) { /* 1..12 . 11,12,1..10 */
848 mon
+= 12; /* Puts Feb last since it has leap day */
852 (unsigned long)(year
/4 - year
/100 + year
/400 + 367*mon
/12 + day
) +
854 )*24 + hour
/* now have hours */
855 )*60 + min
/* now have minutes */
856 )*60 + sec
; /* finally seconds */
860 neol_localtime(unsigned long utc
,
875 /* JDN Date 1/1/1970 */
876 neol_jdn_to_ymd(utc
+ 2440588L, year
, month
, day
);
880 neol_jdn_to_ymd(unsigned long jdn
,
885 unsigned long x
, z
, m
, d
, y
;
886 unsigned long daysPer400Years
= 146097UL;
887 unsigned long fudgedDaysPer4000Years
= 1460970UL + 31UL;
890 z
= 4UL * x
/ daysPer400Years
;
891 x
= x
- (daysPer400Years
* z
+ 3UL) / 4UL;
892 y
= 4000UL * (x
+ 1) / fudgedDaysPer4000Years
;
893 x
= x
- 1461UL * y
/ 4UL + 31UL;
894 m
= 80UL * x
/ 2447UL;
895 d
= x
- 2447UL * m
/ 80UL;
897 m
= m
+ 2UL - 12UL * x
;
898 y
= 100UL * (z
- 49UL) + y
+ x
;
905 #if !defined(NEOCLOCK4X_FIRMWARE)
907 neol_query_firmware(int fd
,
917 int last_crlf_conv_len
;
923 /* wait a little bit */
925 if(-1 != write(fd
, "V", 1))
927 /* wait a little bit */
929 memset(tmpbuf
, 0x00, sizeof(tmpbuf
));
934 last_crlf_conv_len
= 0;
942 msyslog(LOG_ERR
, "NeoClock4X(%d): can't read firmware version (timeout)", unit
);
943 strcpy(tmpbuf
, "unknown due to timeout");
948 msyslog(LOG_ERR
, "NeoClock4X(%d): can't read firmware version (garbage)", unit
);
949 strcpy(tmpbuf
, "unknown due to garbage input");
952 if(-1 == read(fd
, &c
, 1))
956 msyslog(LOG_DEBUG
, "NeoClock4x(%d): read: %s", unit
,strerror(errno
));
972 if(0xA9 != c
) /* wait for (c) char in input stream */
975 strcpy(tmpbuf
, "(c)");
982 msyslog(LOG_NOTICE
, "NeoClock4X(%d): firmware %c = %02Xh", unit
, c
, c
);
985 if(0x0A == c
|| 0x0D == c
)
990 ptr
= strstr(&tmpbuf
[lastsearch
], "S/N");
993 tmpbuf
[last_crlf_conv_len
] = 0;
997 /* convert \n to / */
998 last_crlf_conv_len
= len
;
1000 tmpbuf
[len
++] = '/';
1001 tmpbuf
[len
++] = ' ';
1004 last_c_was_crlf
= 1;
1008 last_c_was_crlf
= 0;
1010 tmpbuf
[len
++] = (char) c
;
1013 if(len
> sizeof(tmpbuf
)-5)
1019 msyslog(LOG_ERR
, "NeoClock4X(%d): can't query firmware version", unit
);
1020 strcpy(tmpbuf
, "unknown error");
1022 strncpy(firmware
, tmpbuf
, maxlen
);
1023 firmware
[maxlen
] = '\0';
1027 NLOG(NLOG_CLOCKINFO
)
1028 msyslog(LOG_INFO
, "NeoClock4X(%d): firmware version: %s", unit
, firmware
);
1035 neol_check_firmware(int unit
,
1036 const char *firmware
,
1042 ptr
= strstr(firmware
, "NDF:");
1045 if((strlen(firmware
) - strlen(ptr
)) >= 7)
1047 if(':' == *(ptr
+5) && '*' == *(ptr
+6))
1048 *firmwaretag
= *(ptr
+4);
1052 if('A' != *firmwaretag
)
1054 msyslog(LOG_CRIT
, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit
, *firmwaretag
);
1063 int refclock_neoclock4x_bs
;
1064 #endif /* REFCLOCK */
1068 * refclock_neoclock4x.c
1071 * Revision 1.0 first release
1074 * preparing for bitkeeper reposity
1078 * - don't assume sprintf returns an int anymore
1079 * - change the way the firmware version is read
1080 * - some customers would like to put a device called
1081 * data diode to the NeoClock4X device to disable
1082 * the write line. We need to now the firmware
1083 * version even in this case. We made a compile time
1084 * definition in this case. The code was previously
1085 * only available on request.
1089 * - changing xprinf to xnprinf to avoid buffer overflows
1090 * - change some logic
1091 * - fixed memory leaks if drivers can't initialize
1096 * - add code to support FreeBSD
1100 * - fix reporting of clock status
1101 * changes. previously a bad clock
1102 * status was never reset.
1106 * - open serial port in a way
1107 * AIX and some other OS can
1108 * handle much better
1112 * - remove some unsued #ifdefs
1113 * - fix nsec calculation, closes #499