1106424Sroberto/* 2106424Sroberto * 3132451Sroberto * Refclock_neoclock4x.c 4106424Sroberto * - NeoClock4X driver for DCF77 or FIA Timecode 5106424Sroberto * 6285612Sdelphij * Date: 2009-12-04 v1.16 7106424Sroberto * 8106424Sroberto * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir 9106424Sroberto * for details about the NeoClock4X device 10106424Sroberto * 11106424Sroberto */ 12106424Sroberto 13106424Sroberto#ifdef HAVE_CONFIG_H 14106424Sroberto# include "config.h" 15106424Sroberto#endif 16106424Sroberto 17106424Sroberto#if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X)) 18106424Sroberto 19106424Sroberto#include <unistd.h> 20106424Sroberto#include <sys/time.h> 21106424Sroberto#include <sys/types.h> 22106424Sroberto#include <termios.h> 23106424Sroberto#include <sys/ioctl.h> 24106424Sroberto#include <ctype.h> 25106424Sroberto 26106424Sroberto#include "ntpd.h" 27106424Sroberto#include "ntp_io.h" 28106424Sroberto#include "ntp_control.h" 29106424Sroberto#include "ntp_refclock.h" 30106424Sroberto#include "ntp_unixtime.h" 31106424Sroberto#include "ntp_stdlib.h" 32106424Sroberto 33106424Sroberto#if defined HAVE_SYS_MODEM_H 34106424Sroberto# include <sys/modem.h> 35182007Sroberto# ifndef __QNXNTO__ 36182007Sroberto# define TIOCMSET MCSETA 37182007Sroberto# define TIOCMGET MCGETA 38182007Sroberto# define TIOCM_RTS MRTS 39182007Sroberto# endif 40106424Sroberto#endif 41106424Sroberto 42106424Sroberto#ifdef HAVE_TERMIOS_H 43106424Sroberto# ifdef TERMIOS_NEEDS__SVID3 44106424Sroberto# define _SVID3 45106424Sroberto# endif 46106424Sroberto# include <termios.h> 47106424Sroberto# ifdef TERMIOS_NEEDS__SVID3 48106424Sroberto# undef _SVID3 49106424Sroberto# endif 50106424Sroberto#endif 51106424Sroberto 52106424Sroberto#ifdef HAVE_SYS_IOCTL_H 53106424Sroberto# include <sys/ioctl.h> 54106424Sroberto#endif 55106424Sroberto 56132451Sroberto/* 57182007Sroberto * NTP version 4.20 change the pp->msec field to pp->nsec. 58182007Sroberto * To allow to support older ntp versions with this sourcefile 59182007Sroberto * you can define NTP_PRE_420 to allow this driver to compile 60182007Sroberto * with ntp version back to 4.1.2. 61182007Sroberto * 62182007Sroberto */ 63182007Sroberto#if 0 64182007Sroberto#define NTP_PRE_420 65182007Sroberto#endif 66182007Sroberto 67182007Sroberto/* 68132451Sroberto * If you want the driver for whatever reason to not use 69132451Sroberto * the TX line to send anything to your NeoClock4X 70132451Sroberto * device you must tell the NTP refclock driver which 71132451Sroberto * firmware you NeoClock4X device uses. 72132451Sroberto * 73132451Sroberto * If you want to enable this feature change the "#if 0" 74132451Sroberto * line to "#if 1" and make sure that the defined firmware 75132451Sroberto * matches the firmware off your NeoClock4X receiver! 76132451Sroberto * 77132451Sroberto */ 78132451Sroberto 79132451Sroberto#if 0 80132451Sroberto#define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A 81132451Sroberto#endif 82132451Sroberto 83182007Sroberto/* at this time only firmware version A is known */ 84132451Sroberto#define NEOCLOCK4X_FIRMWARE_VERSION_A 'A' 85132451Sroberto 86106424Sroberto#define NEOCLOCK4X_TIMECODELEN 37 87106424Sroberto 88106424Sroberto#define NEOCLOCK4X_OFFSET_SERIAL 3 89106424Sroberto#define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9 90106424Sroberto#define NEOCLOCK4X_OFFSET_DAY 12 91106424Sroberto#define NEOCLOCK4X_OFFSET_MONTH 14 92106424Sroberto#define NEOCLOCK4X_OFFSET_YEAR 16 93106424Sroberto#define NEOCLOCK4X_OFFSET_HOUR 18 94106424Sroberto#define NEOCLOCK4X_OFFSET_MINUTE 20 95106424Sroberto#define NEOCLOCK4X_OFFSET_SECOND 22 96106424Sroberto#define NEOCLOCK4X_OFFSET_HSEC 24 97106424Sroberto#define NEOCLOCK4X_OFFSET_DOW 26 98106424Sroberto#define NEOCLOCK4X_OFFSET_TIMESOURCE 28 99106424Sroberto#define NEOCLOCK4X_OFFSET_DSTSTATUS 29 100106424Sroberto#define NEOCLOCK4X_OFFSET_QUARZSTATUS 30 101106424Sroberto#define NEOCLOCK4X_OFFSET_ANTENNA1 31 102106424Sroberto#define NEOCLOCK4X_OFFSET_ANTENNA2 33 103106424Sroberto#define NEOCLOCK4X_OFFSET_CRC 35 104106424Sroberto 105285612Sdelphij#define NEOCLOCK4X_DRIVER_VERSION "1.16 (2009-12-04)" 106132451Sroberto 107182007Sroberto#define NSEC_TO_MILLI 1000000 108182007Sroberto 109106424Srobertostruct neoclock4x_unit { 110106424Sroberto l_fp laststamp; /* last receive timestamp */ 111106424Sroberto short unit; /* NTP refclock unit number */ 112132451Sroberto u_long polled; /* flag to detect noreplies */ 113106424Sroberto char leap_status; /* leap second flag */ 114106424Sroberto int recvnow; 115132451Sroberto 116106424Sroberto char firmware[80]; 117132451Sroberto char firmwaretag; 118106424Sroberto char serial[7]; 119106424Sroberto char radiosignal[4]; 120106424Sroberto char timesource; 121106424Sroberto char dststatus; 122106424Sroberto char quarzstatus; 123106424Sroberto int antenna1; 124106424Sroberto int antenna2; 125106424Sroberto int utc_year; 126106424Sroberto int utc_month; 127106424Sroberto int utc_day; 128106424Sroberto int utc_hour; 129106424Sroberto int utc_minute; 130106424Sroberto int utc_second; 131106424Sroberto int utc_msec; 132106424Sroberto}; 133106424Sroberto 134285612Sdelphijstatic int neoclock4x_start (int, struct peer *); 135285612Sdelphijstatic void neoclock4x_shutdown (int, struct peer *); 136285612Sdelphijstatic void neoclock4x_receive (struct recvbuf *); 137285612Sdelphijstatic void neoclock4x_poll (int, struct peer *); 138285612Sdelphijstatic void neoclock4x_control (int, const struct refclockstat *, struct refclockstat *, struct peer *); 139106424Sroberto 140285612Sdelphijstatic int neol_atoi_len (const char str[], int *, int); 141285612Sdelphijstatic int neol_hexatoi_len (const char str[], int *, int); 142285612Sdelphijstatic void neol_jdn_to_ymd (unsigned long, int *, int *, int *); 143285612Sdelphijstatic void neol_localtime (unsigned long, int* , int*, int*, int*, int*, int*); 144285612Sdelphijstatic unsigned long neol_mktime (int, int, int, int, int, int); 145132451Sroberto#if !defined(NEOCLOCK4X_FIRMWARE) 146285612Sdelphijstatic int neol_query_firmware (int, int, char *, size_t); 147285612Sdelphijstatic int neol_check_firmware (int, const char*, char *); 148132451Sroberto#endif 149106424Sroberto 150106424Srobertostruct refclock refclock_neoclock4x = { 151106424Sroberto neoclock4x_start, /* start up driver */ 152106424Sroberto neoclock4x_shutdown, /* shut down driver */ 153106424Sroberto neoclock4x_poll, /* transmit poll message */ 154106424Sroberto neoclock4x_control, 155106424Sroberto noentry, /* initialize driver (not used) */ 156106424Sroberto noentry, /* not used */ 157106424Sroberto NOFLAGS /* not used */ 158106424Sroberto}; 159106424Sroberto 160106424Srobertostatic int 161106424Srobertoneoclock4x_start(int unit, 162106424Sroberto struct peer *peer) 163106424Sroberto{ 164106424Sroberto struct neoclock4x_unit *up; 165106424Sroberto struct refclockproc *pp; 166106424Sroberto int fd; 167106424Sroberto char dev[20]; 168106424Sroberto int sl232; 169132451Sroberto#if defined(HAVE_TERMIOS) 170106424Sroberto struct termios termsettings; 171132451Sroberto#endif 172132451Sroberto#if !defined(NEOCLOCK4X_FIRMWARE) 173106424Sroberto int tries; 174132451Sroberto#endif 175106424Sroberto 176132451Sroberto (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit); 177132451Sroberto 178106424Sroberto /* LDISC_STD, LDISC_RAW 179106424Sroberto * Open serial port. Use CLK line discipline, if available. 180106424Sroberto */ 181182007Sroberto fd = refclock_open(dev, B2400, LDISC_STD); 182106424Sroberto if(fd <= 0) 183106424Sroberto { 184106424Sroberto return (0); 185106424Sroberto } 186132451Sroberto 187132451Sroberto#if defined(HAVE_TERMIOS) 188182007Sroberto 189182007Sroberto#if 1 190132451Sroberto if(tcgetattr(fd, &termsettings) < 0) 191132451Sroberto { 192132451Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); 193132451Sroberto (void) close(fd); 194132451Sroberto return (0); 195132451Sroberto } 196132451Sroberto 197132451Sroberto /* 2400 Baud 8N2 */ 198182007Sroberto termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL; 199182007Sroberto termsettings.c_oflag = 0; 200182007Sroberto termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD; 201182007Sroberto (void)cfsetispeed(&termsettings, (u_int)B2400); 202182007Sroberto (void)cfsetospeed(&termsettings, (u_int)B2400); 203182007Sroberto 204182007Sroberto if(tcsetattr(fd, TCSANOW, &termsettings) < 0) 205182007Sroberto { 206182007Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); 207182007Sroberto (void) close(fd); 208182007Sroberto return (0); 209182007Sroberto } 210182007Sroberto 211182007Sroberto#else 212182007Sroberto if(tcgetattr(fd, &termsettings) < 0) 213182007Sroberto { 214182007Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); 215182007Sroberto (void) close(fd); 216182007Sroberto return (0); 217182007Sroberto } 218182007Sroberto 219182007Sroberto /* 2400 Baud 8N2 */ 220132451Sroberto termsettings.c_cflag &= ~PARENB; 221132451Sroberto termsettings.c_cflag |= CSTOPB; 222132451Sroberto termsettings.c_cflag &= ~CSIZE; 223132451Sroberto termsettings.c_cflag |= CS8; 224132451Sroberto 225132451Sroberto if(tcsetattr(fd, TCSANOW, &termsettings) < 0) 226132451Sroberto { 227132451Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); 228132451Sroberto (void) close(fd); 229132451Sroberto return (0); 230132451Sroberto } 231182007Sroberto#endif 232182007Sroberto 233132451Sroberto#elif defined(HAVE_SYSV_TTYS) 234132451Sroberto if(ioctl(fd, TCGETA, &termsettings) < 0) 235132451Sroberto { 236132451Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit); 237132451Sroberto (void) close(fd); 238132451Sroberto return (0); 239132451Sroberto } 240132451Sroberto 241132451Sroberto /* 2400 Baud 8N2 */ 242132451Sroberto termsettings.c_cflag &= ~PARENB; 243132451Sroberto termsettings.c_cflag |= CSTOPB; 244132451Sroberto termsettings.c_cflag &= ~CSIZE; 245132451Sroberto termsettings.c_cflag |= CS8; 246132451Sroberto 247132451Sroberto if(ioctl(fd, TCSETA, &termsettings) < 0) 248132451Sroberto { 249132451Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit); 250132451Sroberto (void) close(fd); 251132451Sroberto return (0); 252132451Sroberto } 253132451Sroberto#else 254132451Sroberto msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit); 255132451Sroberto (void) close(fd); 256132451Sroberto return (0); 257132451Sroberto#endif 258132451Sroberto 259106424Sroberto#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) 260106424Sroberto /* turn on RTS, and DTR for power supply */ 261106424Sroberto /* NeoClock4x is powered from serial line */ 262106424Sroberto if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1) 263106424Sroberto { 264106424Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit); 265132451Sroberto (void) close(fd); 266132451Sroberto return (0); 267106424Sroberto } 268106424Sroberto#ifdef TIOCM_RTS 269106424Sroberto sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */ 270106424Sroberto#else 271106424Sroberto sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */ 272106424Sroberto#endif 273106424Sroberto if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1) 274106424Sroberto { 275106424Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit); 276132451Sroberto (void) close(fd); 277132451Sroberto return (0); 278106424Sroberto } 279106424Sroberto#else 280132451Sroberto msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!", 281106424Sroberto unit); 282132451Sroberto (void) close(fd); 283132451Sroberto return (0); 284106424Sroberto#endif 285132451Sroberto 286106424Sroberto up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit)); 287106424Sroberto if(!(up)) 288106424Sroberto { 289106424Sroberto msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit); 290106424Sroberto (void) close(fd); 291106424Sroberto return (0); 292106424Sroberto } 293106424Sroberto 294106424Sroberto memset((char *)up, 0, sizeof(struct neoclock4x_unit)); 295106424Sroberto pp = peer->procptr; 296106424Sroberto pp->clockdesc = "NeoClock4X"; 297285612Sdelphij pp->unitptr = up; 298106424Sroberto pp->io.clock_recv = neoclock4x_receive; 299285612Sdelphij pp->io.srcclock = peer; 300106424Sroberto pp->io.datalen = 0; 301106424Sroberto pp->io.fd = fd; 302132451Sroberto /* 303132451Sroberto * no fudge time is given by user! 304132451Sroberto * use 169.583333 ms to compensate the serial line delay 305106424Sroberto * formula is: 306106424Sroberto * 2400 Baud / 11 bit = 218.18 charaters per second 307106424Sroberto * (NeoClock4X timecode len) 308106424Sroberto */ 309106424Sroberto pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0; 310106424Sroberto 311106424Sroberto /* 312106424Sroberto * Initialize miscellaneous variables 313106424Sroberto */ 314106424Sroberto peer->precision = -10; 315106424Sroberto memcpy((char *)&pp->refid, "neol", 4); 316132451Sroberto 317106424Sroberto up->leap_status = 0; 318106424Sroberto up->unit = unit; 319285612Sdelphij strlcpy(up->firmware, "?", sizeof(up->firmware)); 320132451Sroberto up->firmwaretag = '?'; 321285612Sdelphij strlcpy(up->serial, "?", sizeof(up->serial)); 322285612Sdelphij strlcpy(up->radiosignal, "?", sizeof(up->radiosignal)); 323106424Sroberto up->timesource = '?'; 324106424Sroberto up->dststatus = '?'; 325106424Sroberto up->quarzstatus = '?'; 326106424Sroberto up->antenna1 = -1; 327106424Sroberto up->antenna2 = -1; 328106424Sroberto up->utc_year = 0; 329106424Sroberto up->utc_month = 0; 330106424Sroberto up->utc_day = 0; 331106424Sroberto up->utc_hour = 0; 332106424Sroberto up->utc_minute = 0; 333106424Sroberto up->utc_second = 0; 334106424Sroberto up->utc_msec = 0; 335106424Sroberto 336132451Sroberto#if defined(NEOCLOCK4X_FIRMWARE) 337132451Sroberto#if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A 338285612Sdelphij strlcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)", 339285612Sdelphij sizeof(up->firmware)); 340132451Sroberto up->firmwaretag = 'A'; 341132451Sroberto#else 342182007Sroberto msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X", 343132451Sroberto unit); 344132451Sroberto (void) close(fd); 345132451Sroberto pp->io.fd = -1; 346132451Sroberto free(pp->unitptr); 347132451Sroberto pp->unitptr = NULL; 348132451Sroberto return (0); 349132451Sroberto#endif 350132451Sroberto#else 351106424Sroberto for(tries=0; tries < 5; tries++) 352106424Sroberto { 353106424Sroberto NLOG(NLOG_CLOCKINFO) 354132451Sroberto msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries); 355132451Sroberto /* wait 3 seconds for receiver to power up */ 356106424Sroberto sleep(3); 357106424Sroberto if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware))) 358106424Sroberto { 359106424Sroberto break; 360106424Sroberto } 361106424Sroberto } 362106424Sroberto 363132451Sroberto /* can I handle this firmware version? */ 364132451Sroberto if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag)) 365132451Sroberto { 366132451Sroberto (void) close(fd); 367132451Sroberto pp->io.fd = -1; 368132451Sroberto free(pp->unitptr); 369132451Sroberto pp->unitptr = NULL; 370132451Sroberto return (0); 371132451Sroberto } 372132451Sroberto#endif 373132451Sroberto 374132451Sroberto if(!io_addclock(&pp->io)) 375132451Sroberto { 376132451Sroberto msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit); 377132451Sroberto (void) close(fd); 378132451Sroberto pp->io.fd = -1; 379132451Sroberto free(pp->unitptr); 380132451Sroberto pp->unitptr = NULL; 381132451Sroberto return (0); 382132451Sroberto } 383132451Sroberto 384106424Sroberto NLOG(NLOG_CLOCKINFO) 385106424Sroberto msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit); 386132451Sroberto 387106424Sroberto return (1); 388106424Sroberto} 389106424Sroberto 390106424Srobertostatic void 391106424Srobertoneoclock4x_shutdown(int unit, 392106424Sroberto struct peer *peer) 393106424Sroberto{ 394106424Sroberto struct neoclock4x_unit *up; 395106424Sroberto struct refclockproc *pp; 396106424Sroberto int sl232; 397106424Sroberto 398132451Sroberto if(NULL != peer) 399132451Sroberto { 400132451Sroberto pp = peer->procptr; 401132451Sroberto if(pp != NULL) 402132451Sroberto { 403285612Sdelphij up = pp->unitptr; 404132451Sroberto if(up != NULL) 405132451Sroberto { 406132451Sroberto if(-1 != pp->io.fd) 407132451Sroberto { 408106424Sroberto#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) 409132451Sroberto /* turn on RTS, and DTR for power supply */ 410132451Sroberto /* NeoClock4x is powered from serial line */ 411132451Sroberto if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1) 412132451Sroberto { 413132451Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", 414132451Sroberto unit); 415132451Sroberto } 416106424Sroberto#ifdef TIOCM_RTS 417132451Sroberto /* turn on RTS, and DTR for power supply */ 418132451Sroberto sl232 &= ~(TIOCM_DTR | TIOCM_RTS); 419106424Sroberto#else 420132451Sroberto /* turn on RTS, and DTR for power supply */ 421132451Sroberto sl232 &= ~(CIOCM_DTR | CIOCM_RTS); 422106424Sroberto#endif 423132451Sroberto if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1) 424132451Sroberto { 425132451Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", 426132451Sroberto unit); 427132451Sroberto } 428132451Sroberto#endif 429132451Sroberto io_closeclock(&pp->io); 430132451Sroberto } 431132451Sroberto free(up); 432132451Sroberto pp->unitptr = NULL; 433132451Sroberto } 434132451Sroberto } 435106424Sroberto } 436132451Sroberto 437106424Sroberto msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit); 438106424Sroberto 439106424Sroberto NLOG(NLOG_CLOCKINFO) 440106424Sroberto msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit); 441106424Sroberto} 442106424Sroberto 443106424Srobertostatic void 444106424Srobertoneoclock4x_receive(struct recvbuf *rbufp) 445106424Sroberto{ 446106424Sroberto struct neoclock4x_unit *up; 447106424Sroberto struct refclockproc *pp; 448106424Sroberto struct peer *peer; 449106424Sroberto unsigned long calc_utc; 450106424Sroberto int day; 451106424Sroberto int month; /* ddd conversion */ 452106424Sroberto int c; 453132451Sroberto int dsec; 454106424Sroberto unsigned char calc_chksum; 455106424Sroberto int recv_chksum; 456132451Sroberto 457285612Sdelphij peer = rbufp->recv_peer; 458106424Sroberto pp = peer->procptr; 459285612Sdelphij up = pp->unitptr; 460132451Sroberto 461106424Sroberto /* wait till poll interval is reached */ 462106424Sroberto if(0 == up->recvnow) 463106424Sroberto return; 464132451Sroberto 465106424Sroberto /* reset poll interval flag */ 466106424Sroberto up->recvnow = 0; 467106424Sroberto 468106424Sroberto /* read last received timecode */ 469106424Sroberto pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); 470132451Sroberto pp->leap = LEAP_NOWARNING; 471132451Sroberto 472106424Sroberto if(NEOCLOCK4X_TIMECODELEN != pp->lencode) 473106424Sroberto { 474106424Sroberto NLOG(NLOG_CLOCKEVENT) 475106424Sroberto msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s", 476106424Sroberto up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode); 477106424Sroberto refclock_report(peer, CEVNT_BADREPLY); 478106424Sroberto return; 479106424Sroberto } 480106424Sroberto 481106424Sroberto neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2); 482106424Sroberto 483106424Sroberto /* calculate checksum */ 484106424Sroberto calc_chksum = 0; 485106424Sroberto for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++) 486106424Sroberto { 487106424Sroberto calc_chksum += pp->a_lastcode[c]; 488106424Sroberto } 489106424Sroberto if(recv_chksum != calc_chksum) 490106424Sroberto { 491106424Sroberto NLOG(NLOG_CLOCKEVENT) 492106424Sroberto msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s", 493106424Sroberto up->unit, pp->a_lastcode); 494106424Sroberto refclock_report(peer, CEVNT_BADREPLY); 495106424Sroberto return; 496106424Sroberto } 497106424Sroberto 498106424Sroberto /* Allow synchronization even is quartz clock is 499106424Sroberto * never initialized. 500106424Sroberto * WARNING: This is dangerous! 501132451Sroberto */ 502106424Sroberto up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS]; 503106424Sroberto if(0==(pp->sloppyclockflag & CLK_FLAG2)) 504106424Sroberto { 505106424Sroberto if('I' != up->quarzstatus) 506106424Sroberto { 507106424Sroberto NLOG(NLOG_CLOCKEVENT) 508106424Sroberto msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s", 509106424Sroberto up->unit, pp->a_lastcode); 510106424Sroberto pp->leap = LEAP_NOTINSYNC; 511106424Sroberto refclock_report(peer, CEVNT_BADDATE); 512106424Sroberto return; 513106424Sroberto } 514106424Sroberto } 515106424Sroberto if('I' != up->quarzstatus) 516106424Sroberto { 517106424Sroberto NLOG(NLOG_CLOCKEVENT) 518106424Sroberto msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s", 519106424Sroberto up->unit, pp->a_lastcode); 520106424Sroberto } 521132451Sroberto 522106424Sroberto /* 523132451Sroberto * If NeoClock4X is not synchronized to a radio clock 524106424Sroberto * check if we're allowed to synchronize with the quartz 525106424Sroberto * clock. 526106424Sroberto */ 527106424Sroberto up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE]; 528106424Sroberto if(0==(pp->sloppyclockflag & CLK_FLAG2)) 529106424Sroberto { 530106424Sroberto if('A' != up->timesource) 531106424Sroberto { 532106424Sroberto /* not allowed to sync with quartz clock */ 533106424Sroberto if(0==(pp->sloppyclockflag & CLK_FLAG1)) 534106424Sroberto { 535106424Sroberto refclock_report(peer, CEVNT_BADTIME); 536106424Sroberto pp->leap = LEAP_NOTINSYNC; 537106424Sroberto return; 538106424Sroberto } 539106424Sroberto } 540106424Sroberto } 541106424Sroberto 542106424Sroberto /* this should only used when first install is done */ 543106424Sroberto if(pp->sloppyclockflag & CLK_FLAG4) 544106424Sroberto { 545106424Sroberto msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s", 546106424Sroberto up->unit, pp->a_lastcode); 547106424Sroberto } 548106424Sroberto 549106424Sroberto /* 123456789012345678901234567890123456789012345 */ 550106424Sroberto /* S/N123456DCF1004021010001202ASX1213CR\r\n */ 551106424Sroberto 552106424Sroberto neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2); 553106424Sroberto neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2); 554106424Sroberto neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2); 555106424Sroberto neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2); 556106424Sroberto neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2); 557106424Sroberto neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2); 558132451Sroberto neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2); 559182007Sroberto#if defined(NTP_PRE_420) 560182007Sroberto pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */ 561182007Sroberto#else 562182007Sroberto pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */ 563182007Sroberto#endif 564132451Sroberto 565106424Sroberto memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3); 566106424Sroberto up->radiosignal[3] = 0; 567106424Sroberto memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6); 568106424Sroberto up->serial[6] = 0; 569106424Sroberto up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS]; 570106424Sroberto neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2); 571106424Sroberto neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2); 572106424Sroberto 573106424Sroberto /* 574106424Sroberto Validate received values at least enough to prevent internal 575106424Sroberto array-bounds problems, etc. 576106424Sroberto */ 577106424Sroberto if((pp->hour < 0) || (pp->hour > 23) || 578106424Sroberto (pp->minute < 0) || (pp->minute > 59) || 579106424Sroberto (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || 580106424Sroberto (day < 1) || (day > 31) || 581106424Sroberto (month < 1) || (month > 12) || 582106424Sroberto (pp->year < 0) || (pp->year > 99)) { 583106424Sroberto /* Data out of range. */ 584106424Sroberto NLOG(NLOG_CLOCKEVENT) 585106424Sroberto msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s", 586106424Sroberto up->unit, pp->a_lastcode); 587106424Sroberto refclock_report(peer, CEVNT_BADDATE); 588106424Sroberto return; 589106424Sroberto } 590132451Sroberto 591132451Sroberto /* Year-2000 check not needed anymore. Same problem 592132451Sroberto * will arise at 2099 but what should we do...? 593132451Sroberto * 594132451Sroberto * wrap 2-digit date into 4-digit 595132451Sroberto * 596132451Sroberto * if(pp->year < YEAR_PIVOT) 597132451Sroberto * { 598132451Sroberto * pp->year += 100; 599132451Sroberto * } 600132451Sroberto */ 601132451Sroberto pp->year += 2000; 602132451Sroberto 603132451Sroberto /* adjust NeoClock4X local time to UTC */ 604106424Sroberto calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second); 605106424Sroberto calc_utc -= 3600; 606132451Sroberto /* adjust NeoClock4X daylight saving time if needed */ 607106424Sroberto if('S' == up->dststatus) 608106424Sroberto calc_utc -= 3600; 609106424Sroberto neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second); 610106424Sroberto 611106424Sroberto /* 612106424Sroberto some preparations 613106424Sroberto */ 614132451Sroberto pp->day = ymd2yd(pp->year, month, day); 615106424Sroberto pp->leap = 0; 616106424Sroberto 617106424Sroberto if(pp->sloppyclockflag & CLK_FLAG4) 618106424Sroberto { 619132451Sroberto msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld", 620106424Sroberto up->unit, 621106424Sroberto pp->year, month, day, 622182007Sroberto pp->hour, pp->minute, pp->second, 623182007Sroberto#if defined(NTP_PRE_420) 624182007Sroberto pp->msec 625182007Sroberto#else 626182007Sroberto pp->nsec/NSEC_TO_MILLI 627182007Sroberto#endif 628182007Sroberto ); 629106424Sroberto } 630106424Sroberto 631106424Sroberto up->utc_year = pp->year; 632106424Sroberto up->utc_month = month; 633106424Sroberto up->utc_day = day; 634106424Sroberto up->utc_hour = pp->hour; 635106424Sroberto up->utc_minute = pp->minute; 636106424Sroberto up->utc_second = pp->second; 637182007Sroberto#if defined(NTP_PRE_420) 638182007Sroberto up->utc_msec = pp->msec; 639182007Sroberto#else 640182007Sroberto up->utc_msec = pp->nsec/NSEC_TO_MILLI; 641182007Sroberto#endif 642132451Sroberto 643106424Sroberto if(!refclock_process(pp)) 644106424Sroberto { 645106424Sroberto NLOG(NLOG_CLOCKEVENT) 646106424Sroberto msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit); 647106424Sroberto refclock_report(peer, CEVNT_FAULT); 648106424Sroberto return; 649106424Sroberto } 650106424Sroberto refclock_receive(peer); 651132451Sroberto 652132451Sroberto /* report good status */ 653132451Sroberto refclock_report(peer, CEVNT_NOMINAL); 654132451Sroberto 655106424Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 656106424Sroberto} 657106424Sroberto 658106424Srobertostatic void 659106424Srobertoneoclock4x_poll(int unit, 660106424Sroberto struct peer *peer) 661106424Sroberto{ 662106424Sroberto struct neoclock4x_unit *up; 663106424Sroberto struct refclockproc *pp; 664106424Sroberto 665106424Sroberto pp = peer->procptr; 666285612Sdelphij up = pp->unitptr; 667106424Sroberto 668106424Sroberto pp->polls++; 669106424Sroberto up->recvnow = 1; 670106424Sroberto} 671106424Sroberto 672106424Srobertostatic void 673106424Srobertoneoclock4x_control(int unit, 674285612Sdelphij const struct refclockstat *in, 675106424Sroberto struct refclockstat *out, 676106424Sroberto struct peer *peer) 677106424Sroberto{ 678106424Sroberto struct neoclock4x_unit *up; 679106424Sroberto struct refclockproc *pp; 680132451Sroberto 681106424Sroberto if(NULL == peer) 682106424Sroberto { 683106424Sroberto msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 684106424Sroberto return; 685106424Sroberto } 686106424Sroberto 687106424Sroberto pp = peer->procptr; 688106424Sroberto if(NULL == pp) 689106424Sroberto { 690106424Sroberto msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 691106424Sroberto return; 692106424Sroberto } 693106424Sroberto 694285612Sdelphij up = pp->unitptr; 695106424Sroberto if(NULL == up) 696106424Sroberto { 697106424Sroberto msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); 698106424Sroberto return; 699106424Sroberto } 700106424Sroberto 701106424Sroberto if(NULL != in) 702106424Sroberto { 703106424Sroberto /* check to see if a user supplied time offset is given */ 704106424Sroberto if(in->haveflags & CLK_HAVETIME1) 705106424Sroberto { 706106424Sroberto pp->fudgetime1 = in->fudgetime1; 707106424Sroberto NLOG(NLOG_CLOCKINFO) 708106424Sroberto msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.", 709106424Sroberto unit, pp->fudgetime1); 710106424Sroberto } 711132451Sroberto 712106424Sroberto /* notify */ 713106424Sroberto if(pp->sloppyclockflag & CLK_FLAG1) 714106424Sroberto { 715106424Sroberto NLOG(NLOG_CLOCKINFO) 716106424Sroberto msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit); 717106424Sroberto } 718106424Sroberto else 719106424Sroberto { 720106424Sroberto NLOG(NLOG_CLOCKINFO) 721106424Sroberto msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit); 722106424Sroberto } 723106424Sroberto } 724106424Sroberto 725106424Sroberto if(NULL != out) 726106424Sroberto { 727106424Sroberto char *tt; 728106424Sroberto char tmpbuf[80]; 729132451Sroberto 730106424Sroberto out->kv_list = (struct ctl_var *)0; 731106424Sroberto out->type = REFCLK_NEOCLOCK4X; 732132451Sroberto 733132451Sroberto snprintf(tmpbuf, sizeof(tmpbuf)-1, 734132451Sroberto "%04d-%02d-%02d %02d:%02d:%02d.%03d", 735132451Sroberto up->utc_year, up->utc_month, up->utc_day, 736132451Sroberto up->utc_hour, up->utc_minute, up->utc_second, 737132451Sroberto up->utc_msec); 738132451Sroberto tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF); 739132451Sroberto snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf); 740132451Sroberto 741132451Sroberto tt = add_var(&out->kv_list, 40, RO|DEF); 742132451Sroberto snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal); 743132451Sroberto tt = add_var(&out->kv_list, 40, RO|DEF); 744132451Sroberto snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1); 745132451Sroberto tt = add_var(&out->kv_list, 40, RO|DEF); 746132451Sroberto snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2); 747132451Sroberto tt = add_var(&out->kv_list, 40, RO|DEF); 748106424Sroberto if('A' == up->timesource) 749132451Sroberto snprintf(tt, 39, "timesource=\"radio\""); 750106424Sroberto else if('C' == up->timesource) 751132451Sroberto snprintf(tt, 39, "timesource=\"quartz\""); 752106424Sroberto else 753132451Sroberto snprintf(tt, 39, "timesource=\"unknown\""); 754132451Sroberto tt = add_var(&out->kv_list, 40, RO|DEF); 755106424Sroberto if('I' == up->quarzstatus) 756132451Sroberto snprintf(tt, 39, "quartzstatus=\"synchronized\""); 757106424Sroberto else if('X' == up->quarzstatus) 758132451Sroberto snprintf(tt, 39, "quartzstatus=\"not synchronized\""); 759106424Sroberto else 760132451Sroberto snprintf(tt, 39, "quartzstatus=\"unknown\""); 761132451Sroberto tt = add_var(&out->kv_list, 40, RO|DEF); 762106424Sroberto if('S' == up->dststatus) 763132451Sroberto snprintf(tt, 39, "dststatus=\"summer\""); 764106424Sroberto else if('W' == up->dststatus) 765132451Sroberto snprintf(tt, 39, "dststatus=\"winter\""); 766106424Sroberto else 767132451Sroberto snprintf(tt, 39, "dststatus=\"unknown\""); 768132451Sroberto tt = add_var(&out->kv_list, 80, RO|DEF); 769132451Sroberto snprintf(tt, 79, "firmware=\"%s\"", up->firmware); 770132451Sroberto tt = add_var(&out->kv_list, 40, RO|DEF); 771132451Sroberto snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag); 772132451Sroberto tt = add_var(&out->kv_list, 80, RO|DEF); 773132451Sroberto snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION); 774132451Sroberto tt = add_var(&out->kv_list, 80, RO|DEF); 775132451Sroberto snprintf(tt, 79, "serialnumber=\"%s\"", up->serial); 776106424Sroberto } 777106424Sroberto} 778106424Sroberto 779132451Srobertostatic int 780132451Srobertoneol_hexatoi_len(const char str[], 781132451Sroberto int *result, 782132451Sroberto int maxlen) 783106424Sroberto{ 784106424Sroberto int hexdigit; 785106424Sroberto int i; 786106424Sroberto int n = 0; 787132451Sroberto 788285612Sdelphij for(i=0; isxdigit((unsigned char)str[i]) && i < maxlen; i++) 789106424Sroberto { 790285612Sdelphij hexdigit = isdigit((unsigned char)str[i]) ? toupper((unsigned char)str[i]) - '0' : toupper((unsigned char)str[i]) - 'A' + 10; 791106424Sroberto n = 16 * n + hexdigit; 792106424Sroberto } 793106424Sroberto *result = n; 794106424Sroberto return (n); 795106424Sroberto} 796106424Sroberto 797132451Srobertostatic int 798132451Srobertoneol_atoi_len(const char str[], 799106424Sroberto int *result, 800106424Sroberto int maxlen) 801106424Sroberto{ 802106424Sroberto int digit; 803106424Sroberto int i; 804106424Sroberto int n = 0; 805132451Sroberto 806285612Sdelphij for(i=0; isdigit((unsigned char)str[i]) && i < maxlen; i++) 807106424Sroberto { 808106424Sroberto digit = str[i] - '0'; 809106424Sroberto n = 10 * n + digit; 810106424Sroberto } 811106424Sroberto *result = n; 812106424Sroberto return (n); 813106424Sroberto} 814106424Sroberto 815106424Sroberto/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. 816106424Sroberto * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 817106424Sroberto * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. 818106424Sroberto * 819106424Sroberto * [For the Julian calendar (which was used in Russia before 1917, 820106424Sroberto * Britain & colonies before 1752, anywhere else before 1582, 821106424Sroberto * and is still in use by some communities) leave out the 822106424Sroberto * -year/100+year/400 terms, and add 10.] 823106424Sroberto * 824106424Sroberto * This algorithm was first published by Gauss (I think). 825106424Sroberto * 826106424Sroberto * WARNING: this function will overflow on 2106-02-07 06:28:16 on 827106424Sroberto * machines were long is 32-bit! (However, as time_t is signed, we 828106424Sroberto * will already get problems at other places on 2038-01-19 03:14:08) 829106424Sroberto */ 830132451Srobertostatic unsigned long 831132451Srobertoneol_mktime(int year, 832132451Sroberto int mon, 833132451Sroberto int day, 834132451Sroberto int hour, 835132451Sroberto int min, 836132451Sroberto int sec) 837106424Sroberto{ 838106424Sroberto if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */ 839106424Sroberto mon += 12; /* Puts Feb last since it has leap day */ 840106424Sroberto year -= 1; 841106424Sroberto } 842106424Sroberto return ((( 843106424Sroberto (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + 844106424Sroberto year*365 - 719499 845106424Sroberto )*24 + hour /* now have hours */ 846106424Sroberto )*60 + min /* now have minutes */ 847106424Sroberto )*60 + sec; /* finally seconds */ 848106424Sroberto} 849106424Sroberto 850132451Srobertostatic void 851132451Srobertoneol_localtime(unsigned long utc, 852132451Sroberto int* year, 853132451Sroberto int* month, 854132451Sroberto int* day, 855132451Sroberto int* hour, 856132451Sroberto int* min, 857132451Sroberto int* sec) 858106424Sroberto{ 859132451Sroberto *sec = utc % 60; 860132451Sroberto utc /= 60; 861132451Sroberto *min = utc % 60; 862132451Sroberto utc /= 60; 863132451Sroberto *hour = utc % 24; 864132451Sroberto utc /= 24; 865132451Sroberto 866106424Sroberto /* JDN Date 1/1/1970 */ 867132451Sroberto neol_jdn_to_ymd(utc + 2440588L, year, month, day); 868106424Sroberto} 869106424Sroberto 870132451Srobertostatic void 871132451Srobertoneol_jdn_to_ymd(unsigned long jdn, 872132451Sroberto int *yy, 873132451Sroberto int *mm, 874132451Sroberto int *dd) 875106424Sroberto{ 876106424Sroberto unsigned long x, z, m, d, y; 877106424Sroberto unsigned long daysPer400Years = 146097UL; 878106424Sroberto unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL; 879132451Sroberto 880106424Sroberto x = jdn + 68569UL; 881106424Sroberto z = 4UL * x / daysPer400Years; 882106424Sroberto x = x - (daysPer400Years * z + 3UL) / 4UL; 883106424Sroberto y = 4000UL * (x + 1) / fudgedDaysPer4000Years; 884106424Sroberto x = x - 1461UL * y / 4UL + 31UL; 885106424Sroberto m = 80UL * x / 2447UL; 886106424Sroberto d = x - 2447UL * m / 80UL; 887106424Sroberto x = m / 11UL; 888106424Sroberto m = m + 2UL - 12UL * x; 889106424Sroberto y = 100UL * (z - 49UL) + y + x; 890132451Sroberto 891106424Sroberto *yy = (int)y; 892106424Sroberto *mm = (int)m; 893106424Sroberto *dd = (int)d; 894106424Sroberto} 895106424Sroberto 896132451Sroberto#if !defined(NEOCLOCK4X_FIRMWARE) 897106424Srobertostatic int 898106424Srobertoneol_query_firmware(int fd, 899106424Sroberto int unit, 900106424Sroberto char *firmware, 901285612Sdelphij size_t maxlen) 902106424Sroberto{ 903132451Sroberto char tmpbuf[256]; 904285612Sdelphij size_t len; 905106424Sroberto int lastsearch; 906106424Sroberto unsigned char c; 907106424Sroberto int last_c_was_crlf; 908106424Sroberto int last_crlf_conv_len; 909106424Sroberto int init; 910132451Sroberto int read_errors; 911106424Sroberto int flag = 0; 912132451Sroberto int chars_read; 913106424Sroberto 914106424Sroberto /* wait a little bit */ 915132451Sroberto sleep(1); 916106424Sroberto if(-1 != write(fd, "V", 1)) 917106424Sroberto { 918106424Sroberto /* wait a little bit */ 919132451Sroberto sleep(1); 920106424Sroberto memset(tmpbuf, 0x00, sizeof(tmpbuf)); 921132451Sroberto 922106424Sroberto len = 0; 923106424Sroberto lastsearch = 0; 924106424Sroberto last_c_was_crlf = 0; 925106424Sroberto last_crlf_conv_len = 0; 926106424Sroberto init = 1; 927132451Sroberto read_errors = 0; 928132451Sroberto chars_read = 0; 929106424Sroberto for(;;) 930106424Sroberto { 931132451Sroberto if(read_errors > 5) 932106424Sroberto { 933106424Sroberto msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit); 934285612Sdelphij strlcpy(tmpbuf, "unknown due to timeout", sizeof(tmpbuf)); 935106424Sroberto break; 936106424Sroberto } 937132451Sroberto if(chars_read > 500) 938132451Sroberto { 939132451Sroberto msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit); 940285612Sdelphij strlcpy(tmpbuf, "unknown due to garbage input", sizeof(tmpbuf)); 941132451Sroberto break; 942132451Sroberto } 943106424Sroberto if(-1 == read(fd, &c, 1)) 944106424Sroberto { 945132451Sroberto if(EAGAIN != errno) 946132451Sroberto { 947285612Sdelphij msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %m", unit); 948132451Sroberto read_errors++; 949132451Sroberto } 950132451Sroberto else 951132451Sroberto { 952132451Sroberto sleep(1); 953132451Sroberto } 954106424Sroberto continue; 955106424Sroberto } 956132451Sroberto else 957132451Sroberto { 958132451Sroberto chars_read++; 959132451Sroberto } 960132451Sroberto 961106424Sroberto if(init) 962106424Sroberto { 963106424Sroberto if(0xA9 != c) /* wait for (c) char in input stream */ 964106424Sroberto continue; 965132451Sroberto 966285612Sdelphij strlcpy(tmpbuf, "(c)", sizeof(tmpbuf)); 967106424Sroberto len = 3; 968106424Sroberto init = 0; 969106424Sroberto continue; 970106424Sroberto } 971132451Sroberto 972132451Sroberto#if 0 973132451Sroberto msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c); 974132451Sroberto#endif 975132451Sroberto 976106424Sroberto if(0x0A == c || 0x0D == c) 977106424Sroberto { 978106424Sroberto if(last_c_was_crlf) 979106424Sroberto { 980106424Sroberto char *ptr; 981106424Sroberto ptr = strstr(&tmpbuf[lastsearch], "S/N"); 982106424Sroberto if(NULL != ptr) 983106424Sroberto { 984106424Sroberto tmpbuf[last_crlf_conv_len] = 0; 985106424Sroberto flag = 1; 986106424Sroberto break; 987106424Sroberto } 988106424Sroberto /* convert \n to / */ 989106424Sroberto last_crlf_conv_len = len; 990106424Sroberto tmpbuf[len++] = ' '; 991106424Sroberto tmpbuf[len++] = '/'; 992106424Sroberto tmpbuf[len++] = ' '; 993106424Sroberto lastsearch = len; 994106424Sroberto } 995106424Sroberto last_c_was_crlf = 1; 996106424Sroberto } 997106424Sroberto else 998106424Sroberto { 999106424Sroberto last_c_was_crlf = 0; 1000106424Sroberto if(0x00 != c) 1001132451Sroberto tmpbuf[len++] = (char) c; 1002106424Sroberto } 1003106424Sroberto tmpbuf[len] = '\0'; 1004285612Sdelphij if (len > sizeof(tmpbuf)-5) 1005106424Sroberto break; 1006106424Sroberto } 1007106424Sroberto } 1008106424Sroberto else 1009106424Sroberto { 1010106424Sroberto msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit); 1011285612Sdelphij strlcpy(tmpbuf, "unknown error", sizeof(tmpbuf)); 1012106424Sroberto } 1013285612Sdelphij if (strlcpy(firmware, tmpbuf, maxlen) >= maxlen) 1014285612Sdelphij strlcpy(firmware, "buffer too small", maxlen); 1015106424Sroberto 1016106424Sroberto if(flag) 1017106424Sroberto { 1018106424Sroberto NLOG(NLOG_CLOCKINFO) 1019106424Sroberto msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware); 1020285612Sdelphij 1021285612Sdelphij if(strstr(firmware, "/R2")) 1022285612Sdelphij { 1023285612Sdelphij msyslog(LOG_INFO, "NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit); 1024285612Sdelphij } 1025285612Sdelphij 1026106424Sroberto } 1027106424Sroberto 1028106424Sroberto return (flag); 1029106424Sroberto} 1030132451Sroberto 1031132451Srobertostatic int 1032132451Srobertoneol_check_firmware(int unit, 1033132451Sroberto const char *firmware, 1034132451Sroberto char *firmwaretag) 1035132451Sroberto{ 1036132451Sroberto char *ptr; 1037132451Sroberto 1038132451Sroberto *firmwaretag = '?'; 1039132451Sroberto ptr = strstr(firmware, "NDF:"); 1040132451Sroberto if(NULL != ptr) 1041132451Sroberto { 1042132451Sroberto if((strlen(firmware) - strlen(ptr)) >= 7) 1043132451Sroberto { 1044132451Sroberto if(':' == *(ptr+5) && '*' == *(ptr+6)) 1045132451Sroberto *firmwaretag = *(ptr+4); 1046132451Sroberto } 1047132451Sroberto } 1048132451Sroberto 1049132451Sroberto if('A' != *firmwaretag) 1050132451Sroberto { 1051132451Sroberto msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag); 1052132451Sroberto return (0); 1053132451Sroberto } 1054132451Sroberto 1055132451Sroberto return (1); 1056132451Sroberto} 1057132451Sroberto#endif 1058132451Sroberto 1059106424Sroberto#else 1060106424Srobertoint refclock_neoclock4x_bs; 1061106424Sroberto#endif /* REFCLOCK */ 1062106424Sroberto 1063106424Sroberto/* 1064106424Sroberto * History: 1065106424Sroberto * refclock_neoclock4x.c 1066106424Sroberto * 1067106424Sroberto * 2002/04/27 cjh 1068106424Sroberto * Revision 1.0 first release 1069106424Sroberto * 1070132451Sroberto * 2002/07/15 cjh 1071106424Sroberto * preparing for bitkeeper reposity 1072106424Sroberto * 1073132451Sroberto * 2002/09/09 cjh 1074132451Sroberto * Revision 1.1 1075132451Sroberto * - don't assume sprintf returns an int anymore 1076132451Sroberto * - change the way the firmware version is read 1077132451Sroberto * - some customers would like to put a device called 1078132451Sroberto * data diode to the NeoClock4X device to disable 1079132451Sroberto * the write line. We need to now the firmware 1080132451Sroberto * version even in this case. We made a compile time 1081132451Sroberto * definition in this case. The code was previously 1082132451Sroberto * only available on request. 1083132451Sroberto * 1084132451Sroberto * 2003/01/08 cjh 1085132451Sroberto * Revision 1.11 1086132451Sroberto * - changing xprinf to xnprinf to avoid buffer overflows 1087132451Sroberto * - change some logic 1088132451Sroberto * - fixed memory leaks if drivers can't initialize 1089132451Sroberto * 1090132451Sroberto * 2003/01/10 cjh 1091132451Sroberto * Revision 1.12 1092132451Sroberto * - replaced ldiv 1093132451Sroberto * - add code to support FreeBSD 1094132451Sroberto * 1095132451Sroberto * 2003/07/07 cjh 1096132451Sroberto * Revision 1.13 1097132451Sroberto * - fix reporting of clock status 1098132451Sroberto * changes. previously a bad clock 1099132451Sroberto * status was never reset. 1100182007Sroberto * 1101182007Sroberto * 2004/04/07 cjh 1102182007Sroberto * Revision 1.14 1103182007Sroberto * - open serial port in a way 1104182007Sroberto * AIX and some other OS can 1105182007Sroberto * handle much better 1106182007Sroberto * 1107182007Sroberto * 2006/01/11 cjh 1108182007Sroberto * Revision 1.15 1109182007Sroberto * - remove some unsued #ifdefs 1110182007Sroberto * - fix nsec calculation, closes #499 1111182007Sroberto * 1112285612Sdelphij * 2009/12/04 cjh 1113285612Sdelphij * Revision 1.16 1114285612Sdelphij * - change license to ntp COPYRIGHT notice. This should allow Debian 1115285612Sdelphij * to add this refclock driver in further releases. 1116285612Sdelphij * - detect R2 hardware 1117285612Sdelphij * 1118106424Sroberto */ 1119285612Sdelphij 1120285612Sdelphij 1121285612Sdelphij 1122285612Sdelphij 1123285612Sdelphij 1124285612Sdelphij 1125