1 /* 2 * 3 * refclock_neoclock4x.c 4 * - NeoClock4X driver for DCF77 or FIA Timecode 5 * 6 * Date: 2002-04-27 1.0 7 * 8 * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir 9 * for details about the NeoClock4X device 10 * 11 * Copyright (C) 2002 by Linum Software GmbH <support@linum.com> 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 * 17 * 18 */ 19 20 #ifdef HAVE_CONFIG_H 21 # include "config.h" 22 #endif 23 24 #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X)) 25 26 #include <unistd.h> 27 #include <sys/time.h> 28 #include <sys/types.h> 29 #include <termios.h> 30 #include <sys/ioctl.h> 31 #include <ctype.h> 32 33 #include "ntpd.h" 34 #include "ntp_io.h" 35 #include "ntp_control.h" 36 #include "ntp_refclock.h" 37 #include "ntp_unixtime.h" 38 #include "ntp_stdlib.h" 39 40 #if defined HAVE_SYS_MODEM_H 41 # include <sys/modem.h> 42 # define TIOCMSET MCSETA 43 # define TIOCMGET MCGETA 44 # define TIOCM_RTS MRTS 45 #endif 46 47 #ifdef HAVE_TERMIOS_H 48 # ifdef TERMIOS_NEEDS__SVID3 49 # define _SVID3 50 # endif 51 # include <termios.h> 52 # ifdef TERMIOS_NEEDS__SVID3 53 # undef _SVID3 54 # endif 55 #endif 56 57 #ifdef HAVE_SYS_IOCTL_H 58 # include <sys/ioctl.h> 59 #endif 60 61 #define NEOCLOCK4X_TIMECODELEN 37 62 63 #define NEOCLOCK4X_OFFSET_SERIAL 3 64 #define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9 65 #define NEOCLOCK4X_OFFSET_DAY 12 66 #define NEOCLOCK4X_OFFSET_MONTH 14 67 #define NEOCLOCK4X_OFFSET_YEAR 16 68 #define NEOCLOCK4X_OFFSET_HOUR 18 69 #define NEOCLOCK4X_OFFSET_MINUTE 20 70 #define NEOCLOCK4X_OFFSET_SECOND 22 71 #define NEOCLOCK4X_OFFSET_HSEC 24 72 #define NEOCLOCK4X_OFFSET_DOW 26 73 #define NEOCLOCK4X_OFFSET_TIMESOURCE 28 74 #define NEOCLOCK4X_OFFSET_DSTSTATUS 29 75 #define NEOCLOCK4X_OFFSET_QUARZSTATUS 30 76 #define NEOCLOCK4X_OFFSET_ANTENNA1 31 77 #define NEOCLOCK4X_OFFSET_ANTENNA2 33 78 #define NEOCLOCK4X_OFFSET_CRC 35 79 80 struct neoclock4x_unit { 81 l_fp laststamp; /* last receive timestamp */ 82 short unit; /* NTP refclock unit number */ 83 u_long polled; /* flag to detect noreplies */ 84 char leap_status; /* leap second flag */ 85 int recvnow; 86 87 char firmware[80]; 88 char serial[7]; 89 char radiosignal[4]; 90 char timesource; 91 char dststatus; 92 char quarzstatus; 93 int antenna1; 94 int antenna2; 95 int utc_year; 96 int utc_month; 97 int utc_day; 98 int utc_hour; 99 int utc_minute; 100 int utc_second; 101 int utc_msec; 102 }; 103 104 static int neoclock4x_start P((int, struct peer *)); 105 static void neoclock4x_shutdown P((int, struct peer *)); 106 static void neoclock4x_receive P((struct recvbuf *)); 107 static void neoclock4x_poll P((int, struct peer *)); 108 static void neoclock4x_control P((int, struct refclockstat *, struct refclockstat *, struct peer *)); 109 110 static int neol_atoi_len P((const char str[], int *, int)); 111 static int neol_hexatoi_len P((const char str[], int *, int)); 112 static void neol_jdn_to_ymd P((unsigned long, int *, int *, int *)); 113 static void neol_localtime P((unsigned long, int* , int*, int*, int*, int*, int*)); 114 static unsigned long neol_mktime P((int, int, int, int, int, int)); 115 static void neol_mdelay P((int)); 116 static int neol_query_firmware P((int, int, char *, int)); 117 118 struct refclock refclock_neoclock4x = { 119 neoclock4x_start, /* start up driver */ 120 neoclock4x_shutdown, /* shut down driver */ 121 neoclock4x_poll, /* transmit poll message */ 122 neoclock4x_control, 123 noentry, /* initialize driver (not used) */ 124 noentry, /* not used */ 125 NOFLAGS /* not used */ 126 }; 127 128 static int 129 neoclock4x_start(int unit, 130 struct peer *peer) 131 { 132 struct neoclock4x_unit *up; 133 struct refclockproc *pp; 134 int fd; 135 char dev[20]; 136 int sl232; 137 struct termios termsettings; 138 int tries; 139 140 (void) sprintf(dev, "/dev/neoclock4x-%d", unit); 141 142 /* LDISC_STD, LDISC_RAW 143 * Open serial port. Use CLK line discipline, if available. 144 */ 145 fd = refclock_open(dev, B2400, LDISC_CLK); 146 if(fd <= 0) 147 { 148 return (0); 149 } 150 151 #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) 152 /* turn on RTS, and DTR for power supply */ 153 /* NeoClock4x is powered from serial line */ 154 if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1) 155 { 156 msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit); 157 } 158 #ifdef TIOCM_RTS 159 sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */ 160 #else 161 sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */ 162 #endif 163 if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1) 164 { 165 msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit); 166 } 167 168 if(ioctl(fd, TCGETS, (caddr_t)&termsettings) == -1) 169 { 170 msyslog(LOG_CRIT, "NeoClock4X(%d): can't query serial port settings: %m", unit); 171 } 172 173 /* 2400 Baud mit 8N2 */ 174 termsettings.c_cflag &= ~PARENB; 175 termsettings.c_cflag |= CSTOPB; 176 termsettings.c_cflag &= ~CSIZE; 177 termsettings.c_cflag |= CS8; 178 179 if(ioctl(fd, TCSETS, &termsettings) == -1) 180 { 181 msyslog(LOG_CRIT, "NeoClock4X(%d): can't set serial port to 2400 8N2: %m", unit); 182 } 183 #else 184 msyslog(LOG_EMERG, "NeoClock4X(%d): OS interface is incapable of setting DTR/RTS to power NeoClock4X", 185 unit); 186 #endif 187 188 up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit)); 189 if(!(up)) 190 { 191 msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit); 192 (void) close(fd); 193 return (0); 194 } 195 196 memset((char *)up, 0, sizeof(struct neoclock4x_unit)); 197 pp = peer->procptr; 198 pp->clockdesc = "NeoClock4X"; 199 pp->unitptr = (caddr_t)up; 200 pp->io.clock_recv = neoclock4x_receive; 201 pp->io.srcclock = (caddr_t)peer; 202 pp->io.datalen = 0; 203 pp->io.fd = fd; 204 /* no time is given by user! use 169.583333 ms to compensate the serial line delay 205 * formula is: 206 * 2400 Baud / 11 bit = 218.18 charaters per second 207 * (NeoClock4X timecode len) 208 */ 209 pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0; 210 211 if (!io_addclock(&pp->io)) 212 { 213 msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m",unit); 214 (void) close(fd); 215 free(up); 216 return (0); 217 } 218 219 /* 220 * Initialize miscellaneous variables 221 */ 222 peer->precision = -10; 223 peer->burst = NSTAGE; 224 memcpy((char *)&pp->refid, "neol", 4); 225 226 up->leap_status = 0; 227 up->unit = unit; 228 strcpy(up->firmware, "?"); 229 strcpy(up->serial, "?"); 230 strcpy(up->radiosignal, "?"); 231 up->timesource = '?'; 232 up->dststatus = '?'; 233 up->quarzstatus = '?'; 234 up->antenna1 = -1; 235 up->antenna2 = -1; 236 up->utc_year = 0; 237 up->utc_month = 0; 238 up->utc_day = 0; 239 up->utc_hour = 0; 240 up->utc_minute = 0; 241 up->utc_second = 0; 242 up->utc_msec = 0; 243 244 for(tries=0; tries < 5; tries++) 245 { 246 /* 247 * Wait 3 second for receiver to power up 248 */ 249 NLOG(NLOG_CLOCKINFO) 250 msyslog(LOG_INFO, "NeoClock4X(%d): try query NeoClock4X firmware version (%d/5)", unit, tries); 251 sleep(3); 252 if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware))) 253 { 254 break; 255 } 256 } 257 258 NLOG(NLOG_CLOCKINFO) 259 msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit); 260 261 return (1); 262 } 263 264 static void 265 neoclock4x_shutdown(int unit, 266 struct peer *peer) 267 { 268 struct neoclock4x_unit *up; 269 struct refclockproc *pp; 270 int sl232; 271 272 pp = peer->procptr; 273 up = (struct neoclock4x_unit *)pp->unitptr; 274 275 #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) 276 /* turn on RTS, and DTR for power supply */ 277 /* NeoClock4x is powered from serial line */ 278 if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1) 279 { 280 msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit); 281 } 282 #ifdef TIOCM_RTS 283 sl232 &= ~(TIOCM_DTR | TIOCM_RTS); /* turn on RTS, and DTR for power supply */ 284 #else 285 sl232 &= ~(CIOCM_DTR | CIOCM_RTS); /* turn on RTS, and DTR for power supply */ 286 #endif 287 if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1) 288 { 289 msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit); 290 } 291 #endif 292 msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit); 293 294 io_closeclock(&pp->io); 295 free(up); 296 NLOG(NLOG_CLOCKINFO) 297 msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit); 298 } 299 300 static void 301 neoclock4x_receive(struct recvbuf *rbufp) 302 { 303 struct neoclock4x_unit *up; 304 struct refclockproc *pp; 305 struct peer *peer; 306 unsigned long calc_utc; 307 int day; 308 int month; /* ddd conversion */ 309 int c; 310 unsigned char calc_chksum; 311 int recv_chksum; 312 313 peer = (struct peer *)rbufp->recv_srcclock; 314 pp = peer->procptr; 315 up = (struct neoclock4x_unit *)pp->unitptr; 316 317 /* wait till poll interval is reached */ 318 if(0 == up->recvnow) 319 return; 320 321 /* reset poll interval flag */ 322 up->recvnow = 0; 323 324 /* read last received timecode */ 325 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); 326 327 if(NEOCLOCK4X_TIMECODELEN != pp->lencode) 328 { 329 NLOG(NLOG_CLOCKEVENT) 330 msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s", 331 up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode); 332 refclock_report(peer, CEVNT_BADREPLY); 333 return; 334 } 335 336 neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2); 337 338 /* calculate checksum */ 339 calc_chksum = 0; 340 for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++) 341 { 342 calc_chksum += pp->a_lastcode[c]; 343 } 344 if(recv_chksum != calc_chksum) 345 { 346 NLOG(NLOG_CLOCKEVENT) 347 msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s", 348 up->unit, pp->a_lastcode); 349 refclock_report(peer, CEVNT_BADREPLY); 350 return; 351 } 352 353 /* Allow synchronization even is quartz clock is 354 * never initialized. 355 * WARNING: This is dangerous! 356 */ 357 up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS]; 358 if(0==(pp->sloppyclockflag & CLK_FLAG2)) 359 { 360 if('I' != up->quarzstatus) 361 { 362 NLOG(NLOG_CLOCKEVENT) 363 msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s", 364 up->unit, pp->a_lastcode); 365 pp->leap = LEAP_NOTINSYNC; 366 refclock_report(peer, CEVNT_BADDATE); 367 return; 368 } 369 } 370 if('I' != up->quarzstatus) 371 { 372 NLOG(NLOG_CLOCKEVENT) 373 msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s", 374 up->unit, pp->a_lastcode); 375 } 376 377 /* 378 * If NeoClock4X is not synchronized to a radio clock 379 * check if we're allowed to synchronize with the quartz 380 * clock. 381 */ 382 up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE]; 383 if(0==(pp->sloppyclockflag & CLK_FLAG2)) 384 { 385 if('A' != up->timesource) 386 { 387 /* not allowed to sync with quartz clock */ 388 if(0==(pp->sloppyclockflag & CLK_FLAG1)) 389 { 390 refclock_report(peer, CEVNT_BADTIME); 391 pp->leap = LEAP_NOTINSYNC; 392 return; 393 } 394 } 395 } 396 397 /* this should only used when first install is done */ 398 if(pp->sloppyclockflag & CLK_FLAG4) 399 { 400 msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s", 401 up->unit, pp->a_lastcode); 402 } 403 404 /* 123456789012345678901234567890123456789012345 */ 405 /* S/N123456DCF1004021010001202ASX1213CR\r\n */ 406 407 neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2); 408 neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2); 409 neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2); 410 neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2); 411 neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2); 412 neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2); 413 neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &pp->msec, 2); 414 pp->msec *= 10; /* convert 1/100s from neoclock to real miliseconds */ 415 416 memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3); 417 up->radiosignal[3] = 0; 418 memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6); 419 up->serial[6] = 0; 420 up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS]; 421 neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2); 422 neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2); 423 424 /* 425 Validate received values at least enough to prevent internal 426 array-bounds problems, etc. 427 */ 428 if((pp->hour < 0) || (pp->hour > 23) || 429 (pp->minute < 0) || (pp->minute > 59) || 430 (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || 431 (day < 1) || (day > 31) || 432 (month < 1) || (month > 12) || 433 (pp->year < 0) || (pp->year > 99)) { 434 /* Data out of range. */ 435 NLOG(NLOG_CLOCKEVENT) 436 msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s", 437 up->unit, pp->a_lastcode); 438 refclock_report(peer, CEVNT_BADDATE); 439 return; 440 } 441 442 /* Year-2000 check! */ 443 /* wrap 2-digit date into 4-digit */ 444 445 if(pp->year < YEAR_PIVOT) /* < 98 */ 446 { 447 pp->year += 100; 448 } 449 pp->year += 1900; 450 451 calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second); 452 calc_utc -= 3600; 453 if('S' == up->dststatus) 454 calc_utc -= 3600; 455 neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second); 456 457 /* 458 some preparations 459 */ 460 pp->day = ymd2yd(pp->year,month,day); 461 pp->leap = 0; 462 463 464 if(pp->sloppyclockflag & CLK_FLAG4) 465 { 466 msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03d", 467 up->unit, 468 pp->year, month, day, 469 pp->hour, pp->minute, pp->second, pp->msec); 470 } 471 472 up->utc_year = pp->year; 473 up->utc_month = month; 474 up->utc_day = day; 475 up->utc_hour = pp->hour; 476 up->utc_minute = pp->minute; 477 up->utc_second = pp->second; 478 up->utc_msec = pp->msec; 479 480 if(!refclock_process(pp)) 481 { 482 NLOG(NLOG_CLOCKEVENT) 483 msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit); 484 refclock_report(peer, CEVNT_FAULT); 485 return; 486 } 487 refclock_receive(peer); 488 489 record_clock_stats(&peer->srcadr, pp->a_lastcode); 490 } 491 492 static void 493 neoclock4x_poll(int unit, 494 struct peer *peer) 495 { 496 struct neoclock4x_unit *up; 497 struct refclockproc *pp; 498 499 pp = peer->procptr; 500 up = (struct neoclock4x_unit *)pp->unitptr; 501 502 pp->polls++; 503 up->recvnow = 1; 504 } 505 506 static void 507 neoclock4x_control(int unit, 508 struct refclockstat *in, 509 struct refclockstat *out, 510 struct peer *peer) 511 { 512 struct neoclock4x_unit *up; 513 struct refclockproc *pp; 514 515 if(NULL == peer) 516 { 517 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 518 return; 519 } 520 521 pp = peer->procptr; 522 if(NULL == pp) 523 { 524 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 525 return; 526 } 527 528 up = (struct neoclock4x_unit *)pp->unitptr; 529 if(NULL == up) 530 { 531 msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 532 return; 533 } 534 535 if(NULL != in) 536 { 537 /* check to see if a user supplied time offset is given */ 538 if(in->haveflags & CLK_HAVETIME1) 539 { 540 pp->fudgetime1 = in->fudgetime1; 541 NLOG(NLOG_CLOCKINFO) 542 msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.", 543 unit, pp->fudgetime1); 544 } 545 546 /* notify */ 547 if(pp->sloppyclockflag & CLK_FLAG1) 548 { 549 NLOG(NLOG_CLOCKINFO) 550 msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit); 551 } 552 else 553 { 554 NLOG(NLOG_CLOCKINFO) 555 msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit); 556 } 557 } 558 559 if(NULL != out) 560 { 561 static char outstatus[800]; /* status output buffer */ 562 char *tt; 563 char tmpbuf[80]; 564 565 outstatus[0] = '\0'; 566 out->kv_list = (struct ctl_var *)0; 567 out->type = REFCLK_NEOCLOCK4X; 568 569 sprintf(tmpbuf, "%04d-%02d-%02d %02d:%02d:%02d.%03d", 570 up->utc_year, up->utc_month, up->utc_day, 571 up->utc_hour, up->utc_minute, up->utc_second, 572 up->utc_msec); 573 574 tt = add_var(&out->kv_list, 512, RO|DEF); 575 tt += sprintf(tt, "calc_utc=\"%s\"", tmpbuf); 576 tt = add_var(&out->kv_list, 512, RO|DEF); 577 tt += sprintf(tt, "radiosignal=\"%s\"", up->radiosignal); 578 tt = add_var(&out->kv_list, 512, RO|DEF); 579 tt += sprintf(tt, "antenna1=\"%d\"", up->antenna1); 580 tt = add_var(&out->kv_list, 512, RO|DEF); 581 tt += sprintf(tt, "antenna2=\"%d\"", up->antenna2); 582 tt = add_var(&out->kv_list, 512, RO|DEF); 583 if('A' == up->timesource) 584 tt += sprintf(tt, "timesource=\"radio\""); 585 else if('C' == up->timesource) 586 tt += sprintf(tt, "timesource=\"quartz\""); 587 else 588 tt += sprintf(tt, "timesource=\"unknown\""); 589 tt = add_var(&out->kv_list, 512, RO|DEF); 590 if('I' == up->quarzstatus) 591 tt += sprintf(tt, "quartzstatus=\"synchronized\""); 592 else if('X' == up->quarzstatus) 593 tt += sprintf(tt, "quartzstatus=\"not synchronized\""); 594 else 595 tt += sprintf(tt, "quartzstatus=\"unknown\""); 596 tt = add_var(&out->kv_list, 512, RO|DEF); 597 if('S' == up->dststatus) 598 tt += sprintf(tt, "dststatus=\"summer\""); 599 else if('W' == up->dststatus) 600 tt += sprintf(tt, "dststatus=\"winter\""); 601 else 602 tt += sprintf(tt, "dststatus=\"unknown\""); 603 tt = add_var(&out->kv_list, 512, RO|DEF); 604 tt += sprintf(tt, "firmware=\"%s\"", up->firmware); 605 tt = add_var(&out->kv_list, 512, RO|DEF); 606 tt += sprintf(tt, "serialnumber=\"%s\"", up->serial); 607 tt = add_var(&out->kv_list, 512, RO|DEF); 608 } 609 } 610 611 static int neol_hexatoi_len(const char str[], 612 int *result, 613 int maxlen) 614 { 615 int hexdigit; 616 int i; 617 int n = 0; 618 619 for(i=0; isxdigit(str[i]) && i < maxlen; i++) 620 { 621 hexdigit = isdigit(str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10; 622 n = 16 * n + hexdigit; 623 } 624 *result = n; 625 return (n); 626 } 627 628 int neol_atoi_len(const char str[], 629 int *result, 630 int maxlen) 631 { 632 int digit; 633 int i; 634 int n = 0; 635 636 for(i=0; isdigit(str[i]) && i < maxlen; i++) 637 { 638 digit = str[i] - '0'; 639 n = 10 * n + digit; 640 } 641 *result = n; 642 return (n); 643 } 644 645 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. 646 * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 647 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. 648 * 649 * [For the Julian calendar (which was used in Russia before 1917, 650 * Britain & colonies before 1752, anywhere else before 1582, 651 * and is still in use by some communities) leave out the 652 * -year/100+year/400 terms, and add 10.] 653 * 654 * This algorithm was first published by Gauss (I think). 655 * 656 * WARNING: this function will overflow on 2106-02-07 06:28:16 on 657 * machines were long is 32-bit! (However, as time_t is signed, we 658 * will already get problems at other places on 2038-01-19 03:14:08) 659 */ 660 static unsigned long neol_mktime(int year, 661 int mon, 662 int day, 663 int hour, 664 int min, 665 int sec) 666 { 667 if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */ 668 mon += 12; /* Puts Feb last since it has leap day */ 669 year -= 1; 670 } 671 return ((( 672 (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + 673 year*365 - 719499 674 )*24 + hour /* now have hours */ 675 )*60 + min /* now have minutes */ 676 )*60 + sec; /* finally seconds */ 677 } 678 679 static void neol_localtime(unsigned long utc, 680 int* year, 681 int* month, 682 int* day, 683 int* hour, 684 int* minute, 685 int* second) 686 { 687 ldiv_t d; 688 689 /* Sekunden */ 690 d = ldiv(utc, 60); 691 *second = d.rem; 692 693 /* Minute */ 694 d = ldiv(d.quot, 60); 695 *minute = d.rem; 696 697 /* Stunden */ 698 d = ldiv(d.quot, 24); 699 *hour = d.rem; 700 701 /* JDN Date 1/1/1970 */ 702 neol_jdn_to_ymd(d.quot + 2440588L, year, month, day); 703 } 704 705 static void neol_jdn_to_ymd(unsigned long jdn, 706 int *yy, 707 int *mm, 708 int *dd) 709 { 710 unsigned long x, z, m, d, y; 711 unsigned long daysPer400Years = 146097UL; 712 unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL; 713 714 x = jdn + 68569UL; 715 z = 4UL * x / daysPer400Years; 716 x = x - (daysPer400Years * z + 3UL) / 4UL; 717 y = 4000UL * (x + 1) / fudgedDaysPer4000Years; 718 x = x - 1461UL * y / 4UL + 31UL; 719 m = 80UL * x / 2447UL; 720 d = x - 2447UL * m / 80UL; 721 x = m / 11UL; 722 m = m + 2UL - 12UL * x; 723 y = 100UL * (z - 49UL) + y + x; 724 725 *yy = (int)y; 726 *mm = (int)m; 727 *dd = (int)d; 728 } 729 730 /* 731 * delay in milliseconds 732 */ 733 static void 734 neol_mdelay(int milliseconds) 735 { 736 struct timeval tv; 737 738 if (milliseconds) 739 { 740 tv.tv_sec = 0; 741 tv.tv_usec = milliseconds * 1000; 742 select(1, NULL, NULL, NULL, &tv); 743 } 744 } 745 746 static int 747 neol_query_firmware(int fd, 748 int unit, 749 char *firmware, 750 int maxlen) 751 { 752 unsigned char tmpbuf[256]; 753 int len; 754 int lastsearch; 755 unsigned char c; 756 int last_c_was_crlf; 757 int last_crlf_conv_len; 758 int init; 759 int read_tries; 760 int flag = 0; 761 762 /* wait a little bit */ 763 neol_mdelay(250); 764 if(-1 != write(fd, "V", 1)) 765 { 766 /* wait a little bit */ 767 neol_mdelay(250); 768 memset(tmpbuf, 0x00, sizeof(tmpbuf)); 769 770 len = 0; 771 lastsearch = 0; 772 last_c_was_crlf = 0; 773 last_crlf_conv_len = 0; 774 init = 1; 775 read_tries = 0; 776 for(;;) 777 { 778 if(read_tries++ > 500) 779 { 780 msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit); 781 strcpy(tmpbuf, "unknown due to timeout"); 782 break; 783 } 784 if(-1 == read(fd, &c, 1)) 785 { 786 neol_mdelay(25); 787 continue; 788 } 789 if(init) 790 { 791 if(0xA9 != c) /* wait for (c) char in input stream */ 792 continue; 793 794 strcpy(tmpbuf, "(c)"); 795 len = 3; 796 init = 0; 797 continue; 798 } 799 800 //msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c); 801 if(0x0A == c || 0x0D == c) 802 { 803 if(last_c_was_crlf) 804 { 805 char *ptr; 806 ptr = strstr(&tmpbuf[lastsearch], "S/N"); 807 if(NULL != ptr) 808 { 809 tmpbuf[last_crlf_conv_len] = 0; 810 flag = 1; 811 break; 812 } 813 /* convert \n to / */ 814 last_crlf_conv_len = len; 815 tmpbuf[len++] = ' '; 816 tmpbuf[len++] = '/'; 817 tmpbuf[len++] = ' '; 818 lastsearch = len; 819 } 820 last_c_was_crlf = 1; 821 } 822 else 823 { 824 last_c_was_crlf = 0; 825 if(0x00 != c) 826 tmpbuf[len++] = c; 827 } 828 tmpbuf[len] = '\0'; 829 if(len > sizeof(tmpbuf)-5) 830 break; 831 } 832 } 833 else 834 { 835 msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit); 836 strcpy(tmpbuf, "unknown error"); 837 } 838 strncpy(firmware, tmpbuf, maxlen); 839 firmware[maxlen] = '\0'; 840 841 if(flag) 842 { 843 NLOG(NLOG_CLOCKINFO) 844 msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware); 845 } 846 847 return (flag); 848 } 849 850 #else 851 int refclock_neoclock4x_bs; 852 #endif /* REFCLOCK */ 853 854 /* 855 * History: 856 * refclock_neoclock4x.c 857 * 858 * 2002/04/27 cjh 859 * Revision 1.0 first release 860 * 861 * 2002/0715 cjh 862 * preparing for bitkeeper reposity 863 * 864 */ 865