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