1 /* $NetBSD: refclock_neoclock4x.c,v 1.3 2010/12/04 23:08:35 christos Exp $ */ 2 3 /* 4 * 5 * Refclock_neoclock4x.c 6 * - NeoClock4X driver for DCF77 or FIA Timecode 7 * 8 * Date: 2006-01-11 v1.15 9 * 10 * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir 11 * for details about the NeoClock4X device 12 * 13 * Copyright (C) 2002-2004 by Linum Software GmbH <neoclock4x@linum.com> 14 * 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. 18 * 19 * 20 */ 21 22 #ifdef HAVE_CONFIG_H 23 # include "config.h" 24 #endif 25 26 #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X)) 27 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> 34 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" 41 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 50 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 60 61 #ifdef HAVE_SYS_IOCTL_H 62 # include <sys/ioctl.h> 63 #endif 64 65 /* 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. 70 * 71 */ 72 #if 0 73 #define NTP_PRE_420 74 #endif 75 76 /* 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. 81 * 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! 85 * 86 */ 87 88 #if 0 89 #define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A 90 #endif 91 92 /* at this time only firmware version A is known */ 93 #define NEOCLOCK4X_FIRMWARE_VERSION_A 'A' 94 95 #define NEOCLOCK4X_TIMECODELEN 37 96 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 113 114 #define NEOCLOCK4X_DRIVER_VERSION "1.15 (2006-01-11)" 115 116 #define NSEC_TO_MILLI 1000000 117 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; 124 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; 141 }; 142 143 static int neoclock4x_start (int, struct peer *); 144 static void neoclock4x_shutdown (int, struct peer *); 145 static void neoclock4x_receive (struct recvbuf *); 146 static void neoclock4x_poll (int, struct peer *); 147 static void neoclock4x_control (int, struct refclockstat *, struct refclockstat *, struct peer *); 148 149 static int neol_atoi_len (const char str[], int *, int); 150 static int neol_hexatoi_len (const char str[], int *, int); 151 static void neol_jdn_to_ymd (unsigned long, int *, int *, int *); 152 static void neol_localtime (unsigned long, int* , int*, int*, int*, int*, int*); 153 static unsigned long neol_mktime (int, int, int, int, int, int); 154 #if !defined(NEOCLOCK4X_FIRMWARE) 155 static int neol_query_firmware (int, int, char *, int); 156 static int neol_check_firmware (int, const char*, char *); 157 #endif 158 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 */ 167 }; 168 169 static int 170 neoclock4x_start(int unit, 171 struct peer *peer) 172 { 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 184 185 (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit); 186 187 /* LDISC_STD, LDISC_RAW 188 * Open serial port. Use CLK line discipline, if available. 189 */ 190 fd = refclock_open(dev, B2400, LDISC_STD); 191 if(fd <= 0) 192 { 193 return (0); 194 } 195 196 #if defined(HAVE_TERMIOS) 197 198 #if 1 199 if(tcgetattr(fd, &termsettings) < 0) 200 { 201 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); 202 (void) close(fd); 203 return (0); 204 } 205 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); 212 213 if(tcsetattr(fd, TCSANOW, &termsettings) < 0) 214 { 215 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); 216 (void) close(fd); 217 return (0); 218 } 219 220 #else 221 if(tcgetattr(fd, &termsettings) < 0) 222 { 223 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); 224 (void) close(fd); 225 return (0); 226 } 227 228 /* 2400 Baud 8N2 */ 229 termsettings.c_cflag &= ~PARENB; 230 termsettings.c_cflag |= CSTOPB; 231 termsettings.c_cflag &= ~CSIZE; 232 termsettings.c_cflag |= CS8; 233 234 if(tcsetattr(fd, TCSANOW, &termsettings) < 0) 235 { 236 msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); 237 (void) close(fd); 238 return (0); 239 } 240 #endif 241 242 #elif defined(HAVE_SYSV_TTYS) 243 if(ioctl(fd, TCGETA, &termsettings) < 0) 244 { 245 msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit); 246 (void) close(fd); 247 return (0); 248 } 249 250 /* 2400 Baud 8N2 */ 251 termsettings.c_cflag &= ~PARENB; 252 termsettings.c_cflag |= CSTOPB; 253 termsettings.c_cflag &= ~CSIZE; 254 termsettings.c_cflag |= CS8; 255 256 if(ioctl(fd, TCSETA, &termsettings) < 0) 257 { 258 msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit); 259 (void) close(fd); 260 return (0); 261 } 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 267 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) 272 { 273 msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit); 274 (void) close(fd); 275 return (0); 276 } 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) 283 { 284 msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit); 285 (void) close(fd); 286 return (0); 287 } 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 294 295 up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit)); 296 if(!(up)) 297 { 298 msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit); 299 (void) close(fd); 300 return (0); 301 } 302 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; 311 /* 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) 317 */ 318 pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0; 319 320 /* 321 * Initialize miscellaneous variables 322 */ 323 peer->precision = -10; 324 peer->burst = NSTAGE; 325 memcpy((char *)&pp->refid, "neol", 4); 326 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; 345 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++) 361 { 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))) 367 { 368 break; 369 } 370 } 371 372 /* can I handle this firmware version? */ 373 if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag)) 374 { 375 (void) close(fd); 376 pp->io.fd = -1; 377 free(pp->unitptr); 378 pp->unitptr = NULL; 379 return (0); 380 } 381 #endif 382 383 if(!io_addclock(&pp->io)) 384 { 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); 391 } 392 393 NLOG(NLOG_CLOCKINFO) 394 msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit); 395 396 return (1); 397 } 398 399 static void 400 neoclock4x_shutdown(int unit, 401 struct peer *peer) 402 { 403 struct neoclock4x_unit *up; 404 struct refclockproc *pp; 405 int sl232; 406 407 if(NULL != peer) 408 { 409 pp = peer->procptr; 410 if(pp != NULL) 411 { 412 up = (struct neoclock4x_unit *)pp->unitptr; 413 if(up != NULL) 414 { 415 if(-1 != pp->io.fd) 416 { 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) 421 { 422 msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", 423 unit); 424 } 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) 433 { 434 msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", 435 unit); 436 } 437 #endif 438 io_closeclock(&pp->io); 439 } 440 free(up); 441 pp->unitptr = NULL; 442 } 443 } 444 } 445 446 msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit); 447 448 NLOG(NLOG_CLOCKINFO) 449 msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit); 450 } 451 452 static void 453 neoclock4x_receive(struct recvbuf *rbufp) 454 { 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; 465 466 peer = (struct peer *)rbufp->recv_srcclock; 467 pp = peer->procptr; 468 up = (struct neoclock4x_unit *)pp->unitptr; 469 470 /* wait till poll interval is reached */ 471 if(0 == up->recvnow) 472 return; 473 474 /* reset poll interval flag */ 475 up->recvnow = 0; 476 477 /* read last received timecode */ 478 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); 479 pp->leap = LEAP_NOWARNING; 480 481 if(NEOCLOCK4X_TIMECODELEN != pp->lencode) 482 { 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; 488 } 489 490 neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2); 491 492 /* calculate checksum */ 493 calc_chksum = 0; 494 for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++) 495 { 496 calc_chksum += pp->a_lastcode[c]; 497 } 498 if(recv_chksum != calc_chksum) 499 { 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; 505 } 506 507 /* Allow synchronization even is quartz clock is 508 * never initialized. 509 * WARNING: This is dangerous! 510 */ 511 up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS]; 512 if(0==(pp->sloppyclockflag & CLK_FLAG2)) 513 { 514 if('I' != up->quarzstatus) 515 { 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; 522 } 523 } 524 if('I' != up->quarzstatus) 525 { 526 NLOG(NLOG_CLOCKEVENT) 527 msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s", 528 up->unit, pp->a_lastcode); 529 } 530 531 /* 532 * If NeoClock4X is not synchronized to a radio clock 533 * check if we're allowed to synchronize with the quartz 534 * clock. 535 */ 536 up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE]; 537 if(0==(pp->sloppyclockflag & CLK_FLAG2)) 538 { 539 if('A' != up->timesource) 540 { 541 /* not allowed to sync with quartz clock */ 542 if(0==(pp->sloppyclockflag & CLK_FLAG1)) 543 { 544 refclock_report(peer, CEVNT_BADTIME); 545 pp->leap = LEAP_NOTINSYNC; 546 return; 547 } 548 } 549 } 550 551 /* this should only used when first install is done */ 552 if(pp->sloppyclockflag & CLK_FLAG4) 553 { 554 msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s", 555 up->unit, pp->a_lastcode); 556 } 557 558 /* 123456789012345678901234567890123456789012345 */ 559 /* S/N123456DCF1004021010001202ASX1213CR\r\n */ 560 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 573 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); 581 582 /* 583 Validate received values at least enough to prevent internal 584 array-bounds problems, etc. 585 */ 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; 598 } 599 600 /* Year-2000 check not needed anymore. Same problem 601 * will arise at 2099 but what should we do...? 602 * 603 * wrap 2-digit date into 4-digit 604 * 605 * if(pp->year < YEAR_PIVOT) 606 * { 607 * pp->year += 100; 608 * } 609 */ 610 pp->year += 2000; 611 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); 619 620 /* 621 some preparations 622 */ 623 pp->day = ymd2yd(pp->year, month, day); 624 pp->leap = 0; 625 626 if(pp->sloppyclockflag & CLK_FLAG4) 627 { 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 637 ); 638 } 639 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 651 652 if(!refclock_process(pp)) 653 { 654 NLOG(NLOG_CLOCKEVENT) 655 msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit); 656 refclock_report(peer, CEVNT_FAULT); 657 return; 658 } 659 refclock_receive(peer); 660 661 /* report good status */ 662 refclock_report(peer, CEVNT_NOMINAL); 663 664 record_clock_stats(&peer->srcadr, pp->a_lastcode); 665 } 666 667 static void 668 neoclock4x_poll(int unit, 669 struct peer *peer) 670 { 671 struct neoclock4x_unit *up; 672 struct refclockproc *pp; 673 674 pp = peer->procptr; 675 up = (struct neoclock4x_unit *)pp->unitptr; 676 677 pp->polls++; 678 up->recvnow = 1; 679 } 680 681 static void 682 neoclock4x_control(int unit, 683 struct refclockstat *in, 684 struct refclockstat *out, 685 struct peer *peer) 686 { 687 struct neoclock4x_unit *up; 688 struct refclockproc *pp; 689 690 if(NULL == peer) 691 { 692 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 693 return; 694 } 695 696 pp = peer->procptr; 697 if(NULL == pp) 698 { 699 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 700 return; 701 } 702 703 up = (struct neoclock4x_unit *)pp->unitptr; 704 if(NULL == up) 705 { 706 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 707 return; 708 } 709 710 if(NULL != in) 711 { 712 /* check to see if a user supplied time offset is given */ 713 if(in->haveflags & CLK_HAVETIME1) 714 { 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); 719 } 720 721 /* notify */ 722 if(pp->sloppyclockflag & CLK_FLAG1) 723 { 724 NLOG(NLOG_CLOCKINFO) 725 msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit); 726 } 727 else 728 { 729 NLOG(NLOG_CLOCKINFO) 730 msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit); 731 } 732 } 733 734 if(NULL != out) 735 { 736 char *tt; 737 char tmpbuf[80]; 738 739 out->kv_list = (struct ctl_var *)0; 740 out->type = REFCLK_NEOCLOCK4X; 741 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); 749 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); 785 } 786 } 787 788 static int 789 neol_hexatoi_len(const char str[], 790 int *result, 791 int maxlen) 792 { 793 int hexdigit; 794 int i; 795 int n = 0; 796 797 for(i=0; isxdigit((unsigned char)str[i]) && i < maxlen; i++) 798 { 799 hexdigit = isdigit((unsigned char)str[i]) ? toupper((unsigned char)str[i]) - '0' : toupper((unsigned char)str[i]) - 'A' + 10; 800 n = 16 * n + hexdigit; 801 } 802 *result = n; 803 return (n); 804 } 805 806 static int 807 neol_atoi_len(const char str[], 808 int *result, 809 int maxlen) 810 { 811 int digit; 812 int i; 813 int n = 0; 814 815 for(i=0; isdigit((unsigned char)str[i]) && i < maxlen; i++) 816 { 817 digit = str[i] - '0'; 818 n = 10 * n + digit; 819 } 820 *result = n; 821 return (n); 822 } 823 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. 827 * 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.] 832 * 833 * This algorithm was first published by Gauss (I think). 834 * 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) 838 */ 839 static unsigned long 840 neol_mktime(int year, 841 int mon, 842 int day, 843 int hour, 844 int min, 845 int sec) 846 { 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; 850 } 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 */ 857 } 858 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) 867 { 868 *sec = utc % 60; 869 utc /= 60; 870 *min = utc % 60; 871 utc /= 60; 872 *hour = utc % 24; 873 utc /= 24; 874 875 /* JDN Date 1/1/1970 */ 876 neol_jdn_to_ymd(utc + 2440588L, year, month, day); 877 } 878 879 static void 880 neol_jdn_to_ymd(unsigned long jdn, 881 int *yy, 882 int *mm, 883 int *dd) 884 { 885 unsigned long x, z, m, d, y; 886 unsigned long daysPer400Years = 146097UL; 887 unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL; 888 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; 899 900 *yy = (int)y; 901 *mm = (int)m; 902 *dd = (int)d; 903 } 904 905 #if !defined(NEOCLOCK4X_FIRMWARE) 906 static int 907 neol_query_firmware(int fd, 908 int unit, 909 char *firmware, 910 int maxlen) 911 { 912 char tmpbuf[256]; 913 size_t 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; 922 923 /* wait a little bit */ 924 sleep(1); 925 if(-1 != write(fd, "V", 1)) 926 { 927 /* wait a little bit */ 928 sleep(1); 929 memset(tmpbuf, 0x00, sizeof(tmpbuf)); 930 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(;;) 939 { 940 if(read_errors > 5) 941 { 942 msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit); 943 strcpy(tmpbuf, "unknown due to timeout"); 944 break; 945 } 946 if(chars_read > 500) 947 { 948 msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit); 949 strcpy(tmpbuf, "unknown due to garbage input"); 950 break; 951 } 952 if(-1 == read(fd, &c, 1)) 953 { 954 if(EAGAIN != errno) 955 { 956 msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %s", unit ,strerror(errno)); 957 read_errors++; 958 } 959 else 960 { 961 sleep(1); 962 } 963 continue; 964 } 965 else 966 { 967 chars_read++; 968 } 969 970 if(init) 971 { 972 if(0xA9 != c) /* wait for (c) char in input stream */ 973 continue; 974 975 strcpy(tmpbuf, "(c)"); 976 len = 3; 977 init = 0; 978 continue; 979 } 980 981 #if 0 982 msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c); 983 #endif 984 985 if(0x0A == c || 0x0D == c) 986 { 987 if(last_c_was_crlf) 988 { 989 char *ptr; 990 ptr = strstr(&tmpbuf[lastsearch], "S/N"); 991 if(NULL != ptr) 992 { 993 tmpbuf[last_crlf_conv_len] = 0; 994 flag = 1; 995 break; 996 } 997 /* convert \n to / */ 998 last_crlf_conv_len = len; 999 tmpbuf[len++] = ' '; 1000 tmpbuf[len++] = '/'; 1001 tmpbuf[len++] = ' '; 1002 lastsearch = len; 1003 } 1004 last_c_was_crlf = 1; 1005 } 1006 else 1007 { 1008 last_c_was_crlf = 0; 1009 if(0x00 != c) 1010 tmpbuf[len++] = (char) c; 1011 } 1012 tmpbuf[len] = '\0'; 1013 if (len > sizeof(tmpbuf)-5) 1014 break; 1015 } 1016 } 1017 else 1018 { 1019 msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit); 1020 strcpy(tmpbuf, "unknown error"); 1021 } 1022 strncpy(firmware, tmpbuf, maxlen); 1023 firmware[maxlen] = '\0'; 1024 1025 if(flag) 1026 { 1027 NLOG(NLOG_CLOCKINFO) 1028 msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware); 1029 } 1030 1031 return (flag); 1032 } 1033 1034 static int 1035 neol_check_firmware(int unit, 1036 const char *firmware, 1037 char *firmwaretag) 1038 { 1039 char *ptr; 1040 1041 *firmwaretag = '?'; 1042 ptr = strstr(firmware, "NDF:"); 1043 if(NULL != ptr) 1044 { 1045 if((strlen(firmware) - strlen(ptr)) >= 7) 1046 { 1047 if(':' == *(ptr+5) && '*' == *(ptr+6)) 1048 *firmwaretag = *(ptr+4); 1049 } 1050 } 1051 1052 if('A' != *firmwaretag) 1053 { 1054 msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag); 1055 return (0); 1056 } 1057 1058 return (1); 1059 } 1060 #endif 1061 1062 #else 1063 int refclock_neoclock4x_bs; 1064 #endif /* REFCLOCK */ 1065 1066 /* 1067 * History: 1068 * refclock_neoclock4x.c 1069 * 1070 * 2002/04/27 cjh 1071 * Revision 1.0 first release 1072 * 1073 * 2002/07/15 cjh 1074 * preparing for bitkeeper reposity 1075 * 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. 1086 * 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 1092 * 1093 * 2003/01/10 cjh 1094 * Revision 1.12 1095 * - replaced ldiv 1096 * - add code to support FreeBSD 1097 * 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. 1103 * 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 1109 * 1110 * 2006/01/11 cjh 1111 * Revision 1.15 1112 * - remove some unsued #ifdefs 1113 * - fix nsec calculation, closes #499 1114 * 1115 */ 1116