Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ntp / ntpd / refclock_neoclock4x.c
blob45452a750cffd9e6b4d91ca26ef1652334cd2378
1 /* $NetBSD: refclock_neoclock4x.c,v 1.3 2004/10/30 15:01:32 dsl Exp $ */
3 /*
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.
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
26 #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
28 #include <unistd.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <termios.h>
32 #include <sys/ioctl.h>
33 #include <ctype.h>
35 #include "ntpd.h"
36 #include "ntp_io.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>
44 # ifndef __QNXNTO__
45 # define TIOCMSET MCSETA
46 # define TIOCMGET MCGETA
47 # define TIOCM_RTS MRTS
48 # endif
49 #endif
51 #ifdef HAVE_TERMIOS_H
52 # ifdef TERMIOS_NEEDS__SVID3
53 # define _SVID3
54 # endif
55 # include <termios.h>
56 # ifdef TERMIOS_NEEDS__SVID3
57 # undef _SVID3
58 # endif
59 #endif
61 #ifdef HAVE_SYS_IOCTL_H
62 # include <sys/ioctl.h>
63 #endif
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.
72 #if 0
73 #define NTP_PRE_420
74 #endif
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!
88 #if 0
89 #define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A
90 #endif
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 */
123 int recvnow;
125 char firmware[80];
126 char firmwaretag;
127 char serial[7];
128 char radiosignal[4];
129 char timesource;
130 char dststatus;
131 char quarzstatus;
132 int antenna1;
133 int antenna2;
134 int utc_year;
135 int utc_month;
136 int utc_day;
137 int utc_hour;
138 int utc_minute;
139 int utc_second;
140 int utc_msec;
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 *));
157 #endif
159 struct refclock refclock_neoclock4x = {
160 neoclock4x_start, /* start up driver */
161 neoclock4x_shutdown, /* shut down driver */
162 neoclock4x_poll, /* transmit poll message */
163 neoclock4x_control,
164 noentry, /* initialize driver (not used) */
165 noentry, /* not used */
166 NOFLAGS /* not used */
169 static int
170 neoclock4x_start(int unit,
171 struct peer *peer)
173 struct neoclock4x_unit *up;
174 struct refclockproc *pp;
175 int fd;
176 char dev[20];
177 int sl232;
178 #if defined(HAVE_TERMIOS)
179 struct termios termsettings;
180 #endif
181 #if !defined(NEOCLOCK4X_FIRMWARE)
182 int tries;
183 #endif
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);
191 if(fd <= 0)
193 return (0);
196 #if defined(HAVE_TERMIOS)
198 #if 1
199 if(tcgetattr(fd, &termsettings) < 0)
201 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
202 (void) close(fd);
203 return (0);
206 /* 2400 Baud 8N2 */
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);
216 (void) close(fd);
217 return (0);
220 #else
221 if(tcgetattr(fd, &termsettings) < 0)
223 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
224 (void) close(fd);
225 return (0);
228 /* 2400 Baud 8N2 */
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);
237 (void) close(fd);
238 return (0);
240 #endif
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);
246 (void) close(fd);
247 return (0);
250 /* 2400 Baud 8N2 */
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);
259 (void) close(fd);
260 return (0);
262 #else
263 msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
264 (void) close(fd);
265 return (0);
266 #endif
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);
274 (void) close(fd);
275 return (0);
277 #ifdef TIOCM_RTS
278 sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */
279 #else
280 sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */
281 #endif
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);
285 (void) close(fd);
286 return (0);
288 #else
289 msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
290 unit);
291 (void) close(fd);
292 return (0);
293 #endif
295 up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
296 if(!(up))
298 msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
299 (void) close(fd);
300 return (0);
303 memset((char *)up, 0, sizeof(struct neoclock4x_unit));
304 pp = peer->procptr;
305 pp->clockdesc = "NeoClock4X";
306 pp->unitptr = (caddr_t)up;
307 pp->io.clock_recv = neoclock4x_receive;
308 pp->io.srcclock = (caddr_t)peer;
309 pp->io.datalen = 0;
310 pp->io.fd = fd;
312 * no fudge time is given by user!
313 * use 169.583333 ms to compensate the serial line delay
314 * formula is:
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);
327 up->leap_status = 0;
328 up->unit = unit;
329 strcpy(up->firmware, "?");
330 up->firmwaretag = '?';
331 strcpy(up->serial, "?");
332 strcpy(up->radiosignal, "?");
333 up->timesource = '?';
334 up->dststatus = '?';
335 up->quarzstatus = '?';
336 up->antenna1 = -1;
337 up->antenna2 = -1;
338 up->utc_year = 0;
339 up->utc_month = 0;
340 up->utc_day = 0;
341 up->utc_hour = 0;
342 up->utc_minute = 0;
343 up->utc_second = 0;
344 up->utc_msec = 0;
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';
350 #else
351 msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
352 unit);
353 (void) close(fd);
354 pp->io.fd = -1;
355 free(pp->unitptr);
356 pp->unitptr = NULL;
357 return (0);
358 #endif
359 #else
360 for(tries=0; tries < 5; tries++)
362 NLOG(NLOG_CLOCKINFO)
363 msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
364 /* wait 3 seconds for receiver to power up */
365 sleep(3);
366 if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
368 break;
372 /* can I handle this firmware version? */
373 if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
375 (void) close(fd);
376 pp->io.fd = -1;
377 free(pp->unitptr);
378 pp->unitptr = NULL;
379 return (0);
381 #endif
383 if(!io_addclock(&pp->io))
385 msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
386 (void) close(fd);
387 pp->io.fd = -1;
388 free(pp->unitptr);
389 pp->unitptr = NULL;
390 return (0);
393 NLOG(NLOG_CLOCKINFO)
394 msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
396 return (1);
399 static void
400 neoclock4x_shutdown(int unit,
401 struct peer *peer)
403 struct neoclock4x_unit *up;
404 struct refclockproc *pp;
405 int sl232;
407 if(NULL != peer)
409 pp = peer->procptr;
410 if(pp != NULL)
412 up = (struct neoclock4x_unit *)pp->unitptr;
413 if(up != NULL)
415 if(-1 != pp->io.fd)
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",
423 unit);
425 #ifdef TIOCM_RTS
426 /* turn on RTS, and DTR for power supply */
427 sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
428 #else
429 /* turn on RTS, and DTR for power supply */
430 sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
431 #endif
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",
435 unit);
437 #endif
438 io_closeclock(&pp->io);
440 free(up);
441 pp->unitptr = NULL;
446 msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
448 NLOG(NLOG_CLOCKINFO)
449 msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
452 static void
453 neoclock4x_receive(struct recvbuf *rbufp)
455 struct neoclock4x_unit *up;
456 struct refclockproc *pp;
457 struct peer *peer;
458 unsigned long calc_utc;
459 int day;
460 int month; /* ddd conversion */
461 int c;
462 int dsec;
463 unsigned char calc_chksum;
464 int recv_chksum;
466 peer = (struct peer *)rbufp->recv_srcclock;
467 pp = peer->procptr;
468 up = (struct neoclock4x_unit *)pp->unitptr;
470 /* wait till poll interval is reached */
471 if(0 == up->recvnow)
472 return;
474 /* reset poll interval flag */
475 up->recvnow = 0;
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);
487 return;
490 neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
492 /* calculate checksum */
493 calc_chksum = 0;
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);
504 return;
507 /* Allow synchronization even is quartz clock is
508 * never initialized.
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);
521 return;
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
534 * clock.
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;
546 return;
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 */
570 #else
571 pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */
572 #endif
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);
577 up->serial[6] = 0;
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);
597 return;
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)
607 * pp->year += 100;
610 pp->year += 2000;
612 /* adjust NeoClock4X local time to UTC */
613 calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
614 calc_utc -= 3600;
615 /* adjust NeoClock4X daylight saving time if needed */
616 if('S' == up->dststatus)
617 calc_utc -= 3600;
618 neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
621 some preparations
623 pp->day = ymd2yd(pp->year, month, day);
624 pp->leap = 0;
626 if(pp->sloppyclockflag & CLK_FLAG4)
628 msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
629 up->unit,
630 pp->year, month, day,
631 pp->hour, pp->minute, pp->second,
632 #if defined(NTP_PRE_420)
633 pp->msec
634 #else
635 pp->nsec/NSEC_TO_MILLI
636 #endif
640 up->utc_year = pp->year;
641 up->utc_month = month;
642 up->utc_day = day;
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;
648 #else
649 up->utc_msec = pp->nsec/NSEC_TO_MILLI;
650 #endif
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);
657 return;
659 refclock_receive(peer);
661 /* report good status */
662 refclock_report(peer, CEVNT_NOMINAL);
664 record_clock_stats(&peer->srcadr, pp->a_lastcode);
667 static void
668 neoclock4x_poll(int unit,
669 struct peer *peer)
671 struct neoclock4x_unit *up;
672 struct refclockproc *pp;
674 pp = peer->procptr;
675 up = (struct neoclock4x_unit *)pp->unitptr;
677 pp->polls++;
678 up->recvnow = 1;
681 static void
682 neoclock4x_control(int unit,
683 struct refclockstat *in,
684 struct refclockstat *out,
685 struct peer *peer)
687 struct neoclock4x_unit *up;
688 struct refclockproc *pp;
690 if(NULL == peer)
692 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
693 return;
696 pp = peer->procptr;
697 if(NULL == pp)
699 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
700 return;
703 up = (struct neoclock4x_unit *)pp->unitptr;
704 if(NULL == up)
706 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
707 return;
710 if(NULL != in)
712 /* check to see if a user supplied time offset is given */
713 if(in->haveflags & CLK_HAVETIME1)
715 pp->fudgetime1 = in->fudgetime1;
716 NLOG(NLOG_CLOCKINFO)
717 msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
718 unit, pp->fudgetime1);
721 /* notify */
722 if(pp->sloppyclockflag & CLK_FLAG1)
724 NLOG(NLOG_CLOCKINFO)
725 msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
727 else
729 NLOG(NLOG_CLOCKINFO)
730 msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
734 if(NULL != out)
736 char *tt;
737 char tmpbuf[80];
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,
746 up->utc_msec);
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\"");
761 else
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\"");
768 else
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\"");
775 else
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);
788 static int
789 neol_hexatoi_len(const char str[],
790 int *result,
791 int maxlen)
793 int hexdigit;
794 int i;
795 int n = 0;
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;
802 *result = n;
803 return (n);
806 static int
807 neol_atoi_len(const char str[],
808 int *result,
809 int maxlen)
811 int digit;
812 int i;
813 int n = 0;
815 for(i=0; isdigit((int)str[i]) && i < maxlen; i++)
817 digit = str[i] - '0';
818 n = 10 * n + digit;
820 *result = n;
821 return (n);
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)
839 static unsigned long
840 neol_mktime(int year,
841 int mon,
842 int day,
843 int hour,
844 int min,
845 int sec)
847 if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */
848 mon += 12; /* Puts Feb last since it has leap day */
849 year -= 1;
851 return (((
852 (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
853 year*365 - 719499
854 )*24 + hour /* now have hours */
855 )*60 + min /* now have minutes */
856 )*60 + sec; /* finally seconds */
859 static void
860 neol_localtime(unsigned long utc,
861 int* year,
862 int* month,
863 int* day,
864 int* hour,
865 int* min,
866 int* sec)
868 *sec = utc % 60;
869 utc /= 60;
870 *min = utc % 60;
871 utc /= 60;
872 *hour = utc % 24;
873 utc /= 24;
875 /* JDN Date 1/1/1970 */
876 neol_jdn_to_ymd(utc + 2440588L, year, month, day);
879 static void
880 neol_jdn_to_ymd(unsigned long jdn,
881 int *yy,
882 int *mm,
883 int *dd)
885 unsigned long x, z, m, d, y;
886 unsigned long daysPer400Years = 146097UL;
887 unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
889 x = jdn + 68569UL;
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;
896 x = m / 11UL;
897 m = m + 2UL - 12UL * x;
898 y = 100UL * (z - 49UL) + y + x;
900 *yy = (int)y;
901 *mm = (int)m;
902 *dd = (int)d;
905 #if !defined(NEOCLOCK4X_FIRMWARE)
906 static int
907 neol_query_firmware(int fd,
908 int unit,
909 char *firmware,
910 int maxlen)
912 char tmpbuf[256];
913 int len;
914 int lastsearch;
915 unsigned char c;
916 int last_c_was_crlf;
917 int last_crlf_conv_len;
918 int init;
919 int read_errors;
920 int flag = 0;
921 int chars_read;
923 /* wait a little bit */
924 sleep(1);
925 if(-1 != write(fd, "V", 1))
927 /* wait a little bit */
928 sleep(1);
929 memset(tmpbuf, 0x00, sizeof(tmpbuf));
931 len = 0;
932 lastsearch = 0;
933 last_c_was_crlf = 0;
934 last_crlf_conv_len = 0;
935 init = 1;
936 read_errors = 0;
937 chars_read = 0;
938 for(;;)
940 if(read_errors > 5)
942 msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
943 strcpy(tmpbuf, "unknown due to timeout");
944 break;
946 if(chars_read > 500)
948 msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
949 strcpy(tmpbuf, "unknown due to garbage input");
950 break;
952 if(-1 == read(fd, &c, 1))
954 if(EAGAIN != errno)
956 msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %s", unit ,strerror(errno));
957 read_errors++;
959 else
961 sleep(1);
963 continue;
965 else
967 chars_read++;
970 if(init)
972 if(0xA9 != c) /* wait for (c) char in input stream */
973 continue;
975 strcpy(tmpbuf, "(c)");
976 len = 3;
977 init = 0;
978 continue;
981 #if 0
982 msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
983 #endif
985 if(0x0A == c || 0x0D == c)
987 if(last_c_was_crlf)
989 char *ptr;
990 ptr = strstr(&tmpbuf[lastsearch], "S/N");
991 if(NULL != ptr)
993 tmpbuf[last_crlf_conv_len] = 0;
994 flag = 1;
995 break;
997 /* convert \n to / */
998 last_crlf_conv_len = len;
999 tmpbuf[len++] = ' ';
1000 tmpbuf[len++] = '/';
1001 tmpbuf[len++] = ' ';
1002 lastsearch = len;
1004 last_c_was_crlf = 1;
1006 else
1008 last_c_was_crlf = 0;
1009 if(0x00 != c)
1010 tmpbuf[len++] = (char) c;
1012 tmpbuf[len] = '\0';
1013 if(len > sizeof(tmpbuf)-5)
1014 break;
1017 else
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';
1025 if(flag)
1027 NLOG(NLOG_CLOCKINFO)
1028 msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
1031 return (flag);
1034 static int
1035 neol_check_firmware(int unit,
1036 const char *firmware,
1037 char *firmwaretag)
1039 char *ptr;
1041 *firmwaretag = '?';
1042 ptr = strstr(firmware, "NDF:");
1043 if(NULL != ptr)
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);
1055 return (0);
1058 return (1);
1060 #endif
1062 #else
1063 int refclock_neoclock4x_bs;
1064 #endif /* REFCLOCK */
1067 * History:
1068 * refclock_neoclock4x.c
1070 * 2002/04/27 cjh
1071 * Revision 1.0 first release
1073 * 2002/07/15 cjh
1074 * preparing for bitkeeper reposity
1076 * 2002/09/09 cjh
1077 * Revision 1.1
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.
1087 * 2003/01/08 cjh
1088 * Revision 1.11
1089 * - changing xprinf to xnprinf to avoid buffer overflows
1090 * - change some logic
1091 * - fixed memory leaks if drivers can't initialize
1093 * 2003/01/10 cjh
1094 * Revision 1.12
1095 * - replaced ldiv
1096 * - add code to support FreeBSD
1098 * 2003/07/07 cjh
1099 * Revision 1.13
1100 * - fix reporting of clock status
1101 * changes. previously a bad clock
1102 * status was never reset.
1104 * 2004/04/07 cjh
1105 * Revision 1.14
1106 * - open serial port in a way
1107 * AIX and some other OS can
1108 * handle much better
1110 * 2006/01/11 cjh
1111 * Revision 1.15
1112 * - remove some unsued #ifdefs
1113 * - fix nsec calculation, closes #499