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